【问题标题】:2D Raycast does not correctly detect overlapping colliders and ignores Sorting Layer2D Raycast 无法正确检测重叠碰撞器并忽略排序层
【发布时间】:2019-12-17 15:05:48
【问题描述】:

我希望能够与我的游戏对象交互,同时使用排序层组织我的精灵。 目前,Sorting Layer 不会影响我的 Raycast/collider 情况,只会改变我的订单的视觉外观。

在我的测试场景中,我有两个精灵:一个背景和一个对象。 两者都有一个 2D Box Collider 组件。 我的 Raycast 脚本应该检测我的鼠标悬停在哪个对象上;背景 (BG) 或位于 BG 顶部的对象。

只要在检查器中启用 BG 碰撞器,我的脚本将只识别 BG,即使另一个对象位于我的 BG 顶部(视觉上和根据排序层)(Z 位置 = 0 ,排序层默认(BG:0,对象1))。 对撞机具有正确的大小,如果我从 BG 禁用或删除对撞机,我的脚本将检测到其他对象就好了。 确保在激活 BG 对撞机时检测到我的对象的唯一方法是将对象 Z 位置更改为我的 BG 和我的相机之间的位置(介于较小的 0 和较大的 -10 之间)

我尝试过:

  • 这是一个全新的 2D 项目,所以我的相机 Projection 设置为 Orthograph,我的 BG 和 Object 使用 Sprite Renderer 并使用 2D 碰撞器
  • 来回更改“层中的顺序”,包括负数。
  • 添加前景(和背景)“排序层” (这两个选项都只有视觉效果,不影响 Raycast 输出)

  • 更改层次结构中的位置。

  • 删除并重新添加对撞机。 (!)我观察到我最后添加的 2D Box 对撞机将始终位于所有其他对撞机之后。这是我现在可以使用的东西。但在以后项目变大时会成为问题。

我希望能够在我的 2D 点击冒险中与物品和门等(点击和对鼠标悬停的反应)进行交互。如果 2D Raycast 不是存档的方法,我很高兴知道在这种情况下使用的正确技术。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class RayCast : MonoBehaviour
{
    [SerializeField] private Vector2 mousePosWorld2D;
    RaycastHit2D hit;

    void Update()
    {
        mousePosWorld2D = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);

        hit = Physics2D.Raycast(mousePosWorld2D, Vector2.zero);
        if (hit.collider)
        {
            Debug.Log("Found collider: " + hit.collider.name);
        }
        else
        {
            Debug.Log("No collider found.");
        }
    }
}

我预计“Sorting Layer”或“Order in Layer”会对我的 Raycast 产生影响,而更改 Z 位置则无济于事。但情况恰恰相反。排序层仅具有视觉效果,将我的对象移近相机(Z 位置)有助于检测正确的对撞机。

我该如何解决这个问题?

【问题讨论】:

    标签: c# unity3d raycasting collider


    【解决方案1】:

    Ray 最先命中的内容仅与对撞机与相机的距离有关,与 Hierarchy 或 SortinLayers 中的顺序无关。

    为了根据 SortingLayer 找到“最高”的 RaycastTarget,我创建了下面的脚本。

    我们通过使用 foreach 循环发射 Raycast 并迭代我们击中的所有 Collider。然后该函数将返回具有最高 SortingLayer 的 GameObject。注意:如果存在多个具有最高 SortingLayer 的 GameObject,该函数将返回它找到的最后一个。

    using UnityEngine;
    
    public class Raycast : MonoBehaviour
    {
        // The name of the relevant Layer
        [SerializeField]  string layerToCheck = "Default";
    
        void Update()
        {
            Vector2 mousePosWorld2D = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
    
            GameObject result = GetHighestRaycastTarget(mousePosWorld2D);
    
    
            // Do Stuff example:
            Debug.Log($"Highest Layer: {result}");
    
            if (Input.GetMouseButtonDown(0)
                && result != null)
            {
                Destroy(result);
            }
        }
    
    
        // Get highest RaycastTarget based on the Sortinglayer
        // Note: If multiple Objects have the same SortingLayer (e.g. 42) and this is also the highest SortingLayer, then the Function will return the last one it found
        private GameObject GetHighestRaycastTarget(Vector2 mousePos)
        {
            GameObject topLayer = null;
            RaycastHit2D[] hit = Physics2D.RaycastAll(mousePos, Vector2.zero);
    
            foreach (RaycastHit2D ray in hit)
            {
                SpriteRenderer spriteRenderer = ray.transform.GetComponent<SpriteRenderer>();
    
                // Check if RaycastTarget has a SpriteRenderer and
                // Check if the found SpriteRenderer uses the relevant SortingLayer
                if (spriteRenderer != null
                    && spriteRenderer.sortingLayerName == layerToCheck)
                {
                    if (topLayer == null)
                    {
                        topLayer = spriteRenderer.transform.gameObject;
                    }
    
                    if (spriteRenderer.sortingOrder >= topLayer.GetComponent<SpriteRenderer>().sortingOrder)
                    {
                        topLayer = ray.transform.gameObject;
                    }
                }
    
            }
    
            return topLayer;
        }
    }
    
    

    【讨论】:

      【解决方案2】:

      您是否尝试过 Unity 官方文档中的脚本? https://docs.unity3d.com/ScriptReference/Physics.Raycast.html

      using UnityEngine;
      
      // C# example.
      
      public class ExampleClass : MonoBehaviour
      {
          void Update()
          {
              // Bit shift the index of the layer (8) to get a bit mask
              int layerMask = 1 << 8;
      
              // This would cast rays only against colliders in layer 8.
              // But instead we want to collide against everything except layer 8. The ~ operator does this, it inverts a bitmask.
              layerMask = ~layerMask;
      
              RaycastHit hit;
              // Does the ray intersect any objects excluding the player layer
              if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, Mathf.Infinity, layerMask))
              {
                  Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * hit.distance, Color.yellow);
                  Debug.Log("Did Hit");
              }
              else
              {
                  Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * 1000, Color.white);
                  Debug.Log("Did not Hit");
              }
          }
      }
      

      【讨论】:

      • 是的,我确实使用 Unity 文档。但排除层并没有真正帮助我。如果我使用 LayerMask 来排除我的背景,当我有两个对象相互重叠时,我仍然会遇到问题。
      猜你喜欢
      • 2020-12-31
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      相关资源
      最近更新 更多