Unity构建模式下实例化ScriptableObject返回null问题咨询
Unity 2022.3.21f1c1中ScriptableObject实例化构建后显示null的问题
问题场景
我是Unity新手,使用2022.3.21f1c1版本,定义了继承自ScriptableObject的CardData类:
public class CardData : ScriptableObject { public string id; public string title; // ... 其他多种类型变量,包括列表、字典及自定义类对象 }
通过以下代码实例化:
CardData ref_card = card_list[0]; CardData card = UnityEngine.Object.Instantiate(ref_card); if(card.title.Contains("test")) { Debug.Log($"ref_card.title = {ref_card.title}, ref_card = {ref_card}"); Debug.Log($"card.title = {card.title}, card = {card}"); }
编辑器运行模式下日志正常:
ref_card.title = test1, ref_card = test (TcgEngine.CardData) card.title = test1, card = test(Clone) (TcgEngine.CardData)
但构建项目后(开启Development Build和Player Log),日志显示:
ref_card.title = test1, ref_card = test (TcgEngine.CardData) card.title = test1, card = null
同一代码多次测试均如此,存在两个疑问:
- 为何构建模式下实例化对象显示为null?
- 为何null对象可访问属性且不报错?
原因分析与解决方案
1. 构建后实例化对象显示null的原因
Unity的ScriptableObject在构建后的实例化逻辑和编辑器模式存在差异:
- 编辑器中,
Instantiate可以直接克隆ScriptableObject并保留引用追踪;但在构建后的Player环境中,如果ScriptableObject实例没有被正确序列化或未在内存中注册,Unity的Object.Equals/ToString逻辑会将其识别为"null",但实际内存中该对象的数据依然存在。 - 另一种可能是
card_list中的原始CardData对象在构建时被Unity的资源打包优化处理,导致克隆实例无法被引擎正确标记为有效Object。
2. 为什么"null"对象能访问属性不报错
Unity的Object子类(包括ScriptableObject)的空值判断和常规C#对象不同:
- Unity重载了
==运算符和Object.ToString()方法,当一个Unity Object实例失去了和引擎内部资源管理器的关联(比如构建中未正确注册),ToString()会返回"null",用==判断也会返回true,但该对象在托管堆上的实例依然存在,所以直接访问其公共字段(比如title)不会触发空引用异常——因为字段数据还在托管内存里,只是Unity引擎层面认为它是"无效"对象。
解决方法
- 改用
ScriptableObject.CreateInstance<CardData>()手动创建实例,然后逐个复制字段数据,替代Instantiate克隆:CardData ref_card = card_list[0]; CardData card = ScriptableObject.CreateInstance<CardData>(); // 手动复制所有需要的字段 card.id = ref_card.id; card.title = ref_card.title; // ... 复制其他列表、字典、自定义类对象等 - 如果必须使用
Instantiate,确保原始CardData对象被正确包含在构建资源中:比如将其放在Resources文件夹下,或者通过Addressables/AssetBundle管理,避免构建时被Unity剔除优化。 - 检查
CardData类中的自定义类对象是否标记了[System.Serializable],否则构建后可能出现序列化异常,导致实例无法被引擎正确识别。
内容的提问来源于stack exchange,提问作者xfish
相关产品推荐
相关产品推荐

