简体   繁体   中英

Unity build error: The type or namespace name 'Editor' could not be found

I have an script Item.cs that is responsible for setting the parameters of an item in the player's inventory. In it, I connect the Unity Editor library and create a class that inherits from the Editor class. Until recently I was using Unity version 2020.3.0f1 where everything was building fine, but now I installed 2021.3.9f1 and I started having problems.

When I build the project, I get the following errors:

Assets\Inventory\Items.cs(31,31): error CS0246: The type or namespace name 'Editor' could not be found (are you missing a using directive or an assembly reference?)

Assets\Inventory\Items.cs(30,5): error CS0246: The type or namespace name 'CustomEditorAttribute' could not be found (are you missing a using directive or an assembly reference?)

Assets\Inventory\Items.cs(30,5): error CS0246: The type or namespace name 'CustomEditor' could not be found (are you missing a using directive or an assembly reference?)

Assets\Inventory\Items.cs(32,28): error CS0115: 'Items.ItemsEditor.OnInspectorGUI()': no suitable method found to override

Code:

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"));
      }
   }
}

You need to inherit Editor .

In the class line you should replace it with MonoBehaviour .

public class Items : Editor {

Note that now this script is not a MonoBehaviour , so it will not let you do certain things. For example, Update and Start may not be called.

(In your case you may not need this) If your current class needs to be a MonoBehaviour, I would reccomend a new class that inherits the Editor and keep this one the same. You can do all gui in the new class.


TL;DR Inherit Editor .

public class Items : Editor {

As the name suggests the UnityEditor namespace is only available within the Unity editor itself an completely stripped of / simply not included when compiling a build.

There is usually three ways of preventing editor code to be compiled during a build

In your case the simplest way would be to wrap everything related to the UnityEditor namespace in according #if - #endif pre-processors since you embedded the editor into the type itself.

I usually would prefer the same btw since having the editor embedded yields some advantages imho:

  • You can directly use nameof instead of hardcoded strings -> error prone and maintenance intense
  • You have access to even private serialized fields => can again use nameof even for those
  • You immediately know that for this type a custom editor exists and where to find it which is not that trivial when strictly separating them

I would btw not touch the target directly but completely go through 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
}

However

Now allow me to ask: Why not rather use proper inheritance and do the following without the need for any custom editor at all?

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

and then (each in individual script files of course)

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;
}

Now whenever you need to differ between different items you simply do

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;
}

And finally, does this need to be a MonoBehaviour ? It seems like this could ether be a ScriptableObject or just a generic

[Serializable] 
public class Items 
{ 
    ... 
}

for which you would then rather implement a custom PropertyDrawer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM