Collisions & Triggers
Collision layers
Section titled “Collision layers”Every body has a Collision Profile with a Layer and a Mask.
- Layer is the category this body belongs to.
- Mask is the categories this body collides against.
Two bodies collide if either one’s mask includes the other’s layer. The built-in layers are Static, Dynamic, and Channel 0 through Channel 13 for your own gameplay categories. For example, put bullets on a channel and give them a mask of only the layers they should hit, so they pass through everything else.
By default a body’s Layer is Dynamic and its Mask is Static + Dynamic, so ordinary bodies collide with the world and each other.
Solid colliders vs triggers
Section titled “Solid colliders vs triggers”By default a collider is solid. Bodies bounce off it, and you receive contact events.
Turn on a collider’s Is Trigger flag and it stops being solid. Bodies pass through it, but you receive overlap events. Use triggers for pickups, checkpoints, and damage volumes. The rigid body’s Is Sensor flag does the same thing at the whole-body level.
Reacting to a collision
Section titled “Reacting to a collision”A rigid body publishes its contacts and overlaps as delegates on
SRigidBodyComponent. Contacts are solid collisions. Overlaps are
triggers, which report but produce no physical response.
The delegates are the same objects in both languages, only the way you attach differs: C++ adds a lambda, C# binds a method.
SRigidBodyComponent& Body = Context.Get<SRigidBodyComponent>(Entity);
Body.OnContactBegin.AddLambda([](const SCollisionEvent& Event){ LOG_INFO("hit {} at {} m/s", (uint32)Event.Other, Event.ImpactSpeed);});
Body.OnOverlapBegin.AddLambda([](const SCollisionEvent& Event){ LOG_INFO("entered trigger of {}", (uint32)Event.Other);});AddLambda returns an FDelegateHandle. Nothing unbinds it for you, so keep the
handle and remove the binding when the owner goes away.
public sealed class Mine : EntityScript{ public override void OnContactBegin(SCollisionEvent Event) { Debug.Log($"hit {Event.Other} at {Event.ImpactSpeed} m/s"); }
public override void OnOverlapBegin(SCollisionEvent Event) { Debug.Log($"entered trigger of {Event.Other}"); }}For this entity’s body, override the script hook: the engine delivers the event to every script on the entity, with nothing to subscribe or unsubscribe.
To listen to a different entity’s body, bind that component’s delegate. Do
not cache the component in a field, resolve it where you bind and keep only the
DelegateBinding the call returns.
private DelegateBinding _Hit;
public override void OnReady(){ SRigidBodyComponent? Body = Registry.TryGet<SRigidBodyComponent>(Target); if (Body is not null) { _Hit = Body.OnContactBegin.Bind(OnHit); }}
public override void OnDetach() => _Hit.Unbind();The full set is OnContactBegin / OnContactEnd and OnOverlapBegin /
OnOverlapEnd, each carrying an SCollisionEvent, plus the payload-free
OnWake / OnSleep.
The collision event
Section titled “The collision event”SCollisionEvent is oriented from your entity’s point of view, in both
languages.
| Field | Meaning |
|---|---|
Entity | Your entity |
Other | The other entity |
Point | World-space contact point (FVector3) |
Normal | Contact normal, pointing away from you (FVector3) |
Velocity / OtherVelocity | Linear velocities at contact (m/s) |
RelativeVelocity | Other minus self (FVector3) |
ImpactSpeed | Speed along the normal (m/s) |
bIsTrigger | true if the other side was a trigger or sensor |
BodyID / OtherBodyID | The Jolt body ids |
- Queries, asking what is there instead of waiting to be told.
- Materials & Destruction, surface response and breakable objects.