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

Unity中如何用bool变量在Update中启停协程?当前代码失效

问题核心原因

你当前代码失效的核心问题是调用StopCoroutine(Generate())无法停止正在运行的协程——因为每次调用Generate()方法都会返回一个全新的IEnumerator实例,你停止的只是刚创建的空协程,而非正在执行的那个。另外,重启逻辑也存在冗余,启动新协程前没必要先停止(旧协程根本没被正确终止)。


解决方案1:保存协程引用实例(精准控制启停)

通过存储协程的实例引用,实现对目标协程的精准启停控制,这是最可靠的方案:

using System.Collections;
using UnityEngine;

public class SpawnObjects : MonoBehaviour
{
    [Header("Prefabs To Spawn")]
    public GameObject prefabToSpawn;

    [Header("Terrain Settings")]
    public Terrain terrain;
    public GameObject spawnedTerrainObjectsParent;

    [Header("Spawn Settings")]
    public bool spawn = false;
    public int numberOfObjects = 100;
    public float duration = 1.0f;
    public float yOffset = 0.5f;
    public bool isParent = true;

    private float terrainWidth, terrainLength;
    private float xTerrainPos, zTerrainPos;
    private float yValTerrain, randXTerrain, randZTerrain;

    private bool oldSpawn;
    // 新增:存储正在运行的协程实例
    private Coroutine generateCoroutine;

    void Awake()
    {
        oldSpawn = spawn;

        if (terrain != null)
        {
            terrainWidth = terrain.terrainData.size.x;
            terrainLength = terrain.terrainData.size.z;
            xTerrainPos = terrain.transform.position.x;
            zTerrainPos = terrain.transform.position.z;

            if (isParent && spawnedTerrainObjectsParent == null)
            {
                spawnedTerrainObjectsParent = new GameObject("Spawned Terrain Parent");
            }
        }

        if(spawn)
        {
            generateCoroutine = StartCoroutine(Generate());
        }
    }

    private void Update()
    {
        if(oldSpawn != spawn)
        {
            if(spawn)
            {
                // 仅当协程未运行时才启动
                if(generateCoroutine == null)
                {
                    generateCoroutine = StartCoroutine(Generate());
                }
            }
            else
            {
                // 停止目标协程并清空引用
                if(generateCoroutine != null)
                {
                    StopCoroutine(generateCoroutine);
                    generateCoroutine = null;
                }
            }

            oldSpawn = spawn;
        }
    }

    private IEnumerator Generate()
    {
        for (int i = 0; i < numberOfObjects; i++)
        {
            // 每帧检查spawn状态,强制终止协程
            if(!spawn)
            {
                generateCoroutine = null;
                yield break;
            }
            
            randXTerrain = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
            randZTerrain = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
            yValTerrain = terrain.SampleHeight(new Vector3(randXTerrain, 0, randZTerrain)) + yOffset;

            GameObject objInstance = Instantiate(prefabToSpawn, new Vector3(randXTerrain, yValTerrain, randZTerrain), Quaternion.identity, spawnedTerrainObjectsParent.transform);
            objInstance.name = "Terrain Spawned";

            yield return new WaitForSeconds(duration);
        }
        // 协程正常结束后清空引用
        generateCoroutine = null;
    }
}

关键修改点

  • 新增generateCoroutine变量存储协程实例,确保StopCoroutine能精准终止目标协程。
  • Update中启动协程前判断引用是否为空,避免重复启动多个协程。
  • 协程内部每步检查spawn状态,一旦为false就主动终止并清空引用,防止协程在暂停后继续执行剩余逻辑。
  • 协程正常执行完毕后主动清空引用,保证下次能正常启动。

解决方案2:协程内部自我控制(简化逻辑)

如果不需要立即终止协程,允许它完成当前生成步骤后再停止,可以简化逻辑,去掉多余的状态变量:

using System.Collections;
using UnityEngine;

public class SpawnObjects : MonoBehaviour
{
    [Header("Prefabs To Spawn")]
    public GameObject prefabToSpawn;

    [Header("Terrain Settings")]
    public Terrain terrain;
    public GameObject spawnedTerrainObjectsParent;

    [Header("Spawn Settings")]
    public bool spawn = false;
    public int numberOfObjects = 100;
    public float duration = 1.0f;
    public float yOffset = 0.5f;
    public bool isParent = true;

    private float terrainWidth, terrainLength;
    private float xTerrainPos, zTerrainPos;
    private float yValTerrain, randXTerrain, randZTerrain;

    private bool isCoroutineRunning;

    void Awake()
    {
        if (terrain != null)
        {
            terrainWidth = terrain.terrainData.size.x;
            terrainLength = terrain.terrainData.size.z;
            xTerrainPos = terrain.transform.position.x;
            zTerrainPos = terrain.transform.position.z;

            if (isParent && spawnedTerrainObjectsParent == null)
            {
                spawnedTerrainObjectsParent = new GameObject("Spawned Terrain Parent");
            }
        }
    }

    private void Update()
    {
        // 仅在spawn开启且协程未运行时启动
        if(spawn && !isCoroutineRunning)
        {
            StartCoroutine(Generate());
        }
    }

    private IEnumerator Generate()
    {
        isCoroutineRunning = true;
        for (int i = 0; i < numberOfObjects; i++)
        {
            // 检查spawn状态,终止协程
            if(!spawn)
            {
                isCoroutineRunning = false;
                yield break;
            }
            
            randXTerrain = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
            randZTerrain = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
            yValTerrain = terrain.SampleHeight(new Vector3(randXTerrain, 0, randZTerrain)) + yOffset;

            GameObject objInstance = Instantiate(prefabToSpawn, new Vector3(randXTerrain, yValTerrain, randZTerrain), Quaternion.identity, spawnedTerrainObjectsParent.transform);
            objInstance.name = "Terrain Spawned";

            yield return new WaitForSeconds(duration);
        }
        isCoroutineRunning = false;
    }
}

逻辑说明

  • 用isCoroutineRunning标记协程运行状态,避免重复启动。
  • 协程内部每步检查spawn,一旦为false就终止。
  • Update中仅在spawn为true且协程未运行时启动,逻辑更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:02:08