Unity协程条件检查后仅生成单个敌人的问题排查
问题根源分析
你的问题出在SpawnEnemy协程的启动时机和循环逻辑上:
游戏启动时,你在Start()里直接启动了SpawnEnemy协程,但此时UIManager的playerExists初始值是false,协程里的while (_uimanager.playerExists == true)条件直接不成立,所以协程根本没进入循环就直接终止了。
之后当你按下空格把playerExists设为true时,这个协程已经结束了,自然不会继续生成敌人——你看到的那一个敌人,大概率是测试时偶然在playerExists为true的状态下启动游戏才生成的,正常流程下甚至可能一个都不会生成。
两种解决方案
方案一:修改协程逻辑,持续检查玩家状态
把协程改成无限循环,在循环内部判断玩家是否存在,存在就生成敌人并等待,不存在就每帧继续检查:
IEnumerator SpawnEnemy() { // 无限循环,直到对象被销毁或协程被手动停止 while (true) { if (_uimanager.playerExists) { // 生成敌人 Vector3 position = new Vector3(Random.Range(-8.23f, 8.23f), 5.7f, 0.0f); Instantiate(_enemyShipPrefab, position, Quaternion.identity); // 等待5秒再生成下一个 yield return new WaitForSeconds(5.0f); } else { // 玩家不存在时,每帧检查一次状态 yield return null; } } }
这个方案简单直接,不需要改动太多其他代码,适合你的简易游戏场景。
方案二:按需启动/停止协程(更规范的写法)
如果想更严谨地控制协程生命周期,可以在玩家创建时启动协程,玩家死亡时停止协程:
- 在SpawnManager中添加协程控制方法:
public class SpawnManager : MonoBehaviour { [SerializeField] private GameObject _enemyShipPrefab; [SerializeField] private GameObject[] _powerUp; UIManager _uimanager; // 保存协程引用,方便后续停止 private Coroutine _enemySpawnCoroutine; void Start () { _uimanager = GameObject.Find("Canvas").GetComponent<UIManager>(); StartCoroutine(SpawnPowerUps()); // 这里不再自动启动敌人生成协程 } // 对外暴露启动方法 public void StartEnemySpawn() { if (_enemySpawnCoroutine == null) { _enemySpawnCoroutine = StartCoroutine(SpawnEnemy()); } } // 对外暴露停止方法 public void StopEnemySpawn() { if (_enemySpawnCoroutine != null) { StopCoroutine(_enemySpawnCoroutine); _enemySpawnCoroutine = null; } } IEnumerator SpawnEnemy() { while (_uimanager.playerExists == true) { Vector3 position = new Vector3(Random.Range(-8.23f, 8.23f), 5.7f, 0.0f); Instantiate(_enemyShipPrefab, position, Quaternion.identity); yield return new WaitForSeconds(5.0f); } // 循环结束后清空引用 _enemySpawnCoroutine = null; } }
- 修改UIManager,调用协程控制方法:
public class UIManager : MonoBehaviour { public bool playerExists = false; public int playerScores = 0; public Sprite[] lives; public Image playerLivesImagesToBeShown; public Text playerScoreToBeShown; public Image titleImage; public GameObject playerPrefab; // 引用SpawnManager private SpawnManager _spawnManager; void Start() { _spawnManager = GameObject.FindObjectOfType<SpawnManager>(); } void Update() { if (Input.GetKeyDown(KeyCode.Space) && playerExists == false) { titleImage.gameObject.SetActive(false); Instantiate(playerPrefab, new Vector3(0, 0, 0), Quaternion.identity); playerScores = 0; playerScoreToBeShown.text = "Score : 0"; playerExists = true; // 启动敌人生成协程 _spawnManager.StartEnemySpawn(); } } public void updateLives(int livesToView ) { playerLivesImagesToBeShown.sprite = lives[livesToView]; if(livesToView == 0) { playerExists = false; titleImage.gameObject.SetActive(true); // 停止敌人生成协程 _spawnManager.StopEnemySpawn(); } } }
小提示
- 不管用哪种方案,都要确保
SpawnManager和UIManager的引用不会为空,可以在Unity编辑器中手动拖拽赋值,避免GameObject.Find可能带来的性能问题或空引用错误。 - 如果用方案一的无限循环,记得在场景切换或销毁
SpawnManager对象时,调用StopAllCoroutines()或者手动停止该协程,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Reshma Suresh
相关产品推荐
相关产品推荐

