113 lines
3.2 KiB
GDScript
113 lines
3.2 KiB
GDScript
@tool
|
|
class_name XRFirearmSlide
|
|
extends XRToolsInteractableHandleDriven
|
|
|
|
|
|
## XR Firearm Slide script
|
|
##
|
|
## The firearm slide is a slider transform node controlled by the
|
|
## player through [XRToolsInteractableHandle] instances.
|
|
##
|
|
## The slider translates itelf along its local Z axis, and so should be
|
|
## placed as a child of a node to translate and rotate as appropriate.
|
|
##
|
|
## The interactable slider is not a [RigidBody3D], and as such will not react
|
|
## to any collisions.
|
|
|
|
|
|
## Signal for slider moved
|
|
signal firearm_slider_moved(position)
|
|
|
|
|
|
## Start position for slide, can be positiv and negativ in values
|
|
@export var slider_start : float = 0.0
|
|
|
|
## End position for slide, can be positiv and negativ in values
|
|
@export var slider_end : float = 1.0
|
|
|
|
## Slider position - move to test the position setup
|
|
@export var slider_position : float = 0.0: set = _set_slider_position
|
|
|
|
## Default position
|
|
@export var default_position : float = 0.0
|
|
|
|
## If true, the slider moves to the default position when released
|
|
@export var default_on_release : bool = false
|
|
|
|
|
|
# Add support for is_xr_class on XRTools classes
|
|
func is_xr_class(name : String) -> bool:
|
|
return name == "XRFirearmSlide" or super(name)
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
# In Godot 4 we must now manually call our super class ready function
|
|
super()
|
|
|
|
# Set the initial position to match the initial slider position value
|
|
transform = Transform3D(
|
|
Basis.IDENTITY,
|
|
Vector3(0.0, 0.0, slider_position)
|
|
)
|
|
|
|
# Connect signals
|
|
if released.connect(_on_slider_released):
|
|
push_error("Cannot connect slider released signal")
|
|
|
|
|
|
# Called every frame when one or more handles are held by the player
|
|
func _process(_delta: float) -> void:
|
|
# Get the total handle offsets
|
|
var offset_sum := Vector3.ZERO
|
|
for item in grabbed_handles:
|
|
var handle := item as XRToolsInteractableHandle
|
|
offset_sum += handle.global_transform.origin - handle.handle_origin.global_transform.origin
|
|
|
|
# Rotate the offset sum vector from global into local coordinate space
|
|
offset_sum = offset_sum * global_transform.basis
|
|
|
|
# Get the average displacement in the Z axis
|
|
var offset := offset_sum.z / grabbed_handles.size()
|
|
|
|
# Move the slider by the requested offset
|
|
move_slider(slider_position + offset)
|
|
|
|
|
|
# Move the slider to the specified position
|
|
func move_slider(new_position: float) -> void:
|
|
# Do the slider move
|
|
new_position = _do_move_slider(new_position)
|
|
if new_position == slider_position:
|
|
return
|
|
|
|
# Update the current position
|
|
slider_position = new_position
|
|
|
|
# Emit the moved signal
|
|
emit_signal("firearm_slider_moved", new_position)
|
|
|
|
|
|
# Handle release of slider
|
|
func _on_slider_released(_interactable: XRFirearmSlide):
|
|
if default_on_release:
|
|
move_slider(default_position)
|
|
|
|
|
|
# Called when the slider position is set externally
|
|
func _set_slider_position(new_position: float) -> void:
|
|
new_position = _do_move_slider(new_position)
|
|
slider_position = new_position
|
|
|
|
|
|
# Do the slider move
|
|
func _do_move_slider(new_position: float) -> float:
|
|
# Apply slider limits
|
|
new_position = clamp(new_position, slider_start, slider_end)
|
|
|
|
# Move if necessary
|
|
if new_position != slider_position:
|
|
transform.origin.z = new_position
|
|
|
|
# Return the updated position
|
|
return new_position
|