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

Unity2D实例化随机对象仍报空引用异常问题求助

问题:Unity食物生成与伤害系统的空引用异常

我正在制作一款简易游戏,核心玩法是生成食物并对其造成伤害直至消失。需要实现从数组实例化随机对象、为实例化对象附加脚本,以及在另一脚本中对生成的食物造成伤害。目前两个脚本均触发Null Reference Exception(空引用异常),尝试在实例化对象时附加脚本后问题依旧存在。


Food脚本(Food.cs)

public class Food : MonoBehaviour
{
    public GameObject[] food;
    public Vector3Int spawnPosition;
    public int health = 200;
    public int currentHealth;

    private GameObject clone;

    public void Start()
    {
        currentHealth = health;
        SpawnFood();
    }

    //Spawning food 
    public void SpawnFood()
    {
        int random = Random.Range(0, food.Length); //Null Reference Exception occur here
        clone = Instantiate(food[random], this.spawnPosition, Quaternion.identity) as GameObject;

        clone.AddComponent<Food>();
    }

    public void TakeDamage(int damage)
    {
        currentHealth -= damage;

        //play hurt effect

        if(currentHealth < 0)
        {
            Vanish();
        }
    }

    void Vanish()
    {
        Debug.Log("Vanished");
    }
}

Board脚本(Board.cs)

public class Board : MonoBehaviour
{
    public Tilemap tilemap { get; private set; }
    public Piece activePiece { get; private set; }
    public TetrominoData[] tetrominoes;
    public Vector3Int spawnPosition;
    public Vector2Int boardSize = new Vector2Int(10, 20);
    public int damage;

    public Food clone;

    public TextMeshProUGUI hud_score;

    public static int currentScore = 0;

    public int scoreOneLine = 40;
    public int scoreTwoLine = 100;
    public int scoreThreeLine = 300;
    public int scoreFourLine = 1200;

    private int numberOfRowsThisTurn = 0;

    public RectInt Bounds
    {
        get
        {
            Vector2Int position = new Vector2Int(-this.boardSize.x / 2, -this.boardSize.y / 2);
            return new RectInt(position, this.boardSize);
        }
    }

    private void Awake()
    {
        this.tilemap = GetComponentInChildren<Tilemap>();
        this.activePiece = GetComponentInChildren<Piece>();

        //call Tetromino.Initialize() to spawn pieces
        for (int i = 0; i < this.tetrominoes.Length; i++)
        {
            this.tetrominoes[i].Initialize();
        }
    }
        
    private void Start()
    {
        SpawnPiece();
    }

    private void Update()
    {
        UpdateScore();
        UpdateUI();
    }

    public void UpdateUI()
    {
        hud_score.text = currentScore.ToString();
    }

    public void UpdateScore()
    {
        if(numberOfRowsThisTurn > 0)
        {
            if(numberOfRowsThisTurn == 1)
            {
                ClearedOneLine();
            } 
            else if (numberOfRowsThisTurn == 2)
            {
                ClearedTwoLine();
            } 
            else if (numberOfRowsThisTurn == 3)
            {
                ClearedThreeLine();
            } 
            else if (numberOfRowsThisTurn == 4)
            {
                ClearedFourLine();
            }

            numberOfRowsThisTurn = 0;
        }
    }

    public void ClearedOneLine()
    {
        currentScore += scoreOneLine;

        clone.GetComponent<Food>().TakeDamage(10); //Null Reference Exception occur here
    }

    public void ClearedTwoLine()
    {
        currentScore += scoreTwoLine;

        clone.GetComponent<Food>().TakeDamage(20); //Null Reference Exception occur here
    }

    public void ClearedThreeLine()
    {
        currentScore += scoreThreeLine;

        clone.GetComponent<Food>().TakeDamage(40); //Null Reference Exception occur here
    }

    public void ClearedFourLine()
    {
        currentScore += scoreFourLine;

        clone.GetComponent<Food>().TakeDamage(80); //Null Reference Exception occur here
    }

}

检查器配置说明

检查器中已为food数组配置64个元素(数组已正确赋值)。


解决方案

1. 修复Food脚本的空引用异常

问题根源:同一个脚本既承担食物生成器职责,又承担食物实体职责,导致实例化的新食物对象上的food数组未赋值,触发空引用。

代码优化:拆分职责

  • 新建FoodSpawner脚本负责生成逻辑
  • 修改Food脚本仅处理食物的生命值与受伤逻辑

修改后的Food脚本:

public class Food : MonoBehaviour
{
    public int health = 200;
    private int currentHealth;

    private void Start()
    {
        currentHealth = health;
    }

    public void TakeDamage(int damage)
    {
        currentHealth -= damage;

        //play hurt effect

        if(currentHealth <= 0)
        {
            Vanish();
        }
    }

    void Vanish()
    {
        Debug.Log("Vanished");
        Destroy(gameObject); // 销毁消失的食物对象
    }
}

新增FoodSpawner脚本:

public class FoodSpawner : MonoBehaviour
{
    public GameObject[] foodPrefabs; // 存放带有Food脚本的预制体
    public Vector3Int spawnPosition;

    private GameObject currentFood;

    private void Start()
    {
        SpawnFood();
    }

    public void SpawnFood()
    {
        if(foodPrefabs == null || foodPrefabs.Length == 0)
        {
            Debug.LogError("食物预制体数组为空或未赋值!");
            return;
        }
        int random = Random.Range(0, foodPrefabs.Length);
        currentFood = Instantiate(foodPrefabs[random], spawnPosition, Quaternion.identity);
        
        // 可选:如果预制体未挂载Food脚本,动态添加
        // if(currentFood.GetComponent<Food>() == null)
        // {
        //     currentFood.AddComponent<Food>();
        // }
    }

    // 提供给Board获取当前食物的接口
    public Food GetCurrentFood()
    {
        return currentFood?.GetComponent<Food>();
    }
}

2. 修复Board脚本的空引用异常

问题根源:Board中的clone变量未被赋值,导致调用GetComponent<Food>()时为空。

代码修改:通过Spawner获取食物实例

public class Board : MonoBehaviour
{
    // 新增:引用FoodSpawner对象
    public FoodSpawner foodSpawner;

    // 删除原有的public Food clone;

    // ... 其他代码保持不变 ...

    public void ClearedOneLine()
    {
        currentScore += scoreOneLine;
        Food currentFood = foodSpawner.GetCurrentFood();
        if(currentFood != null)
        {
            currentFood.TakeDamage(10);
        }
        else
        {
            Debug.LogWarning("没有可攻击的食物对象!");
        }
    }

    // 同理修改其余Cleared方法
    public void ClearedTwoLine()
    {
        currentScore += scoreTwoLine;
        Food currentFood = foodSpawner.GetCurrentFood();
        if(currentFood != null) currentFood.TakeDamage(20);
    }

    public void ClearedThreeLine()
    {
        currentScore += scoreThreeLine;
        Food currentFood = foodSpawner.GetCurrentFood();
        if(currentFood != null) currentFood.TakeDamage(40);
    }

    public void ClearedFourLine()
    {
        currentScore += scoreFourLine;
        Food currentFood = foodSpawner.GetCurrentFood();
        if(currentFood != null) currentFood.TakeDamage(80);
    }
}

最终配置步骤

  1. 创建空GameObject命名为FoodSpawner,挂载FoodSpawner脚本,并将食物预制体拖入foodPrefabs数组。
  2. 在Board脚本的检查器中,将FoodSpawner对象拖入foodSpawner字段。
  3. 确保Food脚本仅挂载在食物预制体上,或由FoodSpawner动态添加。

内容的提问来源于stack exchange,提问作者Anend

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 05:40:41