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

Unity 2D基于标签忽略碰撞及远程单位攻击逻辑异常求助

问题描述

我正在开发一款类似《Age Of War》的2D游戏,包含近战单位与远程单位,所有单位共用同一脚本。我在FixedUpdate函数中实现了远程单位的敌人检测逻辑,代码如下:

private void FixedUpdate()
{
    if (RangeUnit)
    {
        RaycastHit2D hit = Physics2D.Raycast(enemyDetecter.transform.position, transform.right, rayDistance);

        if (hit.collider != null && hit.collider.gameObject != gameObject)
        {
            Debug.Log(CC_Name);

            if (!hit.collider.CompareTag(gameObject.tag))
            {
                //if enemy
                ShouldMove = false;
                ShouldAttack = true;
                Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.green);
                CC_Name = hit.collider.gameObject.name;
            }
            else
            {
                //if friend
                Physics2D.IgnoreCollision(hit.collider, GetComponent<Collider2D>());
                Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.magenta);
            }
            Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.yellow);
        }
        else
        {
            //if nothing
            ShouldAttack = false;
            ShouldMove = true;
            Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.red);
            Debug.Log("Hitted: Nothing");
        }
    }
}

场景中放置了3个单位:左侧1个近战单位、1个远程单位,右侧1个敌方近战单位。当前问题:远程单位检测到右侧敌人后停止移动并开始攻击,当友方近战单位击杀敌人后,远程单位仍持续攻击,调试CC_Name显示射线检测到的是友方单位而非敌人。所有单位处于同一Layer但标签不同。

死亡逻辑代码如下:

public void Death()
{
    if (Dead == true)
    {
        Destroy(gameObject);
    }
}

补充说明:当敌人与友方单位在远程单位前方交战时,远程单位表现正常;但敌人死亡后,远程单位会持续攻击直到友方单位超出射线检测范围。需求是找到彻底忽略友方碰撞的方法,同时解决攻击逻辑异常问题。

解决方案

1. 射线检测时直接过滤友方单位

当前代码检测到友方后仅忽略碰撞,但仍会进入判断分支导致ShouldAttack未被重置。修改为用Physics2D.RaycastAll获取所有命中对象,遍历筛选第一个敌方单位,跳过友方检测:

private void FixedUpdate()
{
    if (RangeUnit)
    {
        RaycastHit2D[] hits = Physics2D.RaycastAll(enemyDetecter.transform.position, transform.right, rayDistance);
        RaycastHit2D enemyHit = default;

        // 遍历命中对象,找到第一个敌方单位
        foreach (var hit in hits)
        {
            if (hit.collider.gameObject != gameObject && !hit.collider.CompareTag(gameObject.tag))
            {
                enemyHit = hit;
                break;
            }
        }

        if (enemyHit.collider != null)
        {
            // 检测到敌人
            ShouldMove = false;
            ShouldAttack = true;
            Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.green);
            CC_Name = enemyHit.collider.gameObject.name;
        }
        else
        {
            // 未检测到敌人
            ShouldAttack = false;
            ShouldMove = true;
            Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.red);
            Debug.Log("Hitted: Nothing");
        }
    }
}

2. 优化死亡逻辑执行时机

确保敌人死亡时Dead被及时设置,且销毁操作立即执行,避免延迟导致的误检测:

public void Death()
{
    Dead = true;
    // 可选:通知所有攻击当前单位的远程单位停止攻击
    Destroy(gameObject);
}

3. 用LayerMask优化射线检测(推荐)

给友方和敌方分配不同Layer,在射线检测时直接过滤友方Layer,从根源避免误判:

  1. 在Unity编辑器中创建Friend和Enemy Layer,分别分配给对应单位;
  2. 修改射线检测代码:
// 在Inspector中赋值Enemy层的LayerMask
public LayerMask enemyLayer;

private void FixedUpdate()
{
    if (RangeUnit)
    {
        RaycastHit2D hit = Physics2D.Raycast(enemyDetecter.transform.position, transform.right, rayDistance, enemyLayer);

        if (hit.collider != null)
        {
            ShouldMove = false;
            ShouldAttack = true;
            Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.green);
            CC_Name = hit.collider.gameObject.name;
        }
        else
        {
            ShouldAttack = false;
            ShouldMove = true;
            Debug.DrawRay(enemyDetecter.transform.position, transform.right * rayDistance, Color.red);
            Debug.Log("Hitted: Nothing");
        }
    }
}

4. 修复友方碰撞忽略逻辑

避免在FixedUpdate中重复调用忽略碰撞API,改为在初始化时一次性设置:

private void Start()
{
    if (RangeUnit)
    {
        GameObject[] friends = GameObject.FindGameObjectsWithTag(gameObject.tag);
        foreach (var friend in friends)
        {
            if (friend != gameObject)
            {
                Physics2D.IgnoreCollision(friend.GetComponent<Collider2D>(), GetComponent<Collider2D>());
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:07:34