OnTriggerExit: TRIGGER STAY 2D: Sent while the game object stays inside a 2D Trigger. OnTriggerStay: PARTICLE COLLISION: Sent when a particle hits a Collider. See unity docs for more info. OnParticleCollision: JOINT BREAK: Sent when a Joint attached to the same game object breaks. OnJointBreak: JOINT BREAK 2D. Unity Code Snippets Improved# Create Unity classes and methods easily. This is a fork of Kleber Silva's repository which removes the verbosity of using private. It doesn't fit my code style and therefore I decided to remove it.
Unity provides the OnTriggerEnter() and OnTriggerExit() events that fire whenever a collider enters or exits the designated trigger, and also an OnTriggerStay() that fires in every frame in which the collider is within the trigger. However, it doesn’t provide a one-off “What objects are in this trigger” function, which is what I needed earlier in order to create a “lock-on” targetting system (Note that you can emulate this functionality somewhat with a Physics.Raycast / Physics.SphereCast, but you’d have to call it every frame to keep the list updated)
Similarly to the colliders, OnTriggerStay method will be called each frame as long as an object will be inside a trigger. And at the end, when an object will leave the trigger, Unity will invoke OnTriggerExit method. Fun fact is that that these functions will be invoked on both objects – Trigger and object which entered the Trigger. 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.
I could have sworn that OnTriggerExit() was buggy when my code wasn’t behaving the way I thought it should have, but it turns out my logic was faulty. For my own reference, here’s a description of the problem and the solution:
The “lock-on” target system is as follows: if the player presses a button while an enemy is within the target trigger zone, they lock-on to that enemy (by setting their own transform.parent to the transform of the enemy). Seemed simple enough, and my first thought was simply to keep track of the object currently in the target trigger like this:
The problem with this is that I have many enemies in my scene. So it’s perfectly possible after the player has already locked onto one enemy for another enemy to enter the trigger. The code above would immediately change the target to whatever the latest enemy to enter the trigger was (i.e. the one that most recently caused OnTriggerEnter to fire), causing it to jump around which wasn’t what I wanted. This is a pretty simple fix by adding an extra enemyInTarget null condition to the OnTriggerEnter function to make sure we haven’t already got a target:
But there’s still a problem. Although when a second enemy enters the trigger it will no longer cause the target to lock on to it, when it exits it will still clear the target. So enemies simply “passing through” the trigger caused the player to lose their target focus. Another fix – this time to the OnTriggerExit:
Ontriggerexit2d
Getting better, but still not right. This broke in the following scenario (although this is the bug in my logic that took me a while to realise…)
- Scene start. enemyInTarget = null; transform.parent = null;
- EnemyA enters the trigger. enemyInTarget = EnemyA; transform.parent = null;
- Player presses lock on button. enemyInTarget = EnemyA; transform.parent = EnemyA;
- EnemyB also enters the trigger. Code fixes above mean still enemyInTarget = EnemyA; transform.parent = EnemyA;
- Player stops locking onto EnemyA. enemyInTarget = EnemyA; transform.parent = null;
- EnemyA leaves the trigger. enemyInTarget = null; transform.parent = null;
At the end of this series of events, the game state is identical to that at the start, except that EnemyB is already in the trigger, and pressing the “LockOn” button will have no effect because there is no record of that fact (since, at the point they entered the trigger, we were locking onto something else).
Ontriggerexit2d
Sigh. I did consider changing the code logic completely at this point to ditch triggers and instead use a RayCast hit test to find the best target at the point the player presses the lock-on button. However, I also want to be able to highlight enemies in the target, and to do this using the RayCast method would require an expensive RayCast every frame (The PhysX Trigger calls are pretty optimised in contrast).
And then the strikingly simple solution hit me – I simply needed to maintain a local array of targets as they enter or leave the trigger and, at the point the player attempts to lock-on, choose the item at the top of the list (which, in the case of multiple enemies, is the one that has been there longest). Note that I’ve also changed the LockOn button to toggle between locked/unlocked to a parent target.
This solution is somewhat esoteric, but I wanted to document it for my own personal reference; if you want a more general reusable class to keep track of gameobjects in a Unity trigger, I highly recommend you check out http://technology.blurst.com/unity-physics-trigger-collider-examples/