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

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开头,未解决问题。

问题原因

  1. 竞态条件:每架敌机的Update是独立执行的,执行顺序不确定。当某架敌机触发movingDown = true后,其他敌机的Update可能已先将该静态变量重置为false,导致触发下移的敌机还没执行changeHeight,变量就被覆盖。
  2. 每帧重置标记: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:修改敌机预制体

  1. 删除原invaderScript中的移动相关逻辑(保留射击、死亡等其他逻辑即可),或者直接移除该脚本(如果只有移动逻辑的话)。
  2. 将所有敌机作为敌机管理器对象的子物体,确保管理器能正确收集到所有敌机。

为什么这个方案有效

  • 所有移动逻辑集中在管理器中,所有敌机的移动动作由同一代码触发,完全避免了多对象修改静态变量的竞态问题。
  • 边缘检测和移动执行统一处理,确保所有敌机的动作完全同步,不会出现部分敌机漏执行的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:37:30