【问题标题】:popup field in custom editor resets on play自定义编辑器中的弹出字段在播放时重置
【发布时间】:2018-12-06 13:56:38
【问题描述】:

尝试为自定义编辑器编写代码,但当我按下播放按钮时,下拉或弹出字段值总是会重置。

我浏览了一些类似的问题,并在添加应用修改和设置脏时找到了解决方案,但都没有解决问题。

知道会发生什么吗?

下面是代码:

[CustomEditor(typeof(EnemyAI))]
public class Level_SelectionEditor : Editor
{
    string[] _choices = new[] { "snailer", "sheller" };
    int _choiceIndex = 0;

    override public void OnInspectorGUI()
    {



        // Draw the default inspector
        var mc = target as EnemyAI;



        EditorGUILayout.PropertyField(serializedObject.FindProperty("damage"), true);
       EditorGUILayout.PropertyField(serializedObject.FindProperty("attackCounter"), true);

        EditorGUILayout.PrefixLabel("Type");
        EditorGUI.indentLevel++;
        _choiceIndex = EditorGUILayout.Popup(_choiceIndex, _choices);



        //updated in code
        if (_choices[_choiceIndex] == "snailer")
        {
            mc.type = EnemyAI.Type.snailer;

            EditorGUILayout.PropertyField(serializedObject.FindProperty("snailerEffect"), true);
            EditorGUILayout.PropertyField(serializedObject.FindProperty("snailerShootPoint"), true);
        }
        else
        {
            mc.type = EnemyAI.Type.sheller;
            EditorGUILayout.PropertyField(serializedObject.FindProperty("rotationsPerMinute"), true);
            EditorGUILayout.PropertyField(serializedObject.FindProperty("rotationTime"), true);
            EditorGUILayout.PropertyField(serializedObject.FindProperty("pauseTime"), true);
        }



        // Save the changes back to the object
        EditorUtility.SetDirty(target);

        serializedObject.ApplyModifiedProperties();




    }
}

【问题讨论】:

    标签: unity3d


    【解决方案1】:

    你应该在 EnemyAI 类中定义 choiceIndex 属性和基值,然后在 EditorScript 中像这样设置它:

    mc.choiceIndex = EditorGUILayout.Popup(mc.choiceIndex, _choices);
    

    同样在if语句中:

     if (_choices[mc.choiceIndex] == "snailer"){
         ...
     }
    

    希望对你有帮助!

    【讨论】:

      【解决方案2】:

      首先:不要合并

      EditorUtility.SetDirty(target); 
      serializedObject.ApplyModifiedProperties();
      

      尽量不要混合SerialzedProperties,我称之为“直接”属性(使用target)!这会带来很多麻烦。

      你也在打电话

      serializedObject.ApplyModifiedProperties();
      

      但我没看到你在哪里打电话

      serializedObject.Update();
      

      首先实际获取当前值。

      我猜那条评论不属于那里

      // Draw the default inspector
      var mc = target as EnemyAI;
      

      至少那里没有发生什么。


      最后,你的值被重置的原因是你在编辑器脚本中引入了_choiceIndex,所以serialzedObject.ApplyModifiedProperties()EditorUtility.SetDirty(target) 都没有 会对它产生任何影响,因为它不是目标类的 SerializedField。这样做不好,还有三个原因:

      1. 编辑器脚本不会被编译到构建中 -> 该值将不可用
      2. 您无法访问该值,也不是来自您的实际班级 -> 它有什么好处?
      3. 即使它只是编辑器中内容的控制值:每次初始化编辑器脚本时都会重置该值。发生这种情况

        • 每次重新编译后
        • 进入或退出播放模式后
        • 每次相应对象获得焦点时

      为了保存该值,您应该将 _choiceIndex 放在 EnemyAI 类中,但为什么不简单地使用您的枚举 EnemyAI.Type 或者更好地表示已经存在的字段 type 呢?假设

      [SerialzeField] private EnemyAI.Type type;
      

      在你的编辑器脚本中你可以简单地做

      // This automatically also uses the label "Type"
      EditorGuyLayout.PropertyField(serializedObject.FindProperty("type"));
      

      还有

      if ((EnemyAI.Type)_choiceIndex.intValue == EnemyAI.Type.snailer)
      

      或者,如果您想坚持使用字符串列表,例如要通过编辑器脚本填充更多选择选项,您可以使用 int 做同样的事情,但也可以使用 SerializedProperty

      在敌人AI中

      [SerializeField] private int _choiceIndex;
      

      在编辑器中

      SerializedProperty _choiceIndex = serialzedObject.FindProperty("_choiceIndex");
      _choiceIndex.intValue = EditorGuyLayout.Popup("Type", _choiceIndex.intValue, choices);
      

      所以我的一些外卖规则就像

      1. 如果您想更改/保存值,请始终使用SerlialzedProperty。有时让它们运行有点棘手(尤其是例如使用列表或嵌套类),并且您很容易犯错误,因为您通过变量名(字符串)获取它们。但它们带来了很多好处,比如自动标记脏东西、撤消/重做等。

      2. 使用target as <some type> 直接访问内容的唯一原因应该是从编辑器中调用类的方法(首选不更改任何序列化值)。但也在这里:仅在真正需要时才使用它,因为如前所述,混合 SerializedProperty 和直接更改通常会搞砸事情。

      3. 变量应该只在检查器中引入,如果

        • 你没有兴趣保存它们
        • 它们在 Inspector 每次失去焦点时重置
        • 它们是永远不会更改的常量值(例如,字段标签或 choices 数组)

      最后一个一般提示:

      您不应该在每个绘图调用中都使用serializedObject.FindProperty。而是在OnEnable() 中收集您所有的财产一次

      SerializedProperty _type;
      // ...
      
      private void OnEnable()
      {
          _type = serializedObject.FindProperty("type");
          // ...
      }
      
      public override void OnInpectorGUI()
      {
          // ...
          EditorGUILayout.PropertyField(_type);
          // ...
      }
      

      【讨论】:

        猜你喜欢
        • 2017-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多