2D Raycast 无法正确检测重叠碰撞器并忽略排序层
2D Raycast does not correctly detect overlapping colliders and ignores 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 项目,所以我的相机投影设置为 Orthograph,我的 BG 和对象使用 Sprite Renderer 并使用 2D collider
- 来回更改 "Order in Layer",包括负数。
添加前景(和背景)"Sorting Layer"
(两个选项都只有视觉效果,不影响Raycast输出)
更改层次结构中的位置。
- 删除并重新添加对撞机。 (!) 我观察到我最后添加的 2D Box collider 将始终位于其他所有 collider 之后。这是我现在可以使用的东西。但是以后项目做大了就会出问题了。
我希望能够在我的 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 位置)有助于检测正确的对撞机。
如何解决问题?
您是否尝试过使用 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");
}
}
}
射线首先击中的对象仅与碰撞体与相机的距离有关,与层次结构或 SortinLayers 中的顺序无关。
为了找到基于 SortingLayer 的“最高”RaycastTarget,我创建了下面的脚本。
我们使用 foreach 循环触发 Raycast 并迭代我们命中的所有 Collider。然后该函数将 return 具有最高 SortingLayer 的 GameObject。注意:如果有多个具有最高 SortingLayer 的 GameObject,该函数将 return 它找到的最后一个。
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;
}
}
我希望能够在使用排序层组织我的精灵时与我的游戏对象进行交互。 现在,排序层不会影响我的 Raycast/collider 情况,只会改变我订单的视觉外观。
在我的测试场景中,我有两个精灵:一个背景和一个对象。 两者都有一个 2D Box Collider 组件。 我的 Raycast 脚本应该检测我的鼠标悬停在哪个对象上;背景 (BG) 或位于 BG 之上的对象。
只要在检查器中启用 BG 碰撞器,我的脚本就只会识别 BG,即使其他对象位于我的 BG 之上(视觉上并根据排序层)(两个 Z 位置= 0,排序层默认值(BG:0,对象 1))。 对撞机具有正确的大小,如果我禁用或从 BG 中删除对撞机,我的脚本将检测到另一个对象。 确保我的对象被检测到,同时我的 BG 碰撞器被激活的唯一方法是将对象 Z 位置更改为我的 BG 和我的相机之间的位置(在更小的 0 和更大的 -10 之间)
我试过:
- 这是一个新鲜的 2D 项目,所以我的相机投影设置为 Orthograph,我的 BG 和对象使用 Sprite Renderer 并使用 2D collider
- 来回更改 "Order in Layer",包括负数。
添加前景(和背景)"Sorting Layer" (两个选项都只有视觉效果,不影响Raycast输出)
更改层次结构中的位置。
- 删除并重新添加对撞机。 (!) 我观察到我最后添加的 2D Box collider 将始终位于其他所有 collider 之后。这是我现在可以使用的东西。但是以后项目做大了就会出问题了。
我希望能够在我的 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 位置)有助于检测正确的对撞机。
如何解决问题?
您是否尝试过使用 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");
}
}
}
射线首先击中的对象仅与碰撞体与相机的距离有关,与层次结构或 SortinLayers 中的顺序无关。
为了找到基于 SortingLayer 的“最高”RaycastTarget,我创建了下面的脚本。
我们使用 foreach 循环触发 Raycast 并迭代我们命中的所有 Collider。然后该函数将 return 具有最高 SortingLayer 的 GameObject。注意:如果有多个具有最高 SortingLayer 的 GameObject,该函数将 return 它找到的最后一个。
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;
}
}