Unity构建错误:找不到类型或命名空间名称“Editor”

Ral*_*yph 7 c# unity-game-engine

我有一个脚本Item.cs,负责设置玩家库存中项目的参数。在其中,我连接 Unity Editor 库并创建一个继承自 Editor 类的类。直到最近,我还在使用 Unity 版本2020.3.0f1,一切都构建得很好,但现在我安装了2021.3.9f1,但开始遇到问题。

当我构建项目时,我收到以下错误:

Assets\Inventory\Items.cs(31,31):错误 CS0246:找不到类型或命名空间名称“Editor”(您是否缺少 using 指令或程序集引用?)

Assets\Inventory\Items.cs(30,5):错误 CS0246:找不到类型或命名空间名称“CustomEditorAttribute”(您是否缺少 using 指令或程序集引用?)

Assets\Inventory\Items.cs(30,5):错误 CS0246:找不到类型或命名空间名称“CustomEditor”(您是否缺少 using 指令或程序集引用?)

Assets\Inventory\Items.cs(32,28):错误CS0115:'Items.ItemsEditor.OnInspectorGUI()':找不到合适的方法来覆盖

代码:

using UnityEngine;
using UnityEditor;

public class Items : MonoBehaviour {
   public enum itemTypes { Weapon, Potion, Amulet }
   public enum potionType { Health, Poison, Strong, Beer }
   public enum amuletType { Health, Defense, Speed }

   [Header("Main Settings")]
   public itemTypes ItemTypes;
   [HideInInspector] public GameObject Model;
   [HideInInspector] public Texture2D Icon;
   [HideInInspector] public string itemName;
   [HideInInspector] public int itemNum;

   // Weapon
   [HideInInspector] public float damage;
   [HideInInspector] public float atackSpeed;
   [HideInInspector] public int broken;

   // Potion
   [HideInInspector] public potionType Potion = potionType.Health;
   [HideInInspector] public float healthOfset;
   [HideInInspector] public bool processingEffect;

   // Amulet
   [HideInInspector] public amuletType Amulet = amuletType.Health; 
   [HideInInspector] public float amuletValue;
    
   [CustomEditor(typeof(Items))]
   public class ItemsEditor : Editor {
      public override void OnInspectorGUI() {
         base.OnInspectorGUI();
         Items items = (Items) target;
         DrawMain(items);
         EditorGUILayout.Space();
      
         switch (items.ItemTypes) {
            case itemTypes.Weapon:
               WeaponSettings();
               break;
            case itemTypes.Potion:
               PotionSettings();
               break;
            case itemTypes.Amulet:
               AmuletSettings();
               break;
         }
         serializedObject.ApplyModifiedProperties();
      }

      void DrawMain(Items items) {
         EditorGUILayout.PropertyField(serializedObject.FindProperty("Icon"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("Model"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("itemNum"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("itemName"));
         EditorGUILayout.Space();
      }

      void WeaponSettings() {
         EditorGUILayout.PropertyField(serializedObject.FindProperty("damage"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("atackSpeed"));
      }

      void PotionSettings() {
         EditorGUILayout.PropertyField(serializedObject.FindProperty("Potion"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("healthOfset"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("processingEffect"));
      }

      void AmuletSettings() {
         EditorGUILayout.PropertyField(serializedObject.FindProperty("Amulet"));
         EditorGUILayout.PropertyField(serializedObject.FindProperty("amuletValue"));
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

der*_*ugo 10

顾名思义,UnityEditor命名空间仅在 Unity 编辑器本身内可用,在编译构建时完全被剥离/不包含在内。

通常有三种方法可以防止编辑器代码在构建期间被编译

  • 将您的文件放在名为Editor- 构建期间忽略整个内容的文件夹中(另请参阅特殊文件夹名称
  • 将代码放入具有程序集定义的文件夹中,该文件夹仅针对编辑器而不是其他目标平台进行编译
  • 使用条件编译

在您的情况下,最简单的方法是将与UnityEditor命名空间相关的所有内容包装在相应的#if - #endif预处理器中,因为您将编辑器嵌入到类型本身中。

我通常更喜欢相同的顺便说一句,因为嵌入编辑器会产生一些优势:恕我直言:

  • 您可以直接使用nameof而不是硬编码字符串 -> 容易出错且维护工作量大
  • 您甚至可以访问私有序列化字段 =>nameof甚至可以再次使用这些字段
  • 您立即知道对于这种类型存在自定义编辑器以及在哪里可以找到它,这在严格分离它们时并不是那么简单

顺便说一句,我不会target直接接触但完全经历serializedObject

using UnityEngine;
#if UNITY_EDITOR // => Ignore from here to next endif if not in editor
using UnityEditor;
#endif

public class Items : MonoBehaviour 
{
   public enum itemTypes { Weapon, Potion, Amulet }
   public enum potionType { Health, Poison, Strong, Beer }
   public enum amuletType { Health, Defense, Speed }

   [Header("Main Settings")]
   public itemTypes ItemTypes;
   public GameObject Model;
   public Texture2D Icon;
   public string itemName;
   public int itemNum;

   // Weapon
   public float damage;
   public float atackSpeed;
   public int broken;

   // Potion
   public potionType Potion = potionType.Health;
   public float healthOfset;
   public bool processingEffect;

   // Amulet
   public amuletType Amulet = amuletType.Health; 
   public float amuletValue;
    
#if UNITY_EDITOR // => Ignore from here to next endif if not in editor
   [CustomEditor(typeof(Items))]
   public class ItemsEditor : Editor 
   {
      private SeriaizdProperty itemTypes;
      private SeriaizdProperty icon;
      private SeriaizdProperty model;
      private SeriaizdProperty itemNum;
      private SeriaizdProperty itemName;
      private SeriaizdProperty damage;
      private SeriaizdProperty atackSpeed;
      private SeriaizdProperty potion;
      private SeriaizdProperty healthOfset;
      private SeriaizdProperty processingEffect;
      private SeriaizdProperty amulet;
      private SeriaizdProperty amuletValue;
      

      private void OnEnable()
      {
          // it is enough to do these ONCE when the inspector is loaded

          // note that one huge advantage of having the Editor embedded into your class is that you can 
          // - and imho SHOULD - directly use "nameof" instead of going by hardcoded strings (-> error prone and maintenance intense)
          itemTypes = serializedObject.FindProperty(nameof(ItemTypes));
          icon = serializedObject.FindProperty(nameof(Icon));
          model = serializedObject.FindProperty(nameof(model));
          itemNum = serializedObject.FindProperty(nameof(itemNum));
          itemName = serializedObject.FindProperty(nameof(itemName));
          damage = serializedObject.FindProperty(nameof(damage));
          atackSpeed = serializedObject.FindProperty(nameof(atackSpeed));
          potion = serializedObject.FindProperty(nameof(Potion));
          healthOfset = serializedObject.FindProperty(nameof(healthOfset));
          processingEffect = serializedObject.FindProperty(nameof(processingEffect));
          amulet = serializedObject.FindProperty(nameof(Amulet));
          amuletValue = serializedObject.FindProperty(nameof(amuletValue));
      }

      public override void OnInspectorGUI() 
      {
         //base.OnInspectorGUI();

         // This is also required so the serialized poperties get updated
         // with the current actual values
         serializedObject.Update();

         DrawMain(items);
         EditorGUILayout.Space();
      
         switch (items.ItemTypes) 
         {
            case itemTypes.Weapon:
               WeaponSettings();
               break;

            case itemTypes.Potion:
               PotionSettings();
               break;

            case itemTypes.Amulet:
               AmuletSettings();
               break;
         }

         serializedObject.ApplyModifiedProperties();
      }

      void DrawMain(Items items) 
      {
         EditorGUILayout.PropertyField(icon);
         EditorGUILayout.PropertyField(model);
         EditorGUILayout.PropertyField(itemNum);
         EditorGUILayout.PropertyField(itemName);
         EditorGUILayout.Space();
      }

      void WeaponSettings() 
      {
         EditorGUILayout.PropertyField(damage);
         EditorGUILayout.PropertyField(atackSpeed);
      }

      void PotionSettings() 
      {
         EditorGUILayout.PropertyField(potion);
         EditorGUILayout.PropertyField(healthOfse);
         EditorGUILayout.PropertyField(processingEffect);
      }

      void AmuletSettings() 
      {
         EditorGUILayout.PropertyField(amulet);
         EditorGUILayout.PropertyField(amuletValue);
      }
   }
#endif
}
Run Code Online (Sandbox Code Playgroud)

然而

现在请允许我问:为什么不使用正确的继承并在根本不需要任何自定义编辑器的情况下执行以下操作?

public abstract class Items : MonoBehaviour 
{
   [Header("Main Settings")]
   public GameObject Model;
   public Texture2D Icon;
   public string itemName;
   public int itemNum;
}
Run Code Online (Sandbox Code Playgroud)

然后(当然每个都在单独的脚本文件中)

public class Weapon : Items
{
   [Header("Weapon Settings")]
   public float damage;
   public float atackSpeed;
   public int broken;
}

public class Potion : Items
{
    public enum PotionType { Health, Poison, Strong, Beer }

    [Header("Potion Settings")]
    public PotionType potionType;
    public float healthOfset;
    public bool processingEffect;
}

public class Amulet : Items
{
    public enum AmuletType { Health, Defense, Speed }

    [Header("Amulet Settings")]
    public AmuletType amuletType; 
    public float amuletValue;
}
Run Code Online (Sandbox Code Playgroud)

现在,每当您需要区分不同项目时,您只需这样做

Items item = <Get from somewhere>;
switch(item)
{
    // This combines a type check and casting to the according type
    // in one go. Basically similar to former using 
    // if(item is Weapon weapon){ ... } else if(item is Potion potion) { ... }
    case Weapon weapon:
        // do something with a weapon e.g.
        Debug.Log($"weapon damage = {weapon.damage}", item);
        break;

    case Potion potion:
        // do something if it is a potion
        Debug.Log($"potion type = {potion.potionType}", item);
        break;

    case Amulet amulet:
        // do something if it is an amulet
        Debug.Log($"amulet type = {amulet.amuletType}", item);
        break;

    default:
        // a type you haven't handled so far -> either ignore or implement properly
        throw new ArgumentOutOfRangeException($"Type {item.GetType.AssemblyQualifiedName} is not handled yet!");
        break;
}
Run Code Online (Sandbox Code Playgroud)

最后,这是否需要是一个MonoBehaviour?看起来这可能是一个ScriptableObject或只是一个通用的

[Serializable] 
public class Items 
{ 
    ... 
}
Run Code Online (Sandbox Code Playgroud)

那么您宁愿为此实施自定义PropertyDrawer