Unity中非空变量触发NullReferenceException问题排查求助
问题描述
我用Unity开发多年,近期在基于ScriptableObject创建飞船组件商店时遇到了NullReferenceException错误。已确认shipComponents列表非空且包含2个元素,且通过if (shipComponents == null || shipComponents.Count == 0) continue;过滤了空列表,但仍触发空引用异常,报错指向ShipComponent sc = shipComponents[currentIndex];这一行。
相关ScriptableObject定义
ShipComponent类
public class ShipComponent : ScriptableObject { public int shipID; public string shipName; [TextArea] public string shipDescription; public ShipComponentType ComponentType; public float Mass; public float Friction; public float Thrust; public float Friction2AngularFrictionCoef; public float Thrust2AngularThrustCoef; public bool IsQuantum; public Sprite previewSprite; }
DatabaseComponents类
public class DatabaseComponents : ScriptableObject { public int databaseID; public string databaseName; public List<Guild> databaseGuilds; }
Guild类
public class Guild : ScriptableObject { public int guildID; public string guildName; public List<GameObject> guildComponents; }
问题脚本ShipCreatorDEBUG
using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; public class ShipCreatorDEBUG : MonoBehaviour { [Header("Component Shop")] [SerializeField] private Transform shopPanel; [SerializeField] private GameObject guildNamePrefab; [SerializeField] private GameObject rowPrefab; [SerializeField] private DatabaseComponents database; private Dictionary<Guild, List<ShipComponent>> guildToListComponents; private void Start() { guildToListComponents = GenerateShipComponentDictionary(database); GenerateShop(); } // Shop void GenerateShop() { foreach (Transform trans in shopPanel) { Destroy(trans.gameObject); } foreach (var entry in guildToListComponents) { Guild guild = entry.Key; List<ShipComponent> shipComponents = entry.Value; if (shipComponents == null || shipComponents.Count == 0) continue; GameObject rowName = Instantiate(guildNamePrefab, shopPanel); TMP_Text tmp = rowName.GetComponentInChildren<TMP_Text>(); tmp.text = guild.guildName; int count = shipComponents.Count; int rowAmount = Mathf.CeilToInt(count / 3f); for (int i = 0; i < rowAmount; i++) { GameObject rowComponents = Instantiate(guildNamePrefab, shopPanel); for (int j = 0; j < 3; j++) { int currentIndex = i * 3 + j; if (currentIndex >= count) break; Transform buttonTransform = rowComponents.transform.GetChild(j); Button btn = buttonTransform.GetComponent<Button>(); ShipComponent sc = shipComponents[currentIndex]; // NullReferenceException here btn.onClick.AddListener(() => SelectComponentInfo(sc)); Image image = buttonTransform.GetChild(0).GetComponent<Image>(); image.sprite = sc.previewSprite; } } } } public void SelectComponentInfo(ShipComponent shipComponent) { // TODO } public Dictionary<Guild, List<ShipComponent>> GenerateShipComponentDictionary(DatabaseComponents database) { Dictionary<Guild, List<ShipComponent>> guildComponentsDictionary = new Dictionary<Guild, List<ShipComponent>>(); foreach (Guild guild in database.databaseGuilds) { List<ShipComponent> shipComponentsList = new List<ShipComponent>(); foreach (GameObject componentPrefab in guild.guildComponents) { ShipComponentObject shipComponentObject = componentPrefab.GetComponent<ShipComponentObject>(); if (shipComponentObject != null) { shipComponentsList.Add(shipComponentObject.shipComponent); } } guildComponentsDictionary[guild] = shipComponentsList; } return guildComponentsDictionary; } }
报错日志
NullReferenceException: Object reference not set to an instance of an object ShipCreatorDEBUG.GenerateShop () (at Assets/Assets/Scripts/TestS/ShipCreatorDEBUG.cs:57) ShipCreatorDEBUG.Start () (at Assets/Assets/Scripts/TestS/ShipCreatorDEBUG.cs:20)
可能的原因及解决方案
shipComponents列表存在空元素:在GenerateShipComponentDictionary方法中,仅判断了shipComponentObject != null,但未检查其shipComponent字段是否为null。部分预制体的ShipComponentObject组件可能未赋值shipComponent,导致列表中混入null元素。
解决:添加空值检查,过滤无效元素:if (shipComponentObject != null && shipComponentObject.shipComponent != null) { shipComponentsList.Add(shipComponentObject.shipComponent); }同时在
GenerateShop中访问前增加防御性判断:ShipComponent sc = shipComponents[currentIndex]; if (sc == null) continue;预制体实例化错误:代码中实例化组件行时使用了
guildNamePrefab,但字段中定义了专门的rowPrefab。若guildNamePrefab的子物体结构与组件行所需不符,可能导致后续逻辑混乱,间接引发空引用。
解决:替换为正确的预制体:GameObject rowComponents = Instantiate(rowPrefab, shopPanel);database未在Inspector赋值:虽然你确认列表有元素,但仍需确保database字段已在Unity编辑器中正确赋值,否则database.databaseGuilds会为null,导致后续逻辑异常。
解决:在Start方法中增加检查:private void Start() { if (database == null) { Debug.LogError("DatabaseComponents未在Inspector中赋值!"); return; } guildToListComponents = GenerateShipComponentDictionary(database); GenerateShop(); }循环闭包潜在问题:虽然本次报错发生在列表访问时,但闭包捕获循环变量可能导致后续点击事件异常,可提前规避:
ShipComponent sc = shipComponents[currentIndex]; var tempSc = sc; btn.onClick.AddListener(() => SelectComponentInfo(tempSc));
内容的提问来源于stack exchange,提问作者I'm Gogole

