Collisions and Triggers help make a world more dynamic. You can use them to make things feel more physical, or to cause specific code to run when players or objects collide or overlap with others.
Be sure to check out Collision Basics and Entity Lifecycle for some more in-depth information on these concepts!
Contents
Scripts in an Entity that is colliding with another or interacting with a trigger will have specific events called depending on the state of that interaction.
These events are:
onCollisionEnter()
onCollisionStay()
onCollisionExit()
onTriggerEnter()
onTriggerStay()
onTriggerExit()
Here is an example of each event. These will be called on both Entities involved in the interaction.
onCollisionEnter(otherEntity: Entity, contact: CollisionContact) {
console.log(`${otherEntity.name} started colliding with me`);
}
onCollisionStay(otherEntity: Entity, contact: CollisionContact) {
console.log(`${otherEntity.name} is still colliding with me`);
}
onCollisionExit(otherEntity: Entity) {
console.log(`${otherEntity.name} stopped colliding with me`);
}
onCollisionCorrection(correctionVector: Vector3) {
console.log(`The collision system moved me away from another collider`);
}
onTriggerEnter(otherEntity: Entity) {
console.log(`${otherEntity.name} started overlapping me`);
}
onTriggerStay(otherEntity: Entity) {
console.log(`${otherEntity.name} is still overlapping me`);
}
onTriggerExit(otherEntity: Entity) {
console.log(`${otherEntity.name} stopped overlapping with me`);
}
The onCollisionEnter()
and onCollisionStay()
events come with some additional information about the collision in the form of CollisionContact
object.
This object holds information about the collision like the point of contact, normal, distance and the correction vector.
Note: At the moment the normal is not correct and the correction vector should be normalized and used instead.
Here are a couple of examples showing how you can use colliders and triggers to make your game more interactive.
This is an example of some simple ball physics. The ball will be affected by gravity and have some damping applied when it is on the ground.
To set this up you need to add this code to an Entity along with a static sphere collider. Place other collidable entities around for it to bounce off.
@numberRange(0, 1)
damping = 0.2;
gravity = -98;
private _velocity = new Vector3();
private _frameGravity = new Vector3();
private _frameVelocity = new Vector3();
start() {
// Start off with some force.
this.addForce(50, this.entity.worldTransform.getForward());
}
addForce(force: number, direction: Vector3) {
// Add force in the given direction to the current velocity.
this._velocity.add(direction.clone().normalize().multiplyScalar(force));
}
tick() {
// Apply Gravity to the curent velocity.
this._frameGravity.copy(UP).multiplyScalar(this.gravity * this.game.frameDeltaTime);
this._velocity.add(this._frameGravity);
// Add the current velocity to the position.
this._frameVelocity.copy(this._velocity).multiplyScalar(this.game.frameDeltaTime);
this.entity.worldTransform.position.add(this._frameVelocity);
}
onCollisionEnter(other: Entity, contact: CollisionContact) {
// Bounce off surface by reflecting the current velocity across the normal of the collision.
// Apply some damping to slow the ball down.
this._velocity.reflect(contact.correctionVector.normalize()).multiplyScalar(1 - this.damping);
}
Let's make a Voxel Object change to a new frame based on if something is in it's trigger area or not.
You need to have an Entity with this script, a Voxel Object with at least two frames and a Collider setup to be a trigger. Walking, or moving a collidable object, into the trigger volume will activate the change. Clearing the zone will revert the change.
// The Voxel Object frame to show when nothing is overlapping with this Entity.
inactiveFrame = 0;
// The Voxel Object frame to show when something is overlapping with this Entity.
activeFrame = 1;
private _voxelObject : VoxelObjectComponent | null = null;
start() {
// Store a reference to the Voxel Object Component so we can refer to it later.
this._voxelObject = this.entity.getComponentByType(ComponentType.VoxelObject);
// Only attempt to make changes if there is a Voxel Object Component.
if (this._voxelObject) {
// Stop playback and show the inactive frame.
this._voxelObject.playbackRate = 0;
this._voxelObject.showVoxelFrameAtKey(this.inactiveFrame);
}
}
onTriggerEnter(other : Entity) {
// Only attempt to make changes if there is a Voxel Object Component.
if (this._voxelObject) {
// Show the active frame.
this._voxelObject.showVoxelFrameAtKey(this.activeFrame);
}
}
onTriggerExit(other : Entity) {
// Only attempt to make changes if there is a Voxel Object Component.
if (this._voxelObject) {
// Show the inactive frame.
this._voxelObject.showVoxelFrameAtKey(this.inactiveFrame);
}
}