首页 > 解决方案 > 为什么一遍又一遍地启动协程时出现 MissingReferenceException 异常?

问题描述

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

public class GenerateWalls : MonoBehaviour
{
    public GameObject gameObjectToRaise;
    public float duration;
    public Vector3 raiseAmount;
    public bool go = false;
    public Color[] colors = new Color[4];
    public bool randomColors = false;

    private GameObject objtoraise;
    private GameObject[] walls;
    private bool scaleOver = false;

    private void Start()
    {
        Init();

        ColorWalls();

        // The z Axis must be minimum 1 or any value above 0 could be also 0.1f
        // but it's better to keep it minimum as 1 by default.
        if (raiseAmount.z < 1)
        {
            raiseAmount.z = 1f;
        }

        if (go)
        {
            StartCoroutine(ScaleOverSeconds(objtoraise, new Vector3(raiseAmount.x, raiseAmount.y,
                raiseAmount.z), duration));
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            //if (scaleOver)
            //{
            if (objtoraise != null)
            {
                if (raiseAmount.z < 1)
                {
                    raiseAmount.z = 1f;
                }


                Destroy(objtoraise);

                Init();

                ColorWalls();

                StartCoroutine(ScaleOverSeconds(objtoraise, new Vector3(raiseAmount.x, raiseAmount.y,
                    raiseAmount.z), duration));

                scaleOver = false;
                //}
            }
        }
    }

    private void Init()
    {
        objtoraise = Instantiate(gameObjectToRaise);
        objtoraise.name = "Walls";

        walls = GameObject.FindGameObjectsWithTag("Wall");
    }

    public IEnumerator ScaleOverSeconds(GameObject objectToScale, Vector3 scaleTo, float seconds)
    {
        float elapsedTime = 0;
        Vector3 startingScale = objectToScale.transform.localScale;
        while (elapsedTime < seconds)
        {
            objectToScale.transform.localScale = Vector3.Lerp(startingScale, scaleTo, (elapsedTime / seconds));
            elapsedTime += Time.deltaTime;
            yield return new WaitForEndOfFrame();
        }

        objectToScale.transform.localScale = scaleTo;

        scaleOver = true;
    }

    private void ColorWalls()
    {
        for (int i = 0; i < walls.Length; i++)
        {
            if (randomColors)
            {
                walls[i].transform.GetChild(0).GetComponent<Renderer>().material.color
                    = GetRandomColour32();
            }
            else
            {
                walls[i].transform.GetChild(0).GetComponent<Renderer>().material.color = colors[i];
            }
        }
    }

    private Color32 GetRandomColour32()
    {
        //using Color32
        return new Color32(
          (byte)UnityEngine.Random.Range(0, 255), //Red
          (byte)UnityEngine.Random.Range(0, 255), //Green
          (byte)UnityEngine.Random.Range(0, 255), //Blue
          255 //Alpha (transparency)
        );
    }
}

在 Update() 内部,当我按下 R 键时,它会破坏实例化对象,然后再次实例化并再次启动协程。问题是当我在两次之后连续多次按下 R 键时,我在编辑器中收到 MissingReferenceException 异常:

MissingReferenceException:“GameObject”类型的对象已被销毁,但您仍在尝试访问它。您的脚本应该检查它是否为空,或者您不应该销毁该对象。GenerateWalls+d__12.MoveNext () (在 Assets/Scripts/GenerateWalls.cs:81)

第 81 行是:

objectToScale.transform.localScale = Vector3.Lerp(startingScale, scaleTo, (elapsedTime / seconds));

目标是能够在每次按下 R 时再次生成墙,它应该停止当前的协程并重新开始。

也许问题是它在协程的中间,因为旧的协程还没有停止,所以对象在中间丢失了,因为我破坏了它?

那我应该怎么做才能一遍又一遍地按 R ,它会一遍又一遍地启动协程?不是启动多个协程,而是每次重新启动协程。

标签: c#unity3d

解决方案


解决方案是在 Update() 中添加: StopCoroutine

这不是解决方案。我认为停止协程也会停止内部的while循环,但它没有。似乎在 while 循环中检查 null 解决了这个问题:

public IEnumerator ScaleOverSeconds(GameObject objectToScale, Vector3 scaleTo, float seconds)
    {
        if (objectToScale != null)
        {
            float elapsedTime = 0;
            Vector3 startingScale = objectToScale.transform.localScale;
            while (elapsedTime < seconds)
            {
                if (objectToScale == null)
                {
                    yield return null;
                }
                else
                {
                    objectToScale.transform.localScale = Vector3.Lerp(startingScale, scaleTo, (elapsedTime / seconds));
                    elapsedTime += Time.deltaTime;
                    yield return new WaitForEndOfFrame();
                }
            }

            objectToScale.transform.localScale = scaleTo;

            scaleOver = true;
        }
    }

推荐阅读