237 lines
7.6 KiB
GDScript
237 lines
7.6 KiB
GDScript
@tool
|
|
class_name XRToolsMovementGlide
|
|
extends XRToolsMovementProvider
|
|
|
|
|
|
## XR Tools Movement Provider for Gliding
|
|
##
|
|
## This script provides glide mechanics for the player. This script works
|
|
## with the [XRToolsPlayerBody] attached to the players [XROrigin3D].
|
|
##
|
|
## The player enables flying by moving the controllers apart further than
|
|
## 'glide_detect_distance'.
|
|
##
|
|
## When gliding, the players fall speed will slew to 'glide_fall_speed' and
|
|
## the velocity will slew to 'glide_forward_speed' in the direction the
|
|
## player is facing.
|
|
##
|
|
## Gliding is an exclusive motion operation, and so gliding should be ordered
|
|
## after any Direct movement providers responsible for turning.
|
|
|
|
|
|
## Signal invoked when the player starts gliding
|
|
signal player_glide_start
|
|
|
|
## Signal invoked when the player ends gliding
|
|
signal player_glide_end
|
|
|
|
## Signal invoked when the player flaps
|
|
signal player_flapped
|
|
|
|
## Movement provider order
|
|
@export var order : int = 35
|
|
|
|
## Controller separation distance to register as glide
|
|
@export var glide_detect_distance : float = 1.0
|
|
|
|
## Minimum falling speed to be considered gliding
|
|
@export var glide_min_fall_speed : float = -1.5
|
|
|
|
## Glide falling speed
|
|
@export var glide_fall_speed : float = -2.0
|
|
|
|
## Glide forward speed
|
|
@export var glide_forward_speed : float = 12.0
|
|
|
|
## Slew rate to transition to gliding
|
|
@export var horizontal_slew_rate : float = 1.0
|
|
|
|
## Slew rate to transition to gliding
|
|
@export var vertical_slew_rate : float = 2.0
|
|
|
|
## glide rotate with roll angle
|
|
@export var turn_with_roll : bool = false
|
|
|
|
## Smooth turn speed in radians per second
|
|
@export var roll_turn_speed : float = 1
|
|
|
|
## Add vertical impulse by flapping controllers
|
|
@export var wings_impulse : bool = false
|
|
|
|
## Minimum velocity for flapping
|
|
@export var flap_min_speed : float = 0.3
|
|
|
|
## Flapping force multiplier
|
|
@export var wings_force : float = 1.0
|
|
|
|
## Minimum distance from controllers to ARVRCamera to rearm flaps.
|
|
## if set to 0, you need to reach head level with hands to rearm flaps
|
|
@export var rearm_distance_offset : float = 0.2
|
|
|
|
|
|
## Flap activated (when both controllers are near the ARVRCamera height)
|
|
var flap_armed : bool = false
|
|
|
|
## Last controllers position to calculate flapping velocity
|
|
var last_local_left_position : Vector3
|
|
var last_local_right_position : Vector3
|
|
|
|
# True if the controller positions are valid
|
|
var _has_controller_positions : bool = false
|
|
|
|
|
|
# Left controller
|
|
@onready var _left_controller := XRHelpers.get_left_controller(self)
|
|
|
|
# Right controller
|
|
@onready var _right_controller := XRHelpers.get_right_controller(self)
|
|
|
|
# ARVRCamera
|
|
@onready var _camera_node := XRHelpers.get_xr_camera(self)
|
|
|
|
|
|
# Add support for is_xr_class on XRTools classes
|
|
func is_xr_class(name : String) -> bool:
|
|
return name == "XRToolsMovementGlide" or super(name)
|
|
|
|
|
|
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool):
|
|
# Skip if disabled or either controller is off
|
|
if disabled or !enabled or \
|
|
!_left_controller.get_is_active() or \
|
|
!_right_controller.get_is_active():
|
|
_set_gliding(false)
|
|
return
|
|
|
|
# If on the ground, then not gliding
|
|
if player_body.on_ground:
|
|
_set_gliding(false)
|
|
return
|
|
|
|
# Get the controller left and right global horizontal positions
|
|
var left_position := _left_controller.global_transform.origin
|
|
var right_position := _right_controller.global_transform.origin
|
|
|
|
# Set default wings impulse to zero
|
|
var wings_impulse_velocity := 0.0
|
|
|
|
# If wings impulse is active, calculate flapping impulse
|
|
if wings_impulse:
|
|
# Check controllers position relative to head
|
|
var cam_local_y := _camera_node.position.y
|
|
var left_hand_over_head = cam_local_y < _left_controller.position.y + rearm_distance_offset
|
|
var right_hand_over_head = cam_local_y < _right_controller.position.y + rearm_distance_offset
|
|
if left_hand_over_head && right_hand_over_head:
|
|
flap_armed = true
|
|
|
|
if flap_armed:
|
|
# Get controller local positions
|
|
var local_left_position := _left_controller.position
|
|
var local_right_position := _right_controller.position
|
|
|
|
# Store last frame controller positions for the first step
|
|
if not _has_controller_positions:
|
|
_has_controller_positions = true
|
|
last_local_left_position = local_left_position
|
|
last_local_right_position = local_right_position
|
|
|
|
# Calculate controllers velocity only when flapping downwards
|
|
var left_wing_velocity = 0.0
|
|
var right_wing_velocity = 0.0
|
|
if local_left_position.y < last_local_left_position.y:
|
|
left_wing_velocity = local_left_position.distance_to(last_local_left_position) / delta
|
|
if local_right_position.y < last_local_right_position.y:
|
|
right_wing_velocity = local_right_position.distance_to(last_local_right_position) / delta
|
|
|
|
# Calculate wings impulse
|
|
if left_wing_velocity > flap_min_speed && right_wing_velocity > flap_min_speed:
|
|
wings_impulse_velocity = (left_wing_velocity + right_wing_velocity) / 2
|
|
wings_impulse_velocity = wings_impulse_velocity * wings_force * delta * 50
|
|
emit_signal("player_flapped")
|
|
flap_armed = false
|
|
|
|
# Store controller position for next frame
|
|
last_local_left_position = local_left_position
|
|
last_local_right_position = local_right_position
|
|
|
|
# Calculate global left to right controller vector
|
|
var left_to_right := right_position - left_position
|
|
|
|
if turn_with_roll:
|
|
var angle = -left_to_right.dot(player_body.up_player)
|
|
player_body.rotate_player(roll_turn_speed * delta * angle)
|
|
|
|
# If not falling, then not gliding
|
|
var vertical_velocity := player_body.velocity.dot(player_body.up_gravity)
|
|
vertical_velocity += wings_impulse_velocity
|
|
if vertical_velocity >= glide_min_fall_speed && wings_impulse_velocity == 0.0:
|
|
_set_gliding(false)
|
|
return
|
|
|
|
# Set gliding based on hand separation
|
|
var separation := left_to_right.length() / XRServer.world_scale
|
|
_set_gliding(separation >= glide_detect_distance)
|
|
|
|
# Skip if not gliding
|
|
if !is_active:
|
|
return
|
|
|
|
# Lerp the vertical velocity to glide_fall_speed
|
|
vertical_velocity = lerp(vertical_velocity, glide_fall_speed, vertical_slew_rate * delta)
|
|
|
|
# Lerp the horizontal velocity towards forward_speed
|
|
var horizontal_velocity := player_body.velocity.slide(player_body.up_gravity)
|
|
var dir_forward := left_to_right \
|
|
.rotated(player_body.up_gravity, PI/2) \
|
|
.slide(player_body.up_gravity) \
|
|
.normalized()
|
|
var forward_velocity := dir_forward * glide_forward_speed
|
|
horizontal_velocity = horizontal_velocity.lerp(forward_velocity, horizontal_slew_rate * delta)
|
|
|
|
# Perform the glide
|
|
var glide_velocity := horizontal_velocity + vertical_velocity * player_body.up_gravity
|
|
player_body.velocity = player_body.move_body(glide_velocity)
|
|
|
|
# Report exclusive motion performed (to bypass gravity)
|
|
return true
|
|
|
|
|
|
# Set the gliding state and fire any signals
|
|
func _set_gliding(active: bool) -> void:
|
|
# Skip if no change
|
|
if active == is_active:
|
|
return
|
|
|
|
# Update the is_gliding flag
|
|
is_active = active;
|
|
|
|
# Report transition
|
|
if is_active:
|
|
emit_signal("player_glide_start")
|
|
else:
|
|
emit_signal("player_glide_end")
|
|
|
|
|
|
# This method verifies the movement provider has a valid configuration.
|
|
func _get_configuration_warnings() -> PackedStringArray:
|
|
var warnings := super()
|
|
|
|
# Verify the left controller
|
|
if !XRHelpers.get_left_controller(self):
|
|
warnings.append("Unable to find left XRController3D node")
|
|
|
|
# Verify the right controller
|
|
if !XRHelpers.get_right_controller(self):
|
|
warnings.append("Unable to find right XRController3D node")
|
|
|
|
# Check glide parameters
|
|
if glide_min_fall_speed > 0:
|
|
warnings.append("Glide minimum fall speed must be zero or less")
|
|
if glide_fall_speed > 0:
|
|
warnings.append("Glide fall speed must be zero or less")
|
|
if glide_min_fall_speed < glide_fall_speed:
|
|
warnings.append("Glide fall speed must be faster than minimum fall speed")
|
|
|
|
# Return warnings
|
|
return warnings
|