带有 LayerMask 的 Unity Physics.Raycast 不会检测图层上的对象。使用了位移,尝试了反相层,仍然没有任何效果

Posted

技术标签:

【中文标题】带有 LayerMask 的 Unity Physics.Raycast 不会检测图层上的对象。使用了位移,尝试了反相层,仍然没有任何效果【英文标题】:Unity Physics.Raycast with LayerMask does not detect object on layer. Used bitshifting, tried inverting layer, still nothing works 【发布时间】:2021-10-05 21:05:37 【问题描述】:

这里是 Unity 中级开发人员的新手。在过去的 ~2 天里,我遇到了一个非常重要的障碍,涉及对具有特定图层的对象的光线投射检测。我一直在研究这个问题很多,我发现的所有解决方案似乎都没有反映我面临的奇怪问题。

基本上,问题遵循以下事件序列:

    我的玩家角色有一个名为“InSightBox”的视觉锥形触发网格,它检测所有带有“Mob”标签的对象并将它们添加到名为“MobsInRange”的对撞机列表中。
    public List<Collider> mobsInRange;

    public List<Collider> GetColliders()
    
        return mobsInRange;
    
    // Start is called before the first frame update
    void Start()
    
        mobsInRange = new List<Collider>();
    

    // Update is called once per frame
    void Update()
    
        
    
    //add enemy with tag 'mob' to list
    private void OnTriggerEnter(Collider other)
    
        if(!mobsInRange.Contains(other) && other.tag == "Mob")
        
            mobsInRange.Add(other);
        
    
    //remove enemy with tag 'mob' to list
    private void OnTriggerExit(Collider other)
    
        if (mobsInRange.Contains(other) && other.tag == "Mob")
        
            mobsInRange.Remove(other);
        
    
    然后,此列表被馈送到包含与玩家相关的所有内容的根/父玩家游戏对象。
    public Transform closestMob;
    public List<Collider> mobs;
    public Transform GetClosestEnemy()
    
        Transform tMin = null;
        float minDist = Mathf.Infinity;
        Vector3 currentPos = transform.position;
        foreach(Collider trans in mobs)
        
            //find enemy with closest distance and set tMin to it. Method returns tMin
            float dist = Vector3.Distance(trans.transform.position, currentPos);
            if(dist < minDist)
            
                tMin = trans.transform;
                minDist = dist;
                
            
        

        //Debug.Log(tMin);
        return tMin;
        
    
    然后玩家使用“查看”方法来查找所有“生物”中离玩家最近的。玩家将设置他们的正向变换以查看最近的生物。

问题步骤---> 4)当玩家举起枪并试图射击最近的敌人时,会投射一条带有图层蒙版的射线,该图层仅查找“敌人”图层上的对象,第 8 层。当射线检测到敌人时,敌人脚本应该触发其“TakeDamage”方法,该方法将“curHealth”变量减少 8。唯一的问题是,演员似乎没有检测到“敌人”层上的敌人对象。

LayerMask layerMask = 1 << 8;
void Fire()
    
        //play the audio of the gunshot
        StartCoroutine("SetPlaying");
        RaycastHit hit;
        
        
       //cast a ray from the player forward and check if the hit object is on layer 'Enemy'
       if (Physics.Raycast(transform.position, transform.forward, out hit, Mathf.Infinity, layerMask))
            
              //if hit object is an enemy, set its 'gotShot' bool to true
              print("hit enemy");
              closestMob.GetComponent<EnemyBase>().gotShot = true;
            
            //play gunshot sound and stop player from turning
            source.clip = fireSound;
            source.PlayOneShot(fireSound, gunshotVolumeScale);
            turnSpeed = 0;
       
    

我还要注意,我看到的所有解决此问题的方法都不适合我。我声明了一个名为“layerMask”的 int 变量,并在 Awake() 中通过将第 8 层移位到其中来初始化它(即 int layerMask = 1 奇怪的地方(至少据我所知),当我在演员表中反转掩码(~layerMask)时,它完全符合我的预期,并开始在当玩家“射击”任何没有“敌人”层的东西时,光线投射 if 语句。

当我快要把脸撞到桌子上时,任何帮助都将不胜感激:/

旁注:我已经到了可以将“射程”立方体触发器附加到我的播放器并在触发 Fire() 事件时启用它的地步,然后拥有它检查带有“mob”标签的游戏​​对象,因为这种检测对我来说最有效。

【问题讨论】:

很确定你需要~那个图层蒙版或者你排除了你唯一想要的东西 不。当我反转它时,它会按预期工作,并在遇到任何不在“敌人”层中的东西时触发代码。问题是当我切换回来时,敌人层中的对象没有被检测到。奇怪的是,文档中的说法与所有人所说的相反。 【参考方案1】:

首先,请确保您没有将图层与标签混淆。它们是不同的。

其次,摆脱任何隐式操作和引用,即不要使用位移、层索引或任何非直接引用。相反,创建这样的东西:

[SerializedField] private LayerMask _layerMask;

使用检查器分配所需的层。

这样您将明确看到您正在使用哪个层。这不仅对您有用,而且对您将来忘记图层索引时也有用。此外,对于不熟悉该项目的任何人。

第三个是调试。确保您的光线投射在其他方面按预期工作:

尝试删除 layerMask 并查看光线是否到达您想要的位置 使用自定义 Gizmo 检查您是否将光线投射到正确的方向 尝试使用RaycastAll。也许有些物体比你想象的更早捕捉(阻挡)你的光线

【讨论】:

是的,它肯定不是标签。标签是“Mob”,我用它来添加列表。这是我在 raycast 字段下指定的 100% 层。移除图层蒙版正如您所期望的那样,并开始在所有内容上触发演员阵容中的代码。此外,当使用 Debug.Ray 时,它确实会投射到指定为敌人的对象。 等等,你关于使用 Raycast 的第三条建议都奏效了!!!看起来正在发生的事情是包含脚本的“敌人”对象包含在光线投射命中时包含在另一个也有对撞机的 Empty 中。我意识到把它作为一个空的孩子是相当多余的,所以我将敌人的预制件重建为一个单独的对象,现在它可以很好地工作,即使只是 Raycast 而不是 RaycastAll。谢谢 JottoWorol !!!!!!【参考方案2】:

看起来问题在于我的“敌人”预制件包含嵌套在空游戏对象中的必要组件(刚体、碰撞器、脚本、导航网格代理)。起初我想把一些东西做成一个预制件,你需要把它放在一个空的里面。我现在看到这是相当多余且没有必要的(至少在我的情况下)。

Physics.RaycastAll 解决了这个问题,因为它不再被父空的对撞机“停止”并且也击中了子。

实际上,我只是使用常规 Physics.Raycast 通过将 Enemy 预制件重建为单个 Capsule 对象(稍后我将用角色网格替换)来让它工作。

旁注 在我开始使用这种新方法之前,我还使用了另一种以非常轻量级的方式实现相同目标的方法。

    我在播放器前面添加了一个细长的盒子触发器,以便它有足够的距离与射击范围内的任何敌人发生碰撞。 然后,只要玩家进入“瞄准”状态,我就会启用触发器。如果触发器与任何带有“Mob”标签的网格碰撞,它会在玩家身上设置一个布尔值,指示敌人是否在范围内。 然后,如果敌人在射程内并且玩家进入“开枪”状态,则会触发 Enemy Base 脚本中的一个方法,通过一个名为damagetaken 的公开决定的变量来减少敌人的生命值。

Raycast 方法和 box trigger 方法对我来说同样适用,但 box trigger 只需花费更少的时间来弄清楚并且让我不那么头疼哈哈。

希望这可以帮助其他处于困境的人!!!!!!

【讨论】:

以上是关于带有 LayerMask 的 Unity Physics.Raycast 不会检测图层上的对象。使用了位移,尝试了反相层,仍然没有任何效果的主要内容,如果未能解决你的问题,请参考以下文章

Unity LayerMask

Unity2D重叠圆LayerMask不起作用

Unity3D中Layers和LayerMask解析

Unity LayerMask 的位运算

[Unity]Ray射线物理检测碰撞和LayerMask的使用

[Unity]Ray射线物理检测碰撞和LayerMask的使用