Collision callbacks

Obi does not work with the default Unity collision callbacks (OnCollisionEnter,OnCollisionExit,OnCollisionStay...) because of performance reasons. Instead, the ObiSolver component can provide a list of all particle-collider contacts generated each frame upon request. You can then iterate this list of contacts and perform any custom logic you need.

To request the contact list from the solver, subscribe to its OnCollisionEvent.

The Oni.Contact struct contains the following information:

Distance

Distance from the particle to the collider, before resolving the collision (if any). The distance can be either positive (the particle is outside of the collider) or negative (the particle is partially or completely inside of the collider). A value of zero means the particle is just touching the surface. Obi uses a continuous-collision detection method known as speculative contacts, which can generate contacts even when an actual collision isnĀ“t taking place. If you want to prune all speculative contacts and consider actual collisions only, check for distances below a threshold (e.g 0.01).

Point

Nearest point to the particle on the surface of the collider.

Normal

The collider's surface normal at the contact point.

Tangent

The collider's surface tangent at the contact point.

Bitangent

The collider's surface bitangent at the contact point.

Distance

Distance between the entities involved in the contact. Will be negative or zero for actual collisions.

Particle

Index of the particle involved in the contact.

Other

Index of the collider involved in the contact. This index can be used to retrieve the actual collider object, like this:

ObiCollider.idToCollider[contact.other];

Normal impulse

Impulse magnitude applied by the contact in the normal direction. The larger this value, the stronger the initial collision force was.

Tangent impulse

Impulse magnitude applied by the contact in the tangent direction. The larger this value, the higher the friction between the particle and the collider.

Bitangent impulse

Impulse magnitude applied by the contact in the bitangent direction. The larger this value, the higher the friction between the particle and the collider.

Stick impulse

Impulse magnitude applied by the contact against the normal direction. This impulse keeps the particle stuck to the collider, only generated for sticky materials.

Retrieving the collider involved in a contact

You can get a reference to the collider involved in any contact by doing:
Collider collider = ObiCollider.idToCollider[contact.other] as Collider;
Or, a safer version:
Component colliderComponent;
if (ObiCollider.idToCollider.TryGetValue(contact.other,out colliderComponent)){
	Collider collider = colliderComponent as Collider;
}

Retrieving the actor involved in a contact

You can get a reference to the ObiActor (ObiCloth,ObiRope,ObiEmitter...) involved in any contact by doing:
ObiSolver.ParticleInActor pa = solver.particleToActor[contact.particle];
The ParticleInActor struct has two public variables:

Actor

A reference to the ObiActor to which this particle belongs.

IndexInActor

The index of this particle in the actor's arrays. For instance, you can kill a ObiEmitter particle upon collision by doing this:
ObiSolver.ParticleInActor pa = solver.particleToActor[contact.particle];
ObiEmitter emitter = pa.actor as ObiEmitter;

if (emitter != null)
    emitter.life[pa.indexInActor] = 0;

Example

Here's an example of a component that subscribes to the solver's OnCollision event and iterates over all the contacts. Then, it retrieves the collider object for all actual (non-speculative) contacts:

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

[RequireComponent(typeof(ObiSolver))]
public class CollisionEventHandler : MonoBehaviour {

 	ObiSolver solver;

	Obi.ObiSolver.ObiCollisionEventArgs collisionEvent;

	void Awake(){
		solver = GetComponent<Obi.ObiSolver>();
	}

	void OnEnable () {
		solver.OnCollision += Solver_OnCollision;
	}

	void OnDisable(){
		solver.OnCollision -= Solver_OnCollision;
	}

	void Solver_OnCollision (object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
	{
		foreach (Oni.Contact contact in e.contacts)
		{
			// this one is an actual collision:
			if (contact.distance < 0.01)
			{
				Component collider;
				if (ObiCollider.idToCollider.TryGetValue(contact.other,out collider)){
					// do something with the collider.
				}
			}
		}
	}

}
			

A script very similar to this one (CollisionEventHandler.cs, found in the sample scripts included with Obi) has been used here to draw all contacts using Unity's Gizmos class. In the following image you'll see speculative contacts (distance > 0) drawn in green, and actual contacts (distance <= 0) drawn in red:

Note the row of speculative contacts right across the middle. Since the floor plane is made out of 2 triangles and all particles about to touch the floor lie inside both triangles' bounding boxes, Obi considers they all have a quite high probability of colliding with both triangles. So speculative contacts are generated for the closest feature on the opposite triangle: the central edge. These are ignored later on the constraint enforcement phase, so normal and tangent impulses are only calculated and applied for the red contacts. However stick impulses can still be generated for speculative contacts, if they are close enough to becoming an actual contact.