简体   繁体   中英

Check if collision between the model and two different objects is happening at the same time

Working in a VR unity project, trying to do some C# scripting.

My character model has two feet that are controlled in VR using trackers. I need a way to find out when both feet are colliding with cubes. cube A for left foot and cube B for the right foot at the same time. So that I can spawn another object when the condition is met.

What would be the way of doing it? What object should have the script? Right now, the cubes have a OnTriggerStay function that checks for collision with feet and changes the cube color to green.

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

public class CheckForFoot : MonoBehaviour {

    void OnTriggerStay(Collider other)
    {



    }
}

So this script is put on the cube that is on the floor. The cube has a collier on it the same as the foot. When the collision happens I can do something now, but I need to find out how to check for two identical cubes are colliding at the same time

What would be the way of doing it?

With Collider2D , that would be Physics2D.IsTouching(collider1, collider2) but this is Collider/3D and different no built-in API exist to do this. It's complicated but possible. Here are the steps simplified:

1 .Use List of KeyValuePair to store the touching objects.

static private List<KeyValuePair<GameObject, GameObject>> collisionList;

Make it a static variable so that there is only once instance of this list.

2 .Detect when there is a trigger with OnTriggerEnter .

The trigger detection script with the OnTriggerEnter function must be attached to each GameObject you want to detect when they are touching each other.

3 .Retrieve the two GameObjects that just touched each other.

You can Retrieve the first one by using this.gameObject and the second GameObject by using other.gameObject; . The other variable comes from the Collider other argument in the OnTriggerEnter function.

4 .Now, check if both GameObject exist in the collisionList variable from #1 . If they don't, add them. If they already exist, ignore it.


5 .Just like #2 , detect when there is a trigger exit with OnTriggerExit . This means that the Objects are no longer touching.

6 .Retrieve the two GameObjects that are no longer touching each other like you did in #3 but in the OnTriggerExit function this time.

7 .Now, check if both GameObject exist in the collisionList variable from #1 . If they do remove them from that List. If they don't ignore it.

What object should have the script?

Attach the CollisionDetection script to every GameObject you want to detect with another Object. Also make sure that the Collider's IsTrigger is enabled and that a Rigidbody component is attached to each GameObject too.

using System.Collections.Generic;
using UnityEngine;

public class CollisionDetection: MonoBehaviour

{
    static private List<KeyValuePair<GameObject, GameObject>> collisionList =
        new List<KeyValuePair<GameObject, GameObject>>();

    void OnTriggerEnter(Collider other)
    {
        //Debug.Log("Trigger Entered");

        //Get the two Objects involved in the collision
        GameObject col1 = this.gameObject;
        GameObject col2 = other.gameObject;

        //Add to the collison List
        RegisterTouchedItems(collisionList, col1, col2);
    }

    void OnTriggerExit(Collider other)
    {
        //Debug.Log("Trigger Exit");

        //Get the two Objects involved in the collision
        GameObject col1 = this.gameObject;
        GameObject col2 = other.gameObject;

        //Remove from the collison List
        UnRegisterTouchedItems(collisionList, col1, col2);
    }

    public static bool IsTouching(GameObject obj1, GameObject obj2)
    {
        int matchIndex = 0;
        return _itemExist(collisionList, obj1, obj2, ref matchIndex);
    }

    private void UnRegisterTouchedItems(List<KeyValuePair<GameObject, GameObject>> existingObj, GameObject col1, GameObject col2)
    {
        int matchIndex = 0;

        //Remove if it exist
        if (_itemExist(existingObj, col1, col2, ref matchIndex))

        {
            existingObj.RemoveAt(matchIndex);
        }
    }

    private void RegisterTouchedItems(List<KeyValuePair<GameObject, GameObject>> existingObj, GameObject col1, GameObject col2)
    {
        int matchIndex = 0;

        //Add if it doesn't exist
        if (!_itemExist(existingObj, col1, col2, ref matchIndex))

        {
            KeyValuePair<GameObject, GameObject> item = new KeyValuePair<GameObject, GameObject>(col1, col2);
            existingObj.Add(item);
        }
    }

    private static bool _itemExist(List<KeyValuePair<GameObject, GameObject>> existingObj, GameObject col1,
    GameObject col2, ref int matchIndex)
    {
        bool existInList = false;
        for (int i = 0; i < existingObj.Count; i++)
        {
            //Check if key and value exist and vice versa
            if ((existingObj[i].Key == col1 && existingObj[i].Value == col2) ||
                    (existingObj[i].Key == col2 && existingObj[i].Value == col1))
            {
                existInList = true;
                matchIndex = i;
                break;
            }
        }
        return existInList;
    }

}

USAGE:

Just use CollisionDetection.IsTouching(object1, object2) to check if two Objects are touching.

public GameObject foot1;
public GameObject foot2;

void Update()
{
    //Check if feet GameObjects are touching 
    bool touching = CollisionDetection.IsTouching(foot1, foot2);

    if (touching)
    {
        Debug.Log("<color=green>leg1 and leg2 touching</color>");
    }
    else
    {
        Debug.Log("leg1 and leg2 NOT touching");
    }
}

The accepted answer could be improved a lot by using a different data structure!

Why not mapping one object to a HashSet which can hold as many objects you need in a Dictionary<GameObject, HashSet<GameObject>> making also checks if an object is contained in the HashSet way more efficient than going through a list.

private static Dictionary<GameObject, HashSet<GameObject>> collisions =
    new Dictionary<GameObject, HashSet<GameObject>>();

private void Awake ()
{
    // Create a new entry in the Dictionary for this object
    collisions.Add(gameObject, new HashSet<GameObject>());
}

void OnTriggerEnter(Collider other)
{
    // Your Register method basically becomes that simple
    if(!collisions [gameObject].Contains(other.gameObject))
    {
        collisions[gameObject].Add(other.gameObject);
    }
}

void OnTriggerExit(Collider other)
{
    // And accordingly the unregister
    if(collisions [gameObject].Contains(other.gameObject))
    {
        collisionss[gameObject].Remove(other.gameObject);
    }
}

// Also the getter for your touches becomes way simpler
public static bool IsTouching(GameObject obj1, GameObject obj2)
{
    if(!collisions.ContainsKey(obj1)
    {
        return false;
    }

    return collisions[obj1].Contains(obj2);
}

private void OnDestroy ()
{
    // remove the entry for this object
    collisions.RemoveKey(gameObject);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM