Resources.Load加载ScriptableObject后读取List报空引用异常
问题结论
ScriptableObject完全支持存储可序列化的结构体List,通过Resources.Load加载后可以正常读取,你遇到的空引用和SO的存储、加载机制本身无关,是使用过程中的疏漏导致的,常见原因和修复方案如下:
常见原因排查
- 字段或结构体未配置序列化规则
Unity不会序列化所有C#字段,只有满足以下条件的字段内容才会被存入SO资源、随加载读取:- 列表字段为
public访问权限,或带有[SerializeField]特性;没有被标记为static、const、[NonSerialized] - List存储的自定义结构体带有
[System.Serializable]特性,且结构体内部所有字段均为Unity可序列化类型(基础值类型、string、UnityEngine.Object引用、其他符合序列化规则的自定义类型,不支持字典、未标记序列化的嵌套自定义类型等)
如果不满足上述规则,你在编辑时填入的列表内容不会被保存到资源中,加载后字段自然为null。
- 列表字段为
- 对应SO资源实例未初始化列表
很多人会在字段声明时写public List<XXX> sceneEntries = new List<XXX>();,但Unity反序列化资源时会覆盖字段初始值:如果你创建SO资源后,从来没有在Inspector中编辑过这个列表、也没有通过初始化代码给列表赋值,加载后这个字段就会保持null,不会自动走你写的字段初始化逻辑。 - 加载路径不匹配,加载到了未配置的SO资源
Resources.Load的路径是相对于项目内所有Resources文件夹的相对路径,不包含文件后缀,且大小写敏感。如果拼接出来的路径对应了另一个同类型、没有配置过sceneEntries列表的SO资源,就会出现“加载结果不为null,但访问列表报空”的情况。
修复方案
- 补全序列化标记,参考代码如下:
using System; using System.Collections.Generic; using UnityEngine; // 自定义结构体必须加Serializable特性才会被序列化 [Serializable] public struct SceneCheckpointEntry { // 结构体内部字段也必须是可序列化类型 public Vector3 spawnPos; public string checkpointId; public int unlockProgress; } [CreateAssetMenu(fileName = "checkpointData", menuName = "Game Config/Checkpoint Data")] public class CheckpointData : ScriptableObject { // 列表字段加SerializeField保证可序列化,不建议直接public暴露可写字段 [SerializeField] private List<SceneCheckpointEntry> _sceneEntries; // 对外提供只读访问 public List<SceneCheckpointEntry> sceneEntries => _sceneEntries; // 加兜底初始化,避免空引用 private void OnEnable() { if (_sceneEntries == null) { _sceneEntries = new List<SceneCheckpointEntry>(); } } } - 打开你要加载的对应SO资源,确认Inspector面板能看到sceneEntries列表,手动填入需要的配置数据后保存资源。
- 断点调试确认加载到的
attemptLoad实例就是你编辑的那个配置资源,排除路径拼错、加载到冗余同名资源的问题。
注意:通过Resources加载的SO资源是项目中的持久化资源文件,运行时直接修改sceneEntries里的内容会永久写入资源,导致下次运行配置被污染。如果运行时需要修改配置数据,请先创建列表的深拷贝再操作。
内容的提问来源于stack exchange,提问作者kv8
相关产品推荐
相关产品推荐

