You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中非空变量触发NullReferenceException问题排查求助

Unity中ScriptableObject飞船商店的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:07:33