先看一下官方对新增特性[SerializeReference]的描述
https://docs.unity3d.com/cn/2019.4/ScriptReference/SerializeReference.html
简而言之: 默认情况下,不支持多态字段,用 [SerializeReference] 修饰字段可指示 Unity“按引用”而非“按值”序列化字段。
借助这个特性可以实现字段的多态,达到改变具体子类的效果。
下面开始具体操作:
假设我们正在制作一个可以在编辑器内通过Inspector生成不同子类的物品类
创建一个代理类,继承MonoBehaviour,这个类放到场景中
public class ItemProxy : MonoBehaviour
{
[SerializeReference]
public ItemObjectBase ItemObject;
private void Start()
{
///start
}
private void OnDestroy()
{
///do something
}
}
该类负责持有SerializeReference引用的ItemObjectBase对象,并拥有MonoBehaviour生命周期,稍后对此类制作CustomEditor
创建ItemObjectBase类,不继承MonoBehaviour,这个类是真正的item对象
[Serializable]
public abstract class ItemObjectBase
{
public string ItemID;
protected ItemProxy Proxy;
public void Start(ItemProxy Proxy)
{
this.Proxy = Proxy;
mStart();
}
/// <summary>
/// Item版Start方法
/// </summary>
protected virtual void mStart() { }
public virtual void Use() { }
}
该类是我们真正自定义的物品基类,包含对物品的所有使用之类的逻辑。
创建CustomEditor来实现Inspector自定义
[CustomEditor(typeof(ItemProxy))]
public class ItemEditor : Editor
{
private Type[] _implementations;
private int _implementationTypeIndex;
public override void OnInspectorGUI()
{
ItemProxy item = target as ItemProxy;
if (item == null)
return;
if (_implementations == null || GUILayout.Button("刷新"))
{
//this is probably the most imporant part:
//find all implementations of INode using System.Reflection.Module
_implementations = GetImplementations<ItemObjectBase>().Where(impl => !impl.IsSubclassOf(typeof(UnityEngine.Object))).ToArray();
}
//select implementation from editor popup
_implementationTypeIndex = EditorGUILayout.Popup(new GUIContent("选择子类"),
_implementationTypeIndex, _implementations.Select(impl => impl.FullName).ToArray());
if (GUILayout.Button("生成子类"))
{
bool shouldGenerate = true;
if (item.ItemObject != null)
{
shouldGenerate = EditorUtility.DisplayDialog("确认", "现有对象将会被清除,确定要重新生成吗?", "是", "否");
}
if (shouldGenerate)
{
//set new value
item.ItemObject = (ItemObjectBase)Activator.CreateInstance(_implementations[_implementationTypeIndex]);
}
}
if (item.ItemObject == null)
{
EditorGUILayout.HelpBox("未生成对象,这个 GameObject 不会起任何作用。", MessageType.Warning);
}
else
{
EditorGUILayout.HelpBox($"当前类型: {item.ItemObject.GetType()}", );
}
base.OnInspectorGUI();
}
private static Type[] GetImplementations<T>()
{
var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes());
var interfaceType = typeof(T);
return types.Where(p => interfaceType.IsAssignableFrom(p) && !p.IsAbstract).ToArray();
}
}
这个自定义类中,我们会查找所有的派生类,然后提供popup在Inspector中选择一个具体的子类并创建,使得代理类中持有的ItemObjectBase变为具体的子类对象,随后我们可以发现Inspector面板中的属性参数成功变为子类的参数。
创建一个子类TestItem来看看效果:
public class TestItem : ItemObjectBase
{
public string Audio;
public override void Use()
{
base.Use();
///play this audio.
}
}
Inspector显示出子类的属性
本篇内容结束,如果有任何问题请留言给我。感谢观看。