Back to Journal

Triggers and Collisions: Two Ways for Objects to Meet

Unity lets two colliders meet either as solid bodies that push and bounce or as overlapping volumes that merely report each other's presence, and choosing between them shapes both the physics and the code that responds.

Objects in a game meet in two quite different ways, and the difference is older than any engine. Sometimes a meeting is an obstacle: the boulder strikes the wall, the arrow lodges in the door, the falling crate comes to rest on the floor. At other times a meeting is only an awareness: a traveller crosses a threshold, a hand passes over a coin, a figure steps into the circle of a lamp, and nothing physical resists, yet something in the world ought to notice.

Unity expresses this distinction with a single checkbox. Every collider has an isTrigger setting, and depending on its value the collider either behaves as a solid surface that the physics engine uses to push bodies apart, or as an intangible volume that bodies pass through freely while the engine reports the overlap to scripts. The two modes produce two separate families of messages, governed by slightly different rules, and a great deal of confusion comes from mixing them up.

This article examines both families with some patience: what a trigger is and what it gives up, the collision messages and the rich information that accompanies them, the rules about Rigidbodies that decide whether any message is sent at all, the timing of these events within the physics step, and the everyday uses, zones and pickups and impacts, where one mode or the other is plainly the right choice.

Solid meetings

When neither collider is a trigger, a meeting between them is a collision in the full physical sense. The engine detects that the shapes overlap or are about to, computes where they touch and in which direction, and applies the forces needed to keep them apart, with friction and bounciness taken from their physics materials. A ball bounces, a sliding crate slows against the ground, a character stops at a wall. All of this happens whether or not any script is listening.

Scripts that do want to listen receive three messages. OnCollisionEnter is called on the step when two colliders begin touching, OnCollisionStay on each subsequent physics step while they remain in contact, and OnCollisionExit on the step when they separate. The messages are sent to scripts on both GameObjects involved, so a falling rock and the ground it lands on each have an opportunity to respond, the rock with a cloud of dust and the ground perhaps with nothing at all.

One detail of OnCollisionStay often catches developers out. A Rigidbody that has come to rest may be put to sleep by the engine, which stops simulating it until something disturbs it again, and sleeping bodies do not receive stay messages. A crate that has settled on a pressure plate may therefore report its arrival with OnCollisionEnter and then fall silent. Code that needs to know about continuing contact is usually better served by recording enter and exit than by counting stay calls.

Intangible meetings

A collider with isTrigger enabled gives up its solidity entirely. Bodies pass through it as if it were not there, and it neither pushes nor is pushed. What it keeps is detection. The engine still tracks which other colliders overlap it, and reports changes through a second family of messages: OnTriggerEnter when another collider first enters the volume, OnTriggerStay on each physics step while it remains inside, and OnTriggerExit when it leaves.

These messages carry much less information than their collision counterparts. Each receives a single argument, the Collider of the other object, and nothing about where or how the two met. That is fitting, since a trigger overlap has no meaningful point of contact or impact force; it is simply a question of inside or outside. The script can still look at the other collider's GameObject, its tag, its layer or its components to decide what has arrived and what should follow.

Trigger volumes are usually primitive shapes, a box covering a doorway or a sphere around an object, and they are often placed on their own GameObjects with no renderer, invisible to the player. In a game like Crown & Ashes, the warm circle of a torch at night could be marked with a sphere trigger, so that a script learns the moment someone steps into or out of its light. The shape need not match anything visible; it describes the reach of an idea rather than the edge of a thing.

A caution about exits belongs here. If a collider inside a trigger is disabled, or its GameObject deactivated or destroyed, Unity does not send OnTriggerExit for it. A zone that counts how many villagers stand inside it, adding on enter and subtracting on exit, will therefore drift upward each time someone inside is removed from the scene. Robust zone code either checks for destroyed entries in its own list or has objects notify the zone when they leave the world.

The Rigidbody requirement

Neither family of messages is sent for every pair of colliders. The physics engine considers static colliders, those without a Rigidbody, to be fixed scenery that never moves, and two pieces of scenery overlapping is not an event. For any message to occur, at least one of the two colliders must belong to a GameObject with a Rigidbody. This is the most common reason a newly written OnTriggerEnter never fires: two plain colliders, with no Rigidbody between them, are invisible to each other.

The rule is a little stricter for collisions than for triggers. Trigger messages are sent as long as one of the pair has a Rigidbody, even a kinematic one, so a kinematic character walking into a static trigger box will raise OnTriggerEnter. Collision messages, by default, need at least one of the bodies to be dynamic, a non-kinematic Rigidbody that the simulation moves. Two kinematic bodies passing through each other, or a kinematic body meeting a static wall, produce no collision messages at all.

Projects that need those missing pairs can change the Contact Pairs Mode in the physics settings to include kinematic and static pairs, at some extra cost. Most do not, and instead give the moving object a kinematic Rigidbody and use triggers for detection. It is worth knowing, too, that collision messages are sent even to scripts that are disabled, which Unity documents as a deliberate choice: it allows a disabled behaviour to be switched on in response to the very contact it was waiting for.

What a collision reports

OnCollisionEnter and its siblings receive a Collision object, and that object is where the richness of solid meetings lies. It identifies the other party through its gameObject, collider and rigidbody properties. It reports relativeVelocity, the speed and direction at which the two bodies approached each other, and impulse, the total impulse the engine applied to separate them during that step. Together these say not only that something was hit, but how hard.

It also reports the contacts. The contactCount property gives the number of contact points, and GetContact returns each one as a ContactPoint, with a point in world space, a normal giving the direction of the surface at that point, and a separation value. A sword striking a shield might produce a single contact; a crate landing flat on a floor might produce four, one near each corner. The normal is the key to many effects, telling sparks which way to fly and footprints which way to face.

All of this information has a cost, and Unity offers a small courtesy for scripts that do not need it. If OnCollisionEnter is declared without any parameter, the engine can avoid assembling the contact data for that callback. Code that only needs to know that a collision happened, not where, can use this form. For the rest, retrieving contacts with GetContacts into a reused array avoids creating fresh garbage on every impact, which matters when many objects collide each step.

Zones, pickups and impacts

With both families understood, the everyday choices become straightforward. Anything the player should pass through while the game takes notice belongs to triggers: checkpoints, doorways that load the next area, regions that change the music, hazards such as fire or poison that harm whoever stands in them, and the radius within which an enemy notices an intruder. Each is a volume with a meaning, and the trigger messages tell the script exactly when that meaning begins and ends for a given visitor.

Pickups are the classic small case. A coin, an arrow bundle or a healing herb carries a trigger collider slightly larger than its model, and when the player's collider enters it, OnTriggerEnter checks that the visitor is indeed the player, grants the item and removes the pickup. Making the pickup solid would be a mistake; the player would bump against it and have to walk around a coin. The trigger lets it be collected in passing, which is what the player expects.

Impacts belong to collisions. A cart that should splinter when it strikes a wall hard enough, a vase that breaks when it falls, a soldier who takes damage from a thrown rock: each needs to know how forceful the meeting was, and that comes from relativeVelocity or impulse in the Collision object. Comparing the magnitude of relativeVelocity with a threshold, breaking only above a few metres per second, keeps a vase from shattering because someone nudged the table.

Some objects need both at once, and nothing forbids it. A door can have a solid collider that blocks passage and a separate trigger in front of it that notices someone approaching and swings it open. A creature can have a solid body for physical contact and a large trigger sphere for its senses. Separating the two concerns onto different colliders, often on child objects, keeps each simple and lets the physics engine treat each meeting in the way that fits it.

Timing and order

Both families of messages are delivered as part of the physics step rather than the rendered frame. After FixedUpdate runs and the simulation advances, the engine works out which pairs began, continued or ended contact and calls the corresponding methods. That means a trigger message may arrive several times between two frames, or not at all on a given frame, and a script that sets a flag in OnTriggerEnter and reads it in Update must allow for this difference in rhythm.

Fast objects introduce a further subtlety. With discrete collision detection, a small body moving quickly can pass entirely through a thin collider between one step and the next, and if it never overlaps on any step, neither a collision nor a trigger message is ever sent. Continuous collision detection modes on the Rigidbody help with solid collisions, though they do not apply to triggers in the same way, so a bullet that must register passing through a thin trigger is often better handled with a raycast along its path.

There is something almost moral in the distinction, if one is permitted the indulgence. A collision insists: it demands that two things not occupy the same space, and the engine labours on every step to honour that demand. A trigger merely witnesses, letting the visitor pass while recording that it came and went. Most of the trouble developers have with these messages fades once they ask, of every meeting they design, which of the two it should be, whether the world ought to resist the visitor or only remember it.