1
votes

I couldn't come up with a suitable title for this question but I'll explain.

I have a collision box, which holds a script. This script has an if statement that detects collision from object "Cube001" and sends a Debug.Log to console.

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

 public class cubeDetect : MonoBehaviour {

     void OnCollisionEnter(Collision collision) {

         if (collision.gameObject.name == "Cube001")
         {
             Debug.Log("Cube001 hit!");
         }
     }

 }

With this method, the box collider knows what cube has touched it, as I have instructed so with

collision.gameObject.name == "Cube001"

But say if I have 10 cubes colliding with the collision box, how can I change the if statement so instead of writing another 9 if statements that check if it touches the collision box, I can just have 1 if statement that just first detects a collision from another cube, knows what cube hit the box, and with this knowledge, is able to do a Debug.Log to display the name of the cube that hit the box.

I've tried going through the documentation for OnCollisionEnter but couldn't find anything to help with this.

3
use tags instead of the object name for checking (and only put the name in the Debug.Log) - UnholySheep
@UnholySheep Tag still requires many if statements. I think that OP want's to remove the many if statements required to do this - Programmer
Hi @UnholySheep, if I use tags then each cube will be essentially the same object, which just spreads my issue to multiple objects - toadflax
@toadflax Does each object have unique name? - Programmer
@toadflax if you give them the same tag, you can still print the unique name of the object, as my solution below states... and therefore determine the unique object that has collided. all of the gameobject's information is stored in the collision object that is passed to the OnCollisionEnter function - ryeMoss

3 Answers

4
votes

One option would be to tag all of your similar objects you want to collide with, with the same name. Say we give them the tag "Cubicle". Then we can do the following:

Gameobject myCube;
void OnCollisionEnter(Collision collision) {

     if (collision.collider.tag == "Cubicle")
     {
         Debug.Log(collision.gameObject.name + " hit!");
         myCube = collision.gameObject; // store colliding gameobject info for use elsewhere
     }
 }
2
votes

You need to use Dictionary. This eliminates the need for all the if statements.

This is what the Dictionary should look like:

public static Dictionary<GameObject, System.Action> objToAction = new Dictionary<GameObject, Action>();

Then a function to add objects to the dictionary when they are instantiated or in the Start function if they already exist

public void registerObject(GameObject obj, System.Action action)
{
    objToAction.Add(obj, action);
}

The key in the Dictionary is the GameObject(Cube), you can also use string(name of the GameObject) but using the GameObject is better. The value in the Dictionary stores what you want to to do when OnCollisionEnter is called. So the code that should've been inside that if statement should be placed here. This is done with Action and delegate. You can add as many GameObjects (Cubes) as you wish.

You must add those Cube Objects to the Dictionary with the function above:

public GameObject prefab;

void Start()
{
    //Create new Cube
    GameObject obj = Instantiate(prefab);
    obj.name = "Cube001";

    //Add it to the Dictionary
    registerObject(obj, delegate { Debug.Log("Hit: " + obj.name); });
}

Your new OnCollisionEnter code is as below. No if statement required. If the Object exist in the Dictionary, execute that code we stored in the value of that Dictionary for each key GameObject.

void OnCollisionEnter(Collision collision)
{
    Action action;

    if (objToAction.TryGetValue(collision.gameObject, out action))
    {
        //Execute the approprite code
        action();
    }
}

Note that the objToAction variabel should either be made static or placed in another script attached to an empty GameObject so that you can access it. There should only be one instance of it.

1
votes

What was working best for me was to use interfaces and components and check for that. This is working great if you have certain logic on collision but when you don't you can just use tag and set it to something like "collidable".

interfaces solution:

public interface ICollidableObject
{
    void CollidedWith(ICollidableObject other);
}

public class CollidableBlock : MonoBehaviour, ICollidableObject
{
    public void CollidedWith(ICollidableObject other)
    {
        Debug.Log((other as MonoBehaviour).gameObject.name);
    }
}

public class CollidableSphere : MonoBehaviour, ICollidableObject
{
    public void CollidedWith(ICollidableObject other)
    {
        Debug.Log("sphere001");
    }
}

And just use it like such :

void OnCollisionEnter(Collision collision) 
{
    ICollidableObject collidedWith = collision.gameObject.GetComponent<ICollidableObject>();
    if ( collidedWith != null )
        collidedWith.CollidedWith(this);
}