开发平台:Unity
编程平台:Visual Studio 2017以上
使用语言:C#
问题描述
SerializedObjectNotCreatableException: Object at index 0 is null
问题剖析
问题情况一:
- 该问题报错以为程序序列化过程中出现空值无法处理与赋值造成的。
具体案例如下:
[Tooltip("X轴向最大数")] private int maxXCount = 64;
[Tooltip("X轴向文本内容")] public static string xCount;
private void OnGUI()
{
xCount = EditorGUILayout.TextField("X轴大小", xCount);
this.maxXCount = Convert.ToInt32(xCount);
...
}
这是一个拓展编辑器脚本的部分代码内容。其中EditorGUILayout.TextField(Title, String)
用于在EditorWindow
上绘制文本输入框。【可参考继承Monobehaviour
的public string str;
在Inspector下的公开】。脚本经历一下步骤:
- 建立 TextField 窗口
- 将 TextFiled 文本内容转换为 Int 数据类型并存储于
maxXCount
中
值得注意的是,OnGUI()
是固定时间刷新EditorWindow上的数值。在初次打开EditorWindow
面板时,TextField 窗口内的数据时为空(string.Empyty
),对这种数据无法使用Convert.ToInt32()
进行转换。
问题情况二:
这是通过System.Reflection
反射获取的UnityEditor关于Transform组件的内容的程序段,目的在于修改/拓展原生Transform组件信息。
private void OnEnable()
{
_Editor = CreateEditor(target, Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.TransformInspector", false));
}
【错误】其中关于 。GetAssembly().GetType()
中的flase
为禁用 throw On Error,即抛出异常问题。很神奇/幸运 的是这类异常报错有时候并不影响工具的运行,对整个项目不会产生坏影响。对这类由Unity底层设计出现的错误,仅设置flase
即可规避问题
【推测】根据Unity生命周期,Editor的激活调用在OnReset。而OnInspectorGUI()位于OnReset - OnEnable之后,然后反复调用OnInspectorGUI。在调用扩展编辑器上仅出现一次关于此类的报错。但出现一个问题 OnInspectorGUI结束后再次重复调用。唯一需要确认的时CreatSerializedObject()方法是如何?
【更新】Editor.CreateEditor()
用于创建自定义编辑器扩展。其解释需要Target
与Type
两个参数。
Target
:源于继承的Editor
提供。Type
:通过反射获取。而正是因为这个参数导致 SerializedObject Not CreatableException 的最终原因。
在Editor.CreateEditor()
方法中,解释无指明Editor Type类型情况下,返回为Editor
对象为NULL
。查阅反射文章,对电脑性能要求极高,同时开销极大。则是否可以理解当执行下列代码段:
Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.TransformInspector", false)
获取Transform Type类型这一过程在极短的计算机时间内并没有完成对Type类型的赋值。于是,在每一次的切换Inspector窗口所对应的对象,其Inspector会销毁与创建新的组件对象。即使出现此类报错,也仅是偶然或一段时间。但组件扩展依旧正常运行。
原理可参考UnityWebRequest
,直接访问服务器,可能出现【正在连接中】,而非【连接成功】。所以搭配协程IEnumerator
进行Yeild return request.SendWebRequest();
直到确认到服务器连接后进行。
解决方案:
一、直接解决(根据实际逻辑情况处理,不一定有效解决)
检查代码是否出现类似赋值于转置情况。预先赋值。例如:在 xCount = EditorGUILayout.TextField("X轴大小", xCount);
前添加 xCount = maxXCount.ToString();
进行赋值。
二、替换代码逻辑思路(同上)
修改EditorGUILayout.TextField(Title, String)
为EditorGUILayout.TextField(String)
或如下:
this.maxXCount = Convert.ToInt32(GUILayout.TextField(this.maxXCount.ToString()));
备注:该方法在EditorWindow
上可使用,但无法添加文本标题。仅使用GUILayout.Label(Title)
来完成排版。对排版界面上友强迫性的人不会很友好。
三、关于问题二的解决方法【更新】
- 使用类同
IEnumerator
的优先获取Type,直到反射过程完成后,再进行创建Editor
。
注意:因为是面向Editor模式下的编辑器扩展,据笔者理解情报Editor模式下无法使用协程。
弊端:受限制计算机性能差异,每次打开Inspector窗口,会不稳定的出现Transform组件(扩展版)。 - 使用
DrawDefaultInspector()
替代base.OnInspectorGUI();
。扩展Transform组件并不对原组件进行修改重设计。该方法适用于基于原组件界面基础上添加额外的Button等小部件。因而在Awake/OnEnable
阶段的Editor
创建及相关属性可删除(未使用)。