-
Notifications
You must be signed in to change notification settings - Fork 10
trigger areas #290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
trigger areas #290
Changes from 3 commits
67b4708
54f0b58
f21bf81
bcadccb
ae779ab
2b30a5e
60eef72
58df642
6797b19
be3b6c5
e448293
cd30f09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| --- | ||
| layout: adr | ||
| adr: 258 | ||
| title: Trigger Areas | ||
| date: 2025-02-19 | ||
| status: Draft | ||
| type: RFC | ||
| spdx-license: CC0-1.0 | ||
| authors: | ||
| - nearnshaw | ||
| --- | ||
|
|
||
| ## Abstract | ||
|
|
||
| This document describes an approach for making it possible for creators to use native trigger areas in their scenes. | ||
| Our SDK never included this essential feature, so creators have relied heavily using the Utils library. Triggers in this library are implemented with an approach that has bad performance and is not so easy on the creator, and also has its limits. | ||
|
|
||
| This new approach results in better performance and a better developer experience. It also allows for more freedom, as the trigger areas can now be any shape, not just a box. | ||
|
|
||
| ## Trigger areas | ||
|
|
||
| Trigger areas are a region in the scene that trigger an action whenever something overlaps with them (usually the player, but not necessarily). We can also trigger actions continuously while something keeps overlapping (on each frame), or when it stops overlapping. | ||
|
|
||
| The shape of a trigger area is given by a collider. It can use a simple primitive shape, or it can even take any arbitrary shape from a 3D model. | ||
|
|
||
| We will create a new component for this, called `TriggerArea`. It will have the following fields: | ||
|
|
||
| - `mesh`: An object similar to the one used for [MeshCollider](https://github.com/decentraland/protocol/blob/main/proto/decentraland/sdk/components/mesh_collider.proto#L33), that allows to define the shape of the trigger area. | ||
| - `triggerLayer`: The collision layer that triggers the trigger | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| ### Collision layers | ||
|
|
||
| Trigger areas can only be triggered by entities on certain _collision layers_. Most of the time you want to check for just the position of the player, so we should have a collision layer dedicated to this, and it should be the default. We should create a new enum for this, called `TriggerLayer`. | ||
|
|
||
| This enum should have the following values: | ||
|
|
||
| - `TL_PLAYER`: The default layer for the player | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
| - `TL_CUSTOM1`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM2`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM3`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM4`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM5`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM6`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM7`: A custom layer for any other object that can move around the scene | ||
| - `TL_CUSTOM8`: A custom layer for any other object that can move around the scene | ||
|
|
||
| A single trigger area can have multiple trigger layers at once, similarly to how collision layers work on colliders. | ||
|
|
||
| ### Trigger events | ||
|
|
||
| Trigger areas can trigger events when the player (or any other entity on the trigger layer) enters, exits or stays in the area. | ||
|
|
||
| Trigger events would have to be shared from the engine to the SDK via a component, following a similar approach as we do with pointer events and raycasts. We should create a `TriggerCollisionResult` component. Creators are not expected to read values or make use of this component in any way, unless they really want to fine tune their scene’s behavior. | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
|
|
||
| This component will be added by the engine, similarly to how pointer events are handled. The event will be triggered when the entity enters, exits or stays in the area. Each event will have the following fields: | ||
|
|
||
| - `triggeredEntity`: The entity that was triggered (this is the entity that owns the trigger area) | ||
| - `state`: The state of the trigger event (ENTER, EXIT, STAY) | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
| - `timestamp`: The timestamp of the trigger event | ||
| - `trigger`: An object with the following fields: | ||
| - `entity`: The entity that triggered the trigger | ||
| - `layer`: The collision layer of the entity that triggered the trigger (this is the same as the trigger layer of the trigger area) | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
| - `position`: The position of the entity that triggered the trigger | ||
| - `rotation`: The rotation of the entity that triggered the trigger | ||
| - `scale`: The scale of the entity that triggered the trigger | ||
|
Comment on lines
+88
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i suggested using a transform directly here, but i'm not sure we can actually do that since we don't have a PbTransform message type...
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same would apply to the fields |
||
|
|
||
|
|
||
|
|
||
| ### Code helpers | ||
|
|
||
| Even though at a low-level data travels in an ECS way, we should offer a friendlier way to deal with this for creators. This is the same approach we already use with pointer events and raycasts. | ||
|
|
||
| We’ll create a system and helper functions for reacting to a trigger events from a trigger area, similar to our [pointerEvents](https://docs.decentraland.org/creator/development-guide/sdk7/click-events/) system | ||
|
|
||
| - `onTriggerEnter` | ||
| - `onTriggerExit` | ||
| - `onTriggerStay` | ||
|
|
||
| It will look something like this: | ||
|
|
||
| ```ts | ||
| triggerEventsSystem.OnTriggerEnter( | ||
| { | ||
| entity: myTrigger, | ||
| opts: { | ||
| layer: Player | ||
| } | ||
| }, | ||
| function (otherEntity) { | ||
| // Do whatever I want | ||
| } | ||
| ) | ||
| ``` | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Triggers embedded in 3D models | ||
|
|
||
|
|
||
| It should also be possible to create trigger areas embedded into 3D models. If a mesh in a 3D model has a name that ends with `_trigger`, it will be considered a trigger area. | ||
|
nearnshaw marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
|
|
||
| ## Serialization | ||
|
|
||
| ```yaml | ||
|
|
||
| ``` | ||
|
|
||
| ```protobuf | ||
|
|
||
| ``` | ||
|
|
||
| ## Semantics | ||
|
|
||
| ### Example | ||
|
|
||
| Low level: | ||
|
|
||
| ```ts | ||
| function TriggerReadingSystem() { | ||
| const triggeredEntities = engine.getEntitiesWith(TriggerCollisionResult) | ||
| for (const [entity] of triggeredEntities) { | ||
|
|
||
| const result = TriggerCollisionResult.getOrNull(entity) | ||
| if(result){ | ||
| console.log("TRIGGER EVENT DATA:", result.commands) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| engine.addSystem(TriggerReadingSystem) | ||
| ``` | ||
|
|
||
| High level: | ||
|
|
||
| _Option 1: Using the TriggerArea component_ | ||
| ```ts | ||
| const myTrigger = engine.addEntity() | ||
|
|
||
| MeshCollider.setBox(myTrigger, {collisionMask: ColliderLayer.CL_PHYSICS}) | ||
|
|
||
| TriggerArea.setBox(myTrigger, {layer:TriggerLayer.TL_PLAYER }) | ||
|
|
||
| Tramsform.create(myTrigger) | ||
| ``` | ||
|
|
||
| _Option 2: Using the triggerEventsSystem_ | ||
| ```ts | ||
| const myTrigger = engine.addEntity() | ||
|
|
||
| MeshCollider.setBox(myTrigger, {collisionMask: ColliderLayer.CL_PHYSICS}) | ||
|
|
||
| Tramsform.create(myTrigger) | ||
| triggerEventsSystem.OnTriggerEnter( | ||
| { | ||
| entity: myTrigger, | ||
| opts: { | ||
| layer: Player | ||
| } | ||
| }, | ||
| function (otherEntity) { | ||
| // Do whatever I want | ||
| } | ||
| ) | ||
| ``` | ||
Uh oh!
There was an error while loading. Please reload this page.