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"));
}
}
}
CodePudding user response:
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
- Put your filed in a folder called
Editor
- This entire content is ignored during a build (see also Special Folder Names) - Put your code into a folder with an Assembly Definition which is only compiled for the editor and no other target platform
- Use Conditional Compilation
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
CodePudding user response:
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 {