检测多个对象的接近度并更改它们的 material 颜色

Detecting proximity of multiple objects and changing their material color

我在使用距离计算正确检测物体接近度时遇到问题,希望你们中的一位能帮助我。

我想要什么: 我的场景中有几个具有相同标签的实例化游戏对象,如果它们在 x 轴和 z 轴上的距离低于“1”,我想更改它们的 material 颜色。为此,我遍历所有对象的列表并将它们的位置与当前游戏对象的位置进行比较。

问题: Material 碰撞对象的颜色会随机变化,有时碰撞结束后不会变回颜色。

到目前为止我的代码:

     public class DistanceTester : MonoBehaviour
 {
     void Start()
     {
 
     }
 
     void Update()
     {
         var menschen = GameObject.FindGameObjectsWithTag("Mensch");
         float positionX = transform.position.x;
         float positionZ = transform.position.z;
 
         foreach (GameObject mensch in menschen)
         {
             float distanceX = Mathf.Abs(mensch.transform.position.x - positionX);
             float distanceZ = Mathf.Abs(mensch.transform.position.z - positionZ);
 
             if (gameObject != mensch) //Making sure the object is not the same
             {
                 if (distanceX <= 1 && distanceZ <= 1)
                 {
                     GetComponent<Renderer>().material.color = Color.red;
                     mensch.GetComponent<Renderer>().material.color = Color.red;
                 }
                 else
                 {
                     GetComponent<Renderer>().material.color = Color.green;
                     mensch.GetComponent<Renderer>().material.color = Color.green;
                 }
             }
         }
     }
 }

我已经尝试过使用触发器进行碰撞检测,但我想使用更有效的方式,就像我上面的示例一样。

主要问题可能是你也设置了

GetComponent<Renderer>().material.color = ...;

menschen[0]近但离menschen[1]远怎么办?

→ 你总是用menschen中最后一项的结果重置你的颜色!


听起来您应该只处理自己的对象,因为所有其他对象都做同样的事情,对吗?

using Sytsem.Linq;

public class DistanceTester : MonoBehaviour
{
     // reference this via the Inspector already
     [SerializeField] private Renderer _renderer;

     private void Awake()
     {
         // As fallback get it ONCE
         if(!_renderer) _renderer = GetComponent<Renderer>();
     }
 
     private void Update()
     {
         // If possible you should also store this ONCE
         var menschen = GameObject.FindGameObjectsWithTag("Mensch");

         // This checks if ANY of the objects that is not this object is clsoe enough
         if(menschen.Where(m => m != gameObject).Any(m => (transform.position - m.transform.position).sqrMagnitude < 1))
         {
             _renderer.material.color = Color.red;
         }
         else
         {
             _renderer.material.color = Color.green;
         } 
     }
 }

其中此 Linq 表达式使用 Where and Any

menschen.Where(m => m!= gameObject).Any(m => (transform.position - m.transform.position).sqrMagnitude < 1)

基本上等于做类似的事情

var isClose = false;
foreach(var m in menschen)
{
    if(m == gameObject) continue;

    if((transform.position - m.transform.position).sqrMagnitude < 1)
    {
        isClose = true;
        break;
    }
}

if(isClose)
{
    ...

请注意,如果您可以将 FindGameObjectsWithTag 的结果存储一次 而不是每帧都获取它,它仍然会更有效率。

假设您的任何 Mensch 对象都将具有组件 DistanceTester,您甚至可以通过使用

这样的模式来实现某种“自动检测”
public class DistanceTester : MonoBehaviour
{
    private static HashSet<DistanceTester> _instances = new HashSet<DistanceTester>();

    private void Awake()
    {
        _instances.Add(this);

        ...
    }

    private void OnDestroy()
    {
        _instances.Remove(this);
    }

    ...
}

然后您可以非常有效地遍历 _instances

实际上,更有效的方法是只从全局控制器迭代一次,而不是在 DistanceTester!

的每个实例中都这样做