检测多个物体的接近度并更改其材质颜色

时间:2020-07-28 11:19:21

标签: c# list unity3d distance collision

我在使用距离计算正确检测对象邻近度时遇到问题,希望你们中的一个能帮助我。

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

问题: 碰撞对象上的材质颜色会随机变化,并且一旦碰撞结束,有时不会变回原来的颜色。

到目前为止,我的代码:

     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;
                 }
             }
         }
     }
 }

我已经尝试使用触发器进行碰撞检测,但希望使用一种更有效的方式,如上面的示例中那样。

1 个答案:

答案 0 :(得分:2)

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

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表达式使用WhereAny

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的每个实例中都这样做!