In this tutorial I’m going to introduce Unity 3D’s built-in collision system. We’re going to cover adding collider and rigidbody components to your game objects, how to detect and react to collisions between them, and how to access collision data by script and listen to events triggered by the physics engine.
Just note that, depending on the type of game you’re building, you may want to explore different options, including Quad-tree collisions or Ray-casting.
For the purpose of this tutorial we’re going to use a 3D project. I’ll assume you have basic C# scripting skills as well as familiarity with the Unity editor and know how to do basic operations like creating empty game objects, nesting game objects, setting tags and layers, and adding components.
Note: This tutorial is based on Unity 4. The screenshots have been captured from version 4.5.1. As new releases are distributed, menus, names or layout in the editor may differ.
Defining a collider
When creating a new game object in the Unity editor, we can add new components either in the top menu or in the Inspector, after selecting the related object. Colliders are located under the Physics tab.
The basics ones are Box, Sphere and Capsule collider components.
The Box Collider defines a cube area where collisions will be detected. You can define the box’s center and size using the Inspector.
The Sphere Collider is similar, but using a spheric volume instead. Finally, the Capsule collider defines a Capsule volume for collision detection. In this case, you’ll also be able to define the height of the capsule, and the axis for orientation.
The collider volume and position does not have to correspond with your game object’s mesh, and frequently it won’t.
All Colliders have a Material field. It refers to the Physics material which determines how the collider will react to collisions, in regard to friction and bounciness. We’ll ignore it for now as it’s beyond the scope of this tutorial.
If you’re going to use the collider only as a trigger area (i.e. to detect if an object reaches a certain area of the level in order to trigger a cut-scene) you need to tick βIs Trigger.” This way, collisions will be ignored by the physics engine but they’ll still generate events that you can listen to in your scripts.
You can also define multiple colliders for a single object to define complex collision areas. To do so, you need to create empty child objects of your game object, and define colliders for each child.
Mesh Colliders
Mesh Colliders are a particular type of collider which uses an actual mesh for collision detection.
They are quite expensive as they compute collision against every face of the mesh, so depending on the complexity of your meshes they can add up quickly. For this reason, you should avoid using Mesh colliders if possible. If you really need to, you can supply a separate mesh to the component, which has the same shape and structure of the game object’s mesh but with fewer faces.
Normally, collisions between two mesh colliders are ignored. If you want to detect collisions between mesh colliders, you need to set them as Convex in the Inspector. In this case, you’ll need to supply a mesh with less than 255 faces.
Adding a RigidBody
If you want your object to react to physical collision with other objects and the game world, you’ll need to add a RigidBody component. A game object with a rigid body will be influenced by gravity and external forces.
From the Inspector, you can set whether you want discrete or continuous collision detection.
Continuous collision detection is used when dealing with fast moving objects. When using Discrete collision detection, some collisions may not be detected as they may go through the other object collider between the time another check is performed. By default, this is set to Discrete and you should leave it as continuous collision detection may really slow down your game.
You won’t need a rigidbody if your object’s collider is set as trigger.
Scripting
Every MonoBehaviour attached to the game object will listen for collision with other game objects in the world. The engine offers multiple methods, depending on the collision stage and type of collision.
For non-trigger collision, you’ll use OnCollisionEnter, OnCollisionExit, and OnCollisionStay.
OnCollisionEnter is called when the game object collider starts touching another game object with a collider and rigidbody attached. While colliding, OnCollisionStay will be called once per frame. Finally, when the collision stops, OnCollisionExit will be called.
Here’s an example:
using UnityEngine;
using System.Collections;
public class CollisionTutorialTest : MonoBehaviour
{
void OnCollisionEnter(Collision collisionInfo)
{
print("Detected collision between " + gameObject.name + " and " + collisionInfo.collider.name);
print("There are " + collisionInfo.contacts.Length + " point(s) of contacts");
print("Their relative velocity is " + collisionInfo.relativeVelocity);
}
void OnCollisionStay(Collision collisionInfo)
{
print(gameObject.name + " and " + collisionInfo.collider.name + " are still colliding");
}
void OnCollisionExit(Collision collisionInfo)
{
print(gameObject.name + " and " + collisionInfo.collider.name + " are no longer colliding");
}
}
As you can see, each of these methods supply a Collision object, which contains info about the contact points, the object it collided with, their relative velocity etc. We use it to print to the terminal information about the collision between the two game objects.
If you don’t need to access collision info, you can leave it empty and it will save some calculations.
void OnCollisionStay()
{
print(gameObject.name + β collided with another objectβ);
}
Just note that, if the collision stops as a result of one of the objects being destroyed — using Destroy() — OnCollisionExit won’t be called.
If the other game object’s collider is set as Trigger, OnTriggerEnter, OnTriggerStay, and OnTriggerExit will be used instead. They work roughly the same way, but supply the Collider object directly instead of giving a Collision object:
void OnTriggerEnter(Collider other)
{
print("Collision detected with trigger object " + other.name);
}
void OnTriggerStay(Collider other)
{
print("Still colliding with trigger object " + other.name);
}
void OnTriggerExit(Collider other)
{
print(gameObject.name + " and trigger object " + other.name + " are no longer colliding");
}
As before, if you don’t need info about the other collider, you can leave it empty and speed things up a bit.
void OnTriggerEnter()
{
print("Collision detected with a trigger object");
}
Just remember that, for trigger collision event to be triggered, one of the two colliders needs to have a rigid body attached.
When dealing with collisions, it’s useful to set different tags for the game objects in your game world. This way, you can quickly determine how to react with different types of collisions in your game, whether it’s a collectable, an enemy or anything else:
void OnCollisionEnter(Collision collisionInfo)
{
if(collisionInfo.collider.tag == "Enemy")
{
print("Lose health point");
}
else if (collisionInfo.collider.tag == "Powerup")
{
print("Collect powerup");
}
}
Filtering collisions
You can force the collision system to ignore certain type of collisions, either specifying the actual objects collider or using layers.
To ignore collisions between the game object and another game object you’ll need to use IgnoreCollision, supplying the respective colliders:
Physics.IgnoreCollision(gameObject.collider, anotherGameObject.collider);
Just note that both objects need to be active for it to work. As soon as one of them is deactivated you’ll need to call IgnoreCollision again.
On the other hand, IgnoreLayerCollision lets you specify two layers (using their IDs as integers) and will tell the collision system to ignore collisions between objects of layer1 and layer2.
Physics.IgnoreLayerCollision(1, 2);
You can set default values for this under Project Settings – Physics, using the Layer Collision Matrix in the Inspector:
If you need to check if collision between two layers are ignored, you can use GetIgnoreLayerCollision:
bool areIgnored = Physics.GetIgnoreLayerCollision(1, 2);
Author: Attilio Carotenuto