From c240efa56fbe38ba2e3dfc81af59c02de32038ed Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sun, 5 Nov 2023 13:59:33 +0100 Subject: [PATCH] update interaction system --- README.md | 31 +++++------ main.tscn | 7 ++- src/entities/switch.gd | 3 + src/home_adapters/hass_ws/hass.gd | 10 ++-- src/raycast.gd | 91 +++++++++++++++++++++++++++---- 5 files changed, 110 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index d508edf..84d2e27 100644 --- a/README.md +++ b/README.md @@ -65,30 +65,29 @@ func watch_state(entity: String, callback: Callable[entity: Entity]) -> Callable ### Interaction Events -Each time a button is pressed on the primary controller, a raycast is done to be able to interact with devices or the UI. +Each time a button is pressed on the primary controller, a ray-cast is done to be able to interact with devices or the UI. -**InteractionEvent** -```js -{ - position: Vector3, - rotation: Vector3 +```python +InteractionEvent { + "controller": XRController3D, # The controller that triggered the event + "ray": RayCast3D, # The ray-cast that triggered the event } ``` | Function called | Args | Description | | -- | -- | -- | -| `_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released | -| `_dbl_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released twice in a row | -| `_long_click` | `[event: InteractionEvent]` | The back trigger button has been pressed, then hold still for a short period, then released | -| `_press_down` | `[event: InteractionEvent]` | The back trigger button has been pressed down | -| `_press_move` | `[event: InteractionEvent]` | The back trigger button has been moved while pressed down | -| `_press_up` | `[event: InteractionEvent]` | The back trigger button has been released | -| `_grab_down` | `[event: InteractionEvent]` | The side grap button been pressed down | -| `_grab_move` | `[event: InteractionEvent]` | The side grap button been pressed down | -| `_grab_up` | `[event: InteractionEvent]` | The side grap button been released | +| `_on_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released | +| `_on_press_down` | `[event: InteractionEvent]` | The back trigger button has been pressed down | +| `_on_press_move` | `[event: InteractionEvent]` | The back trigger button has been moved while pressed down | +| `_on_press_up` | `[event: InteractionEvent]` | The back trigger button has been released | +| `_on_grab_down` | `[event: InteractionEvent]` | The side grab button been pressed down | +| `_on_grab_move` | `[event: InteractionEvent]` | The side grab button been pressed down | +| `_on_grab_up` | `[event: InteractionEvent]` | The side grab button been released | +| `_on_ray_enter` | `[event: InteractionEvent]` | The ray-cast enters the the collision body | +| `_on_ray_leave` | `[event: InteractionEvent]` | The ray-cast leaves the the collision body | ### Testing without a VR Headset In order to test without a headset, press the run project (F5) button in Godot and ignore the prompt that OpenXR failed to start. To simulate the headset and controller movement, we're using the [XR Input Simulator](https://godotengine.org/asset-library/asset/1775) asset. -Click at the link to get a list of the supported controlls. +Click at the link to get a list of the supported controls. diff --git a/main.tscn b/main.tscn index c3d7c33..e90b1f1 100644 --- a/main.tscn +++ b/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=12 format=3 uid="uid://eecv28y6jxk4"] +[gd_scene load_steps=13 format=3 uid="uid://eecv28y6jxk4"] [ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] [ext_resource type="Script" path="res://src/raycast.gd" id="1_tsqxc"] @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://c3kdssrmv84kv" path="res://scenes/menu.tscn" id="3_1tbp3"] [ext_resource type="PackedScene" uid="uid://ctltchlf2j2r4" path="res://addons/xr-simulator/XRSimulator.tscn" id="5_3qc8g"] [ext_resource type="Material" uid="uid://bf5ina366dwm6" path="res://assets/materials/sky.material" id="5_wgwf8"] +[ext_resource type="PackedScene" uid="uid://cscl5k7lhopj5" path="res://scenes/entities/switch.tscn" id="8_uxmrb"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true @@ -76,3 +77,7 @@ shadow_enabled = true [node name="XRSimulator" parent="." instance=ExtResource("5_3qc8g")] xr_origin = NodePath("../XROrigin3D") + +[node name="Switch" parent="." instance=ExtResource("8_uxmrb")] +transform = Transform3D(0.999999, -1.39635e-11, 0, 9.48031e-12, 1, 0, 0, 0, 1, 0.564168, 0.725642, -1.56163) +entity_id = "switch.plug_printer_2_fale" diff --git a/src/entities/switch.gd b/src/entities/switch.gd index b9367f7..9f9e3f9 100644 --- a/src/entities/switch.gd +++ b/src/entities/switch.gd @@ -6,6 +6,9 @@ extends StaticBody3D # Called when the node enters the scene tree for the first time. func _ready(): var stateInfo = await HomeAdapters.adapter_ws.get_state(entity_id) + if stateInfo == null: + return + if stateInfo["state"] == "on": sprite.set_frame(0) else: diff --git a/src/home_adapters/hass_ws/hass.gd b/src/home_adapters/hass_ws/hass.gd index d7323ad..e742fe9 100644 --- a/src/home_adapters/hass_ws/hass.gd +++ b/src/home_adapters/hass_ws/hass.gd @@ -10,6 +10,7 @@ var token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0M var LOG_MESSAGES := false var authenticated := false +var loading := true var id := 1 var entities: Dictionary = {} @@ -100,6 +101,7 @@ func start_subscriptions(): "attributes": packet.event.a[entity]["a"] } entitiy_callbacks.call_key(entity, [entities[entity]]) + loading = false on_connect.emit() if packet.event.has("c"): @@ -171,13 +173,13 @@ func encode_packet(packet: Dictionary): return JSON.stringify(packet) func load_devices(): - if !authenticated: + if loading: await on_connect return entities func get_state(entity: String): - if !authenticated: + if loading: await on_connect if entities.has(entity): @@ -186,14 +188,14 @@ func get_state(entity: String): func watch_state(entity: String, callback: Callable): - if !authenticated: + if loading: await on_connect entitiy_callbacks.add(entity, callback) func set_state(entity: String, state: String, attributes: Dictionary = {}): - assert(authenticated, "Not authenticated") + assert(!loading, "Still loading") var domain = entity.split(".")[0] var service: String diff --git a/src/raycast.gd b/src/raycast.gd index badbf27..48a255b 100644 --- a/src/raycast.gd +++ b/src/raycast.gd @@ -5,23 +5,92 @@ extends Node3D # Called when the node enters the scene tree for the first time. func _ready(): - _controller.button_pressed.connect(self._on_button_pressed) + _controller.button_pressed.connect(_on_button_pressed) + _controller.button_released.connect(_on_button_released) -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass +var _last_collided: Object = null +var _is_pressed := false +var _is_grabbed := false +var _moved := false +var _click_point := Vector3.ZERO + +func _physics_process(delta): + _handle_enter_leave() + _handle_move() + +func _get_event_data(): + return { + "controller": _controller, + "ray": ray, + } + +func _handle_move(): + var distance = ray.get_collision_point().distance_to(_click_point) + + if distance > 0.01: + if _is_pressed: + _call_fn("_on_press_move") + _moved = true + if _is_grabbed: + _call_fn("_on_grab_move") + _moved = true + +func _handle_enter_leave(): + var collider = ray.get_collider() + + if collider == _last_collided: + return + + if _last_collided != null && _last_collided.has_method("_on_ray_enter"): + _last_collided._on_ray_enter(_get_event_data()) + + if collider != null && collider.has_method("_on_ray_leave"): + collider._on_ray_leave(_get_event_data()) + + _last_collided = collider func _on_button_pressed(button): - print(button) - if button != "trigger_click": - return - var collider = ray.get_collider() if collider == null: return - print(collider) + match button: + "trigger_click": + _is_pressed = true + _click_point = ray.get_collision_point() + _call_fn("_on_press_down") + "grip_click": + _is_grabbed = true + _click_point = ray.get_collision_point() + _call_fn("_on_grab_down") - if collider.has_method("_on_toggle"): - collider._on_toggle() +func _on_button_released(button): + var collider = ray.get_collider() + + if collider == null: + return + + match button: + "trigger_click": + if _is_pressed: + if _moved == false: + _call_fn("_on_click") + _call_fn("_on_press_up") + _is_pressed = false + _moved = false + "grip_click": + if _is_grabbed: + _call_fn("_on_grab_up") + _is_grabbed = false + _moved = false + +func _call_fn(fn_name: String): + print("call_fn", fn_name) + var collider = ray.get_collider() + + if collider == null: + return + + if collider.has_method(fn_name): + collider.call(fn_name, _get_event_data())