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
相关产品推荐
相关产品推荐

