Skip to content

Commit 7a73e4c

Browse files
committed
feat: add remote avatar detection and scene-aware trigger areas
- Add TriggerDetector collision to avatars for trigger area detection - Track avatar entity IDs via metadata (dcl_entity_id, dcl_scene_id) - Implement scene-awareness: only fire events for entities in active scene - Separate physical state (entities_inside) from logical state (entities_entered) - Generate synthetic ENTER/EXIT events when entities change scenes while physically inside trigger areas - Query avatar current scene via metadata to handle remote avatar scene changes
1 parent 5a533f5 commit 7a73e4c

8 files changed

Lines changed: 461 additions & 58 deletions

File tree

godot/project.godot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ pointing/emulate_touch_from_mouse=true
185185
3d_render/layer_20="OUTLINE_SYSTEM_DEPTH"
186186
3d_physics/layer_1="CL_POINTER"
187187
3d_physics/layer_2="CL_PHYSICS"
188-
3d_physics/layer_3="CL_RESERVED1"
188+
3d_physics/layer_3="CL_PLAYER"
189189
3d_physics/layer_4="CL_RESERVED2"
190190
3d_physics/layer_5="CL_RESERVED3"
191191
3d_physics/layer_6="CL_RESERVED4"

godot/src/decentraland_components/avatar/avatar.gd

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ const WEARABLE_NAME_PREFIX = "__"
1313
@export var hide_name: bool = false
1414
@export var non_3d_audio: bool = false
1515

16+
# Entity info for trigger area detection (set by avatar_shape.rs for scene avatars)
17+
var dcl_scene_id: int = -1
18+
var dcl_entity_id: int = -1
19+
var is_local_player: bool = false
20+
1621
# Public
1722
var avatar_id: String = ""
1823
var hidden: bool = false
@@ -53,6 +58,7 @@ var wearable_promises = null
5358

5459
@onready var avatar_modifier_area_detector = $avatar_modifier_area_detector
5560
@onready var click_area = $ClickArea
61+
@onready var trigger_detector = %TriggerDetector
5662

5763

5864
func _ready():
@@ -90,6 +96,41 @@ func _ready():
9096
click_area.set_meta("is_avatar", true)
9197
click_area.set_meta("avatar_id", avatar_id)
9298

99+
# Connect to scene change signal for trigger area tracking
100+
# This signal is emitted by DclAvatar (Rust side) when the avatar changes parcels
101+
change_scene_id.connect(self._on_avatar_scene_changed)
102+
103+
# Trigger detection is setup later via setup_trigger_detection() when entity info is available
104+
105+
106+
## Setup trigger detection for this avatar.
107+
## Call this after the avatar is created with the appropriate entity info.
108+
## - For local player: scene_id=-1, entity_id=SceneEntityId.PLAYER (0x10000)
109+
## - For remote avatars: scene_id=-1, entity_id=assigned entity from avatar_scene.rs
110+
## - For scene avatars (NPCs): scene_id and entity_id from the scene
111+
func setup_trigger_detection(p_scene_id: int, p_entity_id: int) -> void:
112+
dcl_scene_id = p_scene_id
113+
dcl_entity_id = p_entity_id
114+
115+
# Set metadata on TriggerDetector so trigger_area.rs can identify this avatar
116+
trigger_detector.set_meta("dcl_scene_id", dcl_scene_id)
117+
trigger_detector.set_meta("dcl_entity_id", dcl_entity_id)
118+
119+
# Initialize current scene metadata (will be updated via _on_avatar_scene_changed)
120+
# For scene NPCs, this stays at dcl_scene_id; for remote avatars, it will be updated
121+
trigger_detector.set_meta("dcl_avatar_current_scene_id", dcl_scene_id)
122+
123+
# Enable the collision shape
124+
trigger_detector.get_node("CollisionShape3D").disabled = false
125+
126+
127+
## Called when the avatar changes parcel scenes (signal from DclAvatar/Rust)
128+
func _on_avatar_scene_changed(new_scene_id: int, _prev_scene_id: int) -> void:
129+
# Update the current scene metadata on TriggerDetector
130+
# This allows trigger_area.rs to know which scene this avatar is currently in
131+
if trigger_detector:
132+
trigger_detector.set_meta("dcl_avatar_current_scene_id", new_scene_id)
133+
93134

94135
func on_chat_message(address: String, message: String, _timestamp: float):
95136
if avatar_id != address:

godot/src/decentraland_components/avatar/avatar.tscn

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,10 @@ height = 0.1
360360

361361
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_mwwx3"]
362362

363+
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_trigger"]
364+
radius = 0.25
365+
height = 1.5
366+
363367
[node name="Avatar" type="DclAvatar"]
364368
script = ExtResource("1_mwwx3")
365369

@@ -899,6 +903,16 @@ bus = &"Scene"
899903

900904
[node name="Avatar_SFXs" parent="." instance=ExtResource("7_xt6ge")]
901905

906+
[node name="TriggerDetector" type="StaticBody3D" parent="."]
907+
unique_name_in_owner = true
908+
collision_layer = 4
909+
collision_mask = 0
910+
911+
[node name="CollisionShape3D" type="CollisionShape3D" parent="TriggerDetector"]
912+
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.2, 0)
913+
shape = SubResource("CapsuleShape3D_trigger")
914+
disabled = true
915+
902916
[node name="ClickArea" type="Area3D" parent="."]
903917
unique_name_in_owner = true
904918
collision_layer = 536870912

godot/src/logic/player/player.gd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ func _ready():
135135
if own_click_area:
136136
own_click_area.queue_free()
137137

138+
# Setup trigger detection for local player's avatar
139+
# scene_id=-1 (not a scene NPC), entity_id=1 (SceneEntityId::PLAYER)
140+
avatar.setup_trigger_detection(-1, 1)
141+
138142

139143
func _on_player_profile_changed(new_profile: DclUserProfile):
140144
var new_version = new_profile.get_profile_version()

lib/src/avatars/avatar_scene.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,16 @@ impl AvatarScene {
208208
new_avatar.connect("emote_triggered".into(), emote_triggered_callable);
209209

210210
self.base_mut().add_child(new_avatar.clone().upcast());
211+
212+
// Setup trigger detection with the assigned entity_id
213+
// scene_id=-1 means this is a remote avatar (not a scene NPC)
214+
// NOTE: This must be called AFTER add_child so that _ready() has been called
215+
// and the @onready trigger_detector variable is initialized
216+
new_avatar.call(
217+
"setup_trigger_detection".into(),
218+
&[(-1_i32).to_variant(), entity_id.as_i32().to_variant()],
219+
);
220+
211221
self.avatar_godot_scene
212222
.insert(entity_id, new_avatar.clone());
213223

lib/src/scene_runner/components/avatar_shape.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ pub fn update_avatar_shape(scene: &mut Scene, crdt_state: &mut SceneCrdtState) {
126126
new_avatar_shape.set_name(GString::from("AvatarShape"));
127127
node_3d.add_child(new_avatar_shape.clone().upcast());
128128

129+
// Setup trigger detection for scene avatar (NPC)
130+
// scene_id from the scene, entity_id from the entity
131+
new_avatar_shape.call(
132+
"setup_trigger_detection".into(),
133+
&[scene.scene_id.0.to_variant(), entity.as_i32().to_variant()],
134+
);
135+
129136
new_avatar_shape.call_deferred(
130137
"async_update_avatar".into(),
131138
&[new_avatar_data.to_variant(), avatar_name.to_variant()],

0 commit comments

Comments
 (0)