在构造函数 C# 中获取 NULL 引用错误

问题描述 投票:0回答:1

创建一个基本的贪吃蛇游戏。

试图让游戏对象在网格范围内生成,带有精灵。但是,当尝试调用生成该对象的函数时,出现 NULL REFERENCE 错误。但是,我确信我已经清楚地创建了对该对象的引用?

Snake.cs 脚本(错误发生在行:'levelGrid.SnakeMoved(gridPosition)')

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Snake : MonoBehaviour
{
    private Vector2Int gridMoveDirection;
    private Vector2Int gridPosition;
    private float gridMoveTimer;
    private float gridMoveTimerMax;
    private LevelGrid levelGrid;

    public void Setup(LevelGrid levelGrid)
    {
        this.levelGrid = levelGrid;
    }


    private void Awake ()
    {
        gridPosition = new Vector2Int(10, 10);
        gridMoveTimerMax = .5f;
        gridMoveTimer = gridMoveTimerMax;
        gridMoveDirection = new Vector2Int(1, 0);
    }

    // Update is called once per frame
    private void Update()
    {
        HandleInput();
        HandleGridMovement();
    }

    private void HandleInput()
    {
        if (Input.GetKeyDown(KeyCode.UpArrow) && gridMoveDirection.y != -1)
        {
            gridMoveDirection.x = 0;
            gridMoveDirection.y = +1;
        }
        if (Input.GetKeyDown(KeyCode.DownArrow) && gridMoveDirection.y != +1)
        {
            gridMoveDirection.x = 0;
            gridMoveDirection.y = -1;
        }
        if (Input.GetKeyDown(KeyCode.LeftArrow) && gridMoveDirection.x != +1)
        {
            gridMoveDirection.x = -1;
            gridMoveDirection.y = 0;
        }
        if (Input.GetKeyDown(KeyCode.RightArrow) && gridMoveDirection.x != -1)
        {
            gridMoveDirection.x = +1;
            gridMoveDirection.y = 0;
        }
    }

    /* Explanation: HandleGridMovement()
     * gridMoveTimer += Time.deltaTime causes the variable of gridMove to incrediment in REAL TIME.
     * gridMoveMaxTime is initalized as the starting value of gridMoveTimer, every 0.5 seconds the if statement will take place
     * the if statement transform both the position of the head of the snake, alongside rotating it accordingly.
     */
    private void HandleGridMovement()
    {

        gridMoveTimer += Time.deltaTime;
        if (gridMoveTimer >= gridMoveTimerMax)
        {
            gridMoveTimer -= gridMoveTimerMax;
            gridPosition += gridMoveDirection;

            transform.position = new Vector3(gridPosition.x, gridPosition.y);
            transform.eulerAngles = new Vector3(0, 0, GetAngleFromVector(gridMoveDirection) - 90);//eurlerAngles r just the rotation angles.

            levelGrid.SnakeMoved(gridPosition);
        }

    }

    private float GetAngleFromVector(Vector2Int dir)
    {
        float n = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
        if (n < 0) n += 360;
        return n;
    }
}

LevelGrid.cs 脚本:(不起作用的脚本)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LevelGrid
{

    private Vector2Int foodGridPosition;
    private GameObject foodGameObject;
    private int width;
    private int height;
    private Snake snake;

    public LevelGrid(int width, int height)
    {
        this.width = width;
        this.height = height;

        Debug.Log("here!");
        SpawnFood();
    }

    public void Setup(Snake snake)
    {
        this.snake = snake;
    }

    private void SpawnFood()
    {
        foodGridPosition = new Vector2Int(4,4);

        foodGameObject = new GameObject("FoodApple", typeof(SpriteRenderer));
        foodGameObject.GetComponent<SpriteRenderer>().sprite = GameAssets.i.foodSprite;
        foodGameObject.transform.position = new Vector3(foodGridPosition.x, foodGridPosition.y);
    }

    public void SnakeMoved(Vector2Int snakeGridPosition)
    {
        if (snakeGridPosition == foodGridPosition)
        {
            Object.Destroy(foodGameObject);
            SpawnFood();
        }
    }

}

为清楚起见,其他两个脚本:

GameHandler.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameHandler : MonoBehaviour
{

    [SerializeField] private Snake snake;
    
    private LevelGrid levelGrid;


    private void Start()
    {
        Debug.Log("GameHandler Called!");

        levelGrid = new LevelGrid(20, 20);

        snake.Setup(levelGrid);
        levelGrid.Setup(snake);
    }

}

GameAssets.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameAssets : MonoBehaviour
{

    public static GameAssets i;

    private void Awake()
    {
        i = this;
    }
    
    public Sprite snakeHeadSprite;
    public Sprite foodSprite;
}
c# unity3d 2d
1个回答
0
投票

问题在

Update()
,但“原因”在这里:
snake.Setup(levelGrid)
.

Unity 无法保证实例化对象的顺序。此外,

Awake()
在创建时立即被调用,然后是
Update()
,因此不能保证
Setup()
会更快。

最佳实践方法是在

Snake.Update()
中这样做:

private void Update()
{
    if (levelGrid == null)
        return;

    HandleInput();
    HandleGridMovement();
}

当您的变量依赖于某种流逻辑并且依赖于时间或某些外部设置时,永远不要假设它们已被分配。帧循环、渲染、物理更新甚至重复调用等周期性调用在这里具有优势,因为如果代码未准备好,您可以“跳过”代码执行。

对于单发功能,您需要进行适当的设计以免出现漏洞。幸运的是,你属于前一类。

© www.soinside.com 2019 - 2024. All rights reserved.