-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
26 changed files
with
596 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,4 @@ | |
logs | ||
Thumbs.db | ||
android/ | ||
.venv/ |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@icon("res://addons/godot-xr-tools/editor/icons/rumble.svg") | ||
class_name XRToolsRumbleEvent | ||
extends Resource | ||
|
||
## | ||
## XR Tools Rumble Event Resource | ||
## | ||
|
||
@export_range(0, 1, 0.10) var magnitude: float = 0.5 | ||
|
||
@export_enum("Both", "Left", "Right") var hand = 0 | ||
|
||
@export_category("Timing") | ||
|
||
@export var indefinite: bool = false | ||
|
||
@export_range(100, 4000, 100) var duration_ms: int = 300 | ||
|
||
@export_category("Hints") | ||
|
||
@export var active_during_pause: bool = false | ||
|
||
var remaining_ms: int | ||
|
||
|
||
# Make sure that every parameter has a default value. | ||
# Otherwise, there will be problems with creating and editing | ||
# your resource via the inspector. | ||
func _init(p_magnitude = 0.1, p_hand := 0, p_duration_ms := 0, | ||
p_active_during_pause := false, p_indefinite := false): | ||
magnitude = p_magnitude | ||
hand = p_hand | ||
duration_ms = p_duration_ms | ||
active_during_pause = p_active_during_pause | ||
indefinite = p_indefinite | ||
reset_remaining() | ||
|
||
|
||
# re-arms event for re-use | ||
func reset_remaining() -> void: | ||
remaining_ms = duration_ms |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
@tool | ||
@icon("res://addons/godot-xr-tools/editor/icons/rumble.svg") | ||
class_name XRToolsRumbleManager | ||
extends Node | ||
|
||
## | ||
## XR Tools Rumble (Controllers) Manager Script | ||
## | ||
## @desc: | ||
## 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. | ||
## | ||
|
||
enum Hand { BOTH = 0, LEFT = 1, RIGHT = 2 } | ||
|
||
# Name in the OpenXR Action Map for haptics | ||
const HAPTIC_ACTION = "haptic" | ||
|
||
@export_range(0.0, 1.0, 0.05) var wakeup_rumble := 0.1 | ||
|
||
## Used to control rumble like volume | ||
@export_range(0.0, 1.0, 0.05) var magnitude_scale := 1.0 | ||
|
||
@export var default_rumble_event: XRToolsRumbleEvent = create_event() | ||
|
||
# Keep track of singular instance | ||
# GDLint Issue: https://github.com/Scony/godot-gdscript-toolkit/issues/242 | ||
static var instance: XRToolsRumbleManager | ||
|
||
var time_start: int = 0 | ||
|
||
# Dictionary Variant to XRToolsRumbleEvent | ||
var _events: Dictionary = {} | ||
|
||
@onready var _controller_left_node: XRController3D = XRHelpers.get_left_controller(self) | ||
|
||
@onready var _controller_right_node: XRController3D = XRHelpers.get_right_controller(self) | ||
|
||
|
||
# Add support for is_xr_class | ||
func is_xr_class(name: String) -> bool: | ||
return name == "XRToolsRumbleManager" | ||
|
||
|
||
static 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) | ||
|
||
|
||
static func normal_clamp(value: float) -> float: | ||
return clamp(value, 0.0, 1.0) | ||
|
||
|
||
static func sec_to_ms(secs: float) -> int: | ||
return int(secs * 1000) | ||
|
||
|
||
static func create_event( | ||
magnitude := 0.1, hand := 0, duration_ms := 100, | ||
active_during_pause := false, indefinite := false | ||
) -> XRToolsRumbleEvent: | ||
var event = XRToolsRumbleEvent.new() | ||
event.magnitude = magnitude | ||
event.hand = hand | ||
event.duration_ms = duration_ms | ||
event.active_during_pause = active_during_pause | ||
event.indefinite = indefinite | ||
return event | ||
|
||
|
||
func _enter_tree(): | ||
if not is_instance_valid(instance): | ||
instance = self | ||
else: | ||
self.queue_free() | ||
|
||
|
||
func _exit_tree(): | ||
if is_instance_valid(instance) and instance == self: | ||
instance = null | ||
|
||
|
||
func _ready(): | ||
set_event_details("wakeup", wakeup_rumble, 0, 150, false) | ||
|
||
|
||
func _process(delta: float) -> void: | ||
# default to no rumble (ensure it's a float, or it rounds to all or nothing!) | ||
var left_magnitude: float = 0.0 | ||
var right_magnitude: float = 0.0 | ||
|
||
# We'll be subtracting this from the event remaining ms | ||
var delta_ms = sec_to_ms(delta) | ||
|
||
# Iterate over the events | ||
for key in _events.keys(): | ||
var event : XRToolsRumbleEvent = _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 | ||
|
||
# Reduce the time remaining | ||
event.remaining_ms -= delta_ms | ||
|
||
# If we've passed the threshold from positive to negative, the event is done | ||
if !event.indefinite and event.remaining_ms < 0: | ||
_events.erase(key) | ||
continue | ||
|
||
# If it's a left or both hand - and of greater magnitude - update left magnitude to be set | ||
if event.hand != Hand.RIGHT and event.magnitude > left_magnitude: | ||
left_magnitude = event.magnitude | ||
|
||
# If it's a right or both hand - and of greater magnitude - update right magnitude to be set | ||
if event.hand != Hand.LEFT and event.magnitude > right_magnitude: | ||
right_magnitude = event.magnitude | ||
|
||
# now that we're done looping through the events, set the rumbles, scaled | ||
if is_instance_valid(_controller_left_node): | ||
_controller_left_node.trigger_haptic_pulse( | ||
HAPTIC_ACTION, 0, left_magnitude * magnitude_scale, 0.1, 0) | ||
|
||
if is_instance_valid(_controller_right_node): | ||
_controller_right_node.trigger_haptic_pulse( | ||
HAPTIC_ACTION, 0, right_magnitude * magnitude_scale, 0.1, 0) | ||
|
||
|
||
func set_event_details( | ||
event_key: Variant, magnitude: float, hand := 0, duration_ms := -1, | ||
active_during_pause := false, indefinite := false | ||
) -> void: | ||
set_event(event_key, create_event(magnitude, hand, duration_ms, active_during_pause, indefinite)) | ||
|
||
|
||
func set_event(event_key: Variant, event: XRToolsRumbleEvent) -> void: | ||
event.reset_remaining() # re-arms event for re-use | ||
if event_key != null: | ||
_events[event_key] = event | ||
else: | ||
push_error("Event key is invalid! ", event_key) | ||
|
||
|
||
func remove(event_key: Variant) -> void: | ||
if event_key != null: | ||
_events.erase(event_key) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
@icon("res://addons/godot-xr-tools/editor/icons/rumble.svg") | ||
class_name XRToolsRumbler | ||
extends Node | ||
|
||
## | ||
## XR Tools Rumbler | ||
## | ||
## @desc: | ||
## A node you attach to handle (contain and make easy to activate/cancel) | ||
## a particular rumble event. | ||
## | ||
|
||
|
||
@export var event: XRToolsRumbleEvent | ||
|
||
|
||
func rumble(): | ||
if is_instance_valid(event) and is_instance_valid(XRToolsRumbleManager.instance): | ||
XRToolsRumbleManager.instance.set_event(self, event) | ||
|
||
|
||
func cancel(): | ||
if is_instance_valid(XRToolsRumbleManager.instance): | ||
XRToolsRumbleManager.instance.remove(self) | ||
|
||
|
||
func rumble_if_player_body(body): | ||
if XRTools.is_xr_class(body, "XRToolsPlayerBody"): | ||
rumble() |
Oops, something went wrong.