Unity太空侵略者:敌群下移不同步问题排查求助
太空侵略者敌机同步下移问题
问题描述
从零开发Unity太空侵略者游戏时,已实现敌机群体同步左右移动,但任意敌机触碰到屏幕边缘时,所有敌机无法稳定同步下移。所有敌机为同一预制体实例,代码完全一致。
当前代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class invaderScript : MonoBehaviour { float timer = 0; [SerializeField] float delay = 1; static bool movingRight = true; static bool movingLeft = false; static bool movingDown = false; [SerializeField] int speed = 1; float heightTimeDelay = 0; bool initialDelayPassed = false; // Added flag for initial delay // Update is called once per frame void Update() { timer += Time.deltaTime; heightTimeDelay -= Time.deltaTime; // Check if initial delay has passed if (!initialDelayPassed) { if (timer >= delay) { initialDelayPassed = true; timer = 0; // Reset timer after initial delay } else { return; // Skip the rest of Update() until initial delay has passed } } movingDown = false; changeDirection(); changeHeight(); movingFunction(); } void movingFunction() { if (timer > delay) { if (movingRight) { transform.Translate(Vector2.right * speed * Time.deltaTime); timer = 0; } else if (movingLeft) { transform.Translate(Vector2.left * speed * Time.deltaTime); timer = 0; } } } void changeDirection() { if (transform.position.x > 7.5 && movingRight) { movingLeft = true; movingRight = false; movingDown = true; } else if (transform.position.x < -7.5 && movingLeft) { movingLeft = false; movingRight = true; movingDown = true; } } void changeHeight() { if (movingDown) { transform.Translate(Vector2.down); } } }
现象:使用静态变量movingLeft/movingRight/movingDown实现全局同步,左右移动正常,但下移动作不稳定——有时仅部分敌机下移,有时仅一架执行。尝试将movingDown = false移至LateUpdate或Update开头,未解决问题。
问题原因
- 竞态条件:每架敌机的
Update是独立执行的,执行顺序不确定。当某架敌机触发movingDown = true后,其他敌机的Update可能已先将该静态变量重置为false,导致触发下移的敌机还没执行changeHeight,变量就被覆盖。 - 每帧重置标记:
movingDown = false放在Update内,会在每帧开始时清空下移标记,部分敌机可能来不及响应。
解决方案:使用控制者模式统一管理
放弃让每个敌机自行判断移动逻辑,改用单独的敌机管理器统一控制所有敌机的移动,彻底避免同步问题。
步骤1:创建敌机管理器脚本
using UnityEngine; using System.Collections.Generic; public class EnemyManager : MonoBehaviour { [SerializeField] float moveDelay = 1f; // 移动间隔时间 [SerializeField] int moveSpeed = 1; // 水平移动速度 [SerializeField] float edgeXLimit = 7.5f; // 屏幕左右边缘X坐标 [SerializeField] float downMoveDistance = 1f; // 每次下移的距离 private List<Transform> allEnemies = new List<Transform>(); private bool isMovingRight = true; private float moveTimer = 0f; void Start() { // 收集所有子对象(敌机)的Transform组件 foreach (Transform enemyTransform in transform) { allEnemies.Add(enemyTransform); } } void Update() { moveTimer += Time.deltaTime; // 到达移动间隔时间时执行动作 if (moveTimer >= moveDelay) { CheckIfHitEdge(); ExecuteMove(); moveTimer = 0f; } } // 检测是否有敌机碰到边缘 void CheckIfHitEdge() { foreach (var enemy in allEnemies) { if (isMovingRight && enemy.position.x > edgeXLimit) { isMovingRight = false; break; } else if (!isMovingRight && enemy.position.x < -edgeXLimit) { isMovingRight = true; break; } } } // 执行水平移动或下移 void ExecuteMove() { // 判断是否需要下移 bool needMoveDown = (!isMovingRight && allEnemies.Exists(e => e.position.x > edgeXLimit)) || (isMovingRight && allEnemies.Exists(e => e.position.x < -edgeXLimit)); if (needMoveDown) { // 所有敌机下移 foreach (var enemy in allEnemies) { enemy.Translate(Vector2.down * downMoveDistance); } } else { // 所有敌机水平移动 Vector2 moveDir = isMovingRight ? Vector2.right : Vector2.left; foreach (var enemy in allEnemies) { enemy.Translate(moveDir * moveSpeed * moveDelay); } } } }
步骤2:修改敌机预制体
- 删除原
invaderScript中的移动相关逻辑(保留射击、死亡等其他逻辑即可),或者直接移除该脚本(如果只有移动逻辑的话)。 - 将所有敌机作为敌机管理器对象的子物体,确保管理器能正确收集到所有敌机。
为什么这个方案有效
- 所有移动逻辑集中在管理器中,所有敌机的移动动作由同一代码触发,完全避免了多对象修改静态变量的竞态问题。
- 边缘检测和移动执行统一处理,确保所有敌机的动作完全同步,不会出现部分敌机漏执行的情况。
内容的提问来源于stack exchange,提问作者Nik
相关产品推荐
相关产品推荐

