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

Unity3D敌人AI避障方向选择的条件判断优化方案咨询

嘿,这个需求我太熟悉了——用位掩码重构方向判断绝对是解决大量if嵌套的最优解,不仅代码会清爽很多,后续维护和扩展也方便。我给你梳理一套落地的思路,一步步来:

第一步:用带Flags特性的枚举定义方向位掩码

先把7个方向对应到不同的二进制位上,C#里的[Flags]特性能让我们方便地做位运算,把多个方向打包成一个整数:

[Flags]
public enum EnemyMoveDirection
{
    None = 0,          // 00000000(无可行方向)
    ForwardLeft = 1 << 0,  // 00000001(左前方)
    ForwardRight = 1 << 1, // 00000010(右前方)
    Left = 1 << 2,         // 00000100(左侧)
    Right = 1 << 3,        // 00001000(右侧)
    BackLeft = 1 << 4,     // 00010000(左后方)
    BackRight = 1 << 5,    // 00100000(右后方)
    Back = 1 << 6          // 01000000(正后方)
}

每个方向对应一个独有的二进制位,这样我们就能用一个枚举值来表示所有可行方向的集合。

第二步:收集可行方向的位掩码

遍历你要检测的7个方向,对每个方向发射Raycast,如果没有击中障碍物,就把对应的位加到掩码里:

private EnemyMoveDirection GetAvailableDirections()
{
    EnemyMoveDirection available = EnemyMoveDirection.None;
    LayerMask obstacleMask = LayerMask.GetMask("Obstacles"); // 只检测障碍物层,排除自身和玩家
    float rayDistance = 1.5f; // 根据你的游戏场景调整射线长度

    // 检测左前方
    if (!Physics.Raycast(transform.position, transform.TransformDirection(new Vector3(-1, 0, 1).normalized), rayDistance, obstacleMask))
    {
        available |= EnemyMoveDirection.ForwardLeft;
    }

    // 检测右前方
    if (!Physics.Raycast(transform.position, transform.TransformDirection(new Vector3(1, 0, 1).normalized), rayDistance, obstacleMask))
    {
        available |= EnemyMoveDirection.ForwardRight;
    }

    // 依次检测左侧、右侧、左后方、右后方、正后方,逻辑和上面一致
    // ...

    return available;
}

这里要注意用transform.TransformDirection把局部方向转换成世界空间的射线方向,同时用LayerMask避免误检测到敌人自身。

第三步:按优先级从掩码中选最优方向

核心的方向选择逻辑来了,我们把优先级组做成一个列表,按顺序检查,完全不用嵌套if:

private EnemyMoveDirection SelectBestDirection(EnemyMoveDirection availableDirections)
{
    // 定义优先级顺序的方向组,越靠前优先级越高
    var priorityGroups = new List<EnemyMoveDirection>
    {
        EnemyMoveDirection.ForwardLeft | EnemyMoveDirection.ForwardRight,
        EnemyMoveDirection.Left | EnemyMoveDirection.Right,
        EnemyMoveDirection.BackLeft | EnemyMoveDirection.BackRight,
        EnemyMoveDirection.Back
    };

    foreach (var group in priorityGroups)
    {
        // 用位与运算提取当前优先级组里的可行方向
        var validInGroup = availableDirections & group;
        if (validInGroup == EnemyMoveDirection.None)
            continue; // 这个优先级组没有可行方向,跳过

        // 把当前组里的可行方向拆成列表,方便随机选择
        List<EnemyMoveDirection> options = new List<EnemyMoveDirection>();
        foreach (EnemyMoveDirection dir in Enum.GetValues(typeof(EnemyMoveDirection)))
        {
            if (dir == EnemyMoveDirection.None)
                continue;
            if ((validInGroup & dir) != EnemyMoveDirection.None)
            {
                options.Add(dir);
            }
        }

        // 随机选一个返回
        return options[Random.Range(0, options.Count)];
    }

    // 兜底:如果所有方向都被挡住(理论上不会出现,留个保险)
    return EnemyMoveDirection.None;
}

这个逻辑的好处是,以后要调整优先级或者新增方向,只要修改priorityGroups列表或者枚举就行,完全不用动核心判断逻辑。

第四步:把方向转换成移动向量

最后,我们可以做一个字典,把每个方向映射成局部空间的移动向量,这样选完方向后直接拿来用:

private Dictionary<EnemyMoveDirection, Vector3> _directionToVector = new Dictionary<EnemyMoveDirection, Vector3>
{
    { EnemyMoveDirection.ForwardLeft, new Vector3(-1, 0, 1).normalized },
    { EnemyMoveDirection.ForwardRight, new Vector3(1, 0, 1).normalized },
    { EnemyMoveDirection.Left, new Vector3(-1, 0, 0).normalized },
    { EnemyMoveDirection.Right, new Vector3(1, 0, 0).normalized },
    { EnemyMoveDirection.BackLeft, new Vector3(-1, 0, -1).normalized },
    { EnemyMoveDirection.BackRight, new Vector3(1, 0, -1).normalized },
    { EnemyMoveDirection.Back, new Vector3(0, 0, -1).normalized }
};

// 在Update里调用逻辑
void Update()
{
    var available = GetAvailableDirections();
    var bestDir = SelectBestDirection(available);
    if (bestDir != EnemyMoveDirection.None)
    {
        Vector3 moveDir = transform.TransformDirection(_directionToVector[bestDir]);
        transform.Translate(moveDir * moveSpeed * Time.deltaTime, Space.World);
    }
}
额外优化建议
  • 可以把射线检测的参数(方向向量、检测距离、LayerMask)做成可配置的数组,甚至存在ScriptableObject里,不用改代码就能调整每个方向的检测规则。
  • 如果敌人碰撞体较大,用SphereCast或BoxCast代替Raycast,能更准确判断是否会撞墙,避免“射线能过但身体会卡”的情况。
  • 可以加入玩家位置的权重:如果玩家在某个可行方向的范围内,优先选择朝向玩家的方向,只要在收集可行方向时给对应位加权重,或者在随机选择时做加权随机即可。

这样整个逻辑就非常清晰,完全摆脱了大量的if嵌套,而且扩展性拉满!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:11:16