init
This commit is contained in:
19
addons/godot-xr-tools/rumble/rumble_event.gd
Normal file
19
addons/godot-xr-tools/rumble/rumble_event.gd
Normal file
@@ -0,0 +1,19 @@
|
||||
@icon("res://addons/godot-xr-tools/editor/icons/rumble.svg")
|
||||
class_name XRToolsRumbleEvent
|
||||
extends Resource
|
||||
|
||||
## XR Tools Rumble Event Resource
|
||||
|
||||
## Strength of the rumbling
|
||||
@export_range(0, 1, 0.10) var magnitude: float = 0.5
|
||||
|
||||
## Whether the rumble can be active during a tree pause
|
||||
@export var active_during_pause: bool = false
|
||||
|
||||
@export_category("Timing")
|
||||
|
||||
## Whether the rumble continues until cleared
|
||||
@export var indefinite: bool = false
|
||||
|
||||
## Time to rumble (unless indefinite)
|
||||
@export_range(10, 4000, 10) var duration_ms: int = 300
|
||||
1
addons/godot-xr-tools/rumble/rumble_event.gd.uid
Normal file
1
addons/godot-xr-tools/rumble/rumble_event.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b15qba0ch13uw
|
||||
161
addons/godot-xr-tools/rumble/rumble_manager.gd
Normal file
161
addons/godot-xr-tools/rumble/rumble_manager.gd
Normal file
@@ -0,0 +1,161 @@
|
||||
extends Node
|
||||
|
||||
|
||||
## XR Tools Rumble (Controllers) Manager Script
|
||||
##
|
||||
## This script uses the controller's existing rumble intensity variable,
|
||||
## and allows you to rumble the controller for a certain amount
|
||||
## of time 'beats'.
|
||||
##
|
||||
## Example: something hits you while you're mowing the lawn,
|
||||
## i.e. a short intense rumble happens during long low rumble.
|
||||
|
||||
|
||||
## Name in the OpenXR Action Map for haptics
|
||||
const HAPTIC_ACTION := &"haptic" # TODO: Migrate
|
||||
|
||||
# Shorthand for all trackers, in use to be substituted with _queues.keys()
|
||||
const ALL_TRACKERS := [&"all"]
|
||||
|
||||
|
||||
# A Queue Per Haptic device (Dictionary<StringName, XRToolsRumbleManagerQueue>)
|
||||
var _queues: Dictionary = {}
|
||||
|
||||
|
||||
## Add support for is_xr_class
|
||||
func is_xr_class(xr_name: String) -> bool:
|
||||
return xr_name == "XRToolsRumbleManager"
|
||||
|
||||
|
||||
## Get the default Haptics Scale value
|
||||
func get_default_haptics_scale() -> float:
|
||||
var default = 1.0
|
||||
|
||||
# Check if the project has overridden the addon's default
|
||||
if ProjectSettings.has_setting("godot_xr_tools/input/haptics_scale"):
|
||||
default = ProjectSettings.get_setting("godot_xr_tools/input/haptics_scale")
|
||||
|
||||
if default < 0.0 or default > 1.0:
|
||||
# out of bounds? reset to default
|
||||
default = 1.0
|
||||
|
||||
return default
|
||||
|
||||
|
||||
## Used to convert gamepad magnitudes to equivalent XR haptic magnitude
|
||||
func combine_magnitudes(weak: float, strong: float) -> float:
|
||||
if strong >= 0.01:
|
||||
return 0.5 + clamp(strong / 2, 0.0, 0.5)
|
||||
return clamp(weak / 2, 0.0, 0.5)
|
||||
|
||||
|
||||
# On Ready
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
# Some rumble events are active during pause
|
||||
process_mode = PROCESS_MODE_ALWAYS
|
||||
|
||||
# Create a queues for standard controllers
|
||||
_queues[&"left_hand"] = XRToolsRumbleManagerQueue.new()
|
||||
_queues[&"right_hand"] = XRToolsRumbleManagerQueue.new()
|
||||
|
||||
|
||||
# Determine how much to - and perform the - rumbles each tick
|
||||
func _process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
# We'll be subtracting this from the event remaining ms
|
||||
var delta_ms = int(delta * 1000)
|
||||
|
||||
for tracker_name in _queues:
|
||||
var haptic_queue : XRToolsRumbleManagerQueue = _queues[tracker_name]
|
||||
|
||||
# default to noXRToolsRumbleManagerQueuensure it's a float, or it rounds to all or nothing!)
|
||||
var magnitude: float = 0.0
|
||||
|
||||
# Iterate over the events
|
||||
for key in haptic_queue.events.keys():
|
||||
var event : XRToolsRumbleEvent = haptic_queue.events[key]
|
||||
|
||||
# if we're paused and it's not supposed to be active, skip
|
||||
if get_tree().paused and not event.active_during_pause:
|
||||
continue
|
||||
|
||||
# If we've passed the threshold from positive to negative, the event is done
|
||||
if !event.indefinite and haptic_queue.time_remaining[key] < 0:
|
||||
clear(key, [tracker_name])
|
||||
continue
|
||||
|
||||
# Reduce the time remaining
|
||||
haptic_queue.time_remaining[key] -= delta_ms
|
||||
|
||||
# If it's of greater magnitude, update left magnitude to be set
|
||||
if event.magnitude > magnitude:
|
||||
magnitude = event.magnitude
|
||||
|
||||
# scale the final magnitude
|
||||
magnitude *= XRToolsUserSettings.haptics_scale
|
||||
|
||||
# Make that tracker rumble
|
||||
if magnitude > 0 and XRServer.primary_interface:
|
||||
XRServer.primary_interface.trigger_haptic_pulse(
|
||||
HAPTIC_ACTION,
|
||||
tracker_name, # if the tracker name isn't valid, it will error but continue
|
||||
0,
|
||||
magnitude,
|
||||
0.1,
|
||||
0)
|
||||
|
||||
|
||||
# Add an event
|
||||
func add(event_key: Variant, event: XRToolsRumbleEvent,
|
||||
trackers: Array = ALL_TRACKERS) -> void:
|
||||
if not event_key:
|
||||
push_error("Event key is invalid!")
|
||||
return
|
||||
|
||||
if not event:
|
||||
clear(event_key, trackers)
|
||||
return
|
||||
|
||||
# Substitube the shorthand for all trackers with the real thing
|
||||
if trackers == ALL_TRACKERS:
|
||||
trackers = _queues.keys()
|
||||
|
||||
for tracker in trackers:
|
||||
if tracker is XRNode3D:
|
||||
tracker = tracker.tracker
|
||||
|
||||
# Create queue first time a target is suggested
|
||||
if not _queues.has(tracker):
|
||||
_queues[tracker] = XRToolsRumbleManagerQueue.new()
|
||||
|
||||
# Add the event and it's remaining time to the respective queues
|
||||
_queues[tracker].events[event_key] = event
|
||||
_queues[tracker].time_remaining[event_key] = event.duration_ms
|
||||
|
||||
|
||||
# Remove an event
|
||||
func clear(event_key: Variant, trackers: Array = ALL_TRACKERS) -> void:
|
||||
if not event_key:
|
||||
push_error("Event key is invalid!")
|
||||
return
|
||||
|
||||
# Substitube the shorthand for all trackers with the real thing
|
||||
if trackers == ALL_TRACKERS:
|
||||
trackers = _queues.keys()
|
||||
|
||||
for tracker in trackers:
|
||||
if tracker is XRNode3D:
|
||||
tracker = tracker.tracker
|
||||
|
||||
# Ignore if the queue doesn't exist
|
||||
if not _queues.has(tracker):
|
||||
continue
|
||||
|
||||
# Remove the event and it's remaining time from the respective queues
|
||||
_queues[tracker].events.erase(event_key)
|
||||
_queues[tracker].time_remaining.erase(event_key)
|
||||
1
addons/godot-xr-tools/rumble/rumble_manager.gd.uid
Normal file
1
addons/godot-xr-tools/rumble/rumble_manager.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://by853dk86g1qw
|
||||
12
addons/godot-xr-tools/rumble/rumble_manager_queue.gd
Normal file
12
addons/godot-xr-tools/rumble/rumble_manager_queue.gd
Normal file
@@ -0,0 +1,12 @@
|
||||
class_name XRToolsRumbleManagerQueue
|
||||
extends Resource
|
||||
|
||||
# All currently-active events (Dictionary<Variant, XRToolsRumbleEvent>)
|
||||
var events: Dictionary
|
||||
|
||||
# All currently-active events' time remaining (Dictionary<Variant, int>)
|
||||
var time_remaining: Dictionary
|
||||
|
||||
func _init():
|
||||
events = {}
|
||||
time_remaining = {}
|
||||
1
addons/godot-xr-tools/rumble/rumble_manager_queue.gd.uid
Normal file
1
addons/godot-xr-tools/rumble/rumble_manager_queue.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ddpbyiiknangp
|
||||
76
addons/godot-xr-tools/rumble/rumbler.gd
Normal file
76
addons/godot-xr-tools/rumble/rumbler.gd
Normal file
@@ -0,0 +1,76 @@
|
||||
@tool
|
||||
@icon("res://addons/godot-xr-tools/editor/icons/rumble.svg")
|
||||
class_name XRToolsRumbler
|
||||
extends Node
|
||||
|
||||
## XR Tools Rumbler
|
||||
##
|
||||
## A node you attach to handle (contain and make easy to activate/cancel)
|
||||
## a particular rumble event.
|
||||
|
||||
## The details of this rumbler
|
||||
@export var event: XRToolsRumbleEvent : set = _set_event
|
||||
|
||||
@export var target: XRNode3D
|
||||
|
||||
## Activate the event
|
||||
func rumble() -> void:
|
||||
if is_instance_valid(target):
|
||||
XRToolsRumbleManager.add(self, event, [target.tracker])
|
||||
|
||||
|
||||
## Cancel the event
|
||||
func cancel() -> void:
|
||||
XRToolsRumbleManager.clear(self)
|
||||
|
||||
|
||||
## Rumble on the hand which owns the node
|
||||
func rumble_hand(hand_child: Node3D) -> void:
|
||||
var hand: XRNode3D = XRHelpers.get_xr_controller(hand_child)
|
||||
if is_instance_valid(hand):
|
||||
XRToolsRumbleManager.add(self, event, [hand.tracker])
|
||||
|
||||
|
||||
## Cancel rumble for the hand which owns the node
|
||||
func cancel_hand(hand_child: Node3D) -> void:
|
||||
var hand: XRNode3D = XRHelpers.get_xr_controller(hand_child)
|
||||
if is_instance_valid(hand):
|
||||
XRToolsRumbleManager.clear(self, [hand.tracker])
|
||||
|
||||
|
||||
## Activate the event, if provided the XR player body
|
||||
func rumble_if_player_body(body: Node3D) -> void:
|
||||
if is_instance_valid(body) and body is XRToolsPlayerBody:
|
||||
rumble()
|
||||
|
||||
|
||||
## Cancel rumble for the event, if provided the XR player body
|
||||
func cancel_if_player_body(body: Node3D) -> void:
|
||||
if is_instance_valid(body) and body is XRToolsPlayerBody:
|
||||
cancel()
|
||||
|
||||
|
||||
## Activate the event during an active pointer event
|
||||
func rumble_pointer(event : XRToolsPointerEvent) -> void:
|
||||
if event.event_type == XRToolsPointerEvent.Type.PRESSED:
|
||||
rumble_hand(event.pointer)
|
||||
elif event.event_type == XRToolsPointerEvent.Type.RELEASED:
|
||||
cancel_hand(event.pointer)
|
||||
|
||||
|
||||
func _set_event(p_event: XRToolsRumbleEvent) -> void:
|
||||
event = p_event
|
||||
update_configuration_warnings()
|
||||
|
||||
|
||||
# This method verifies the hand has a valid configuration.
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings := PackedStringArray()
|
||||
|
||||
# Check hand for animation player
|
||||
if not event:
|
||||
warnings.append("Rumbler must have a rumble event")
|
||||
|
||||
# Return warnings
|
||||
return warnings
|
||||
|
||||
1
addons/godot-xr-tools/rumble/rumbler.gd.uid
Normal file
1
addons/godot-xr-tools/rumble/rumbler.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dff408qq7s26e
|
||||
10
addons/godot-xr-tools/rumble/tap_rumble.tres
Normal file
10
addons/godot-xr-tools/rumble/tap_rumble.tres
Normal file
@@ -0,0 +1,10 @@
|
||||
[gd_resource type="Resource" script_class="XRToolsRumbleEvent" load_steps=2 format=3 uid="uid://brci6umrcd157"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b15qba0ch13uw" path="res://addons/godot-xr-tools/rumble/rumble_event.gd" id="1_r2qd4"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_r2qd4")
|
||||
magnitude = 0.5
|
||||
active_during_pause = true
|
||||
indefinite = false
|
||||
duration_ms = 10
|
||||
Reference in New Issue
Block a user