forked from GodotVR/godot-xr-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
40a1d59
commit 6a84924
Showing
14 changed files
with
810 additions
and
83 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
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,217 @@ | ||
@tool | ||
class_name XRToolsMovementWorldGrab | ||
extends XRToolsMovementProvider | ||
|
||
|
||
## XR Tools Movement Provider for World-Grab | ||
## | ||
## This script provides world-grab movement for the player. To add world-grab | ||
## support, the player must also have [XRToolsFunctionPickup] nodes attached | ||
## to the left and right controllers, and an [XRToolsPlayerBody] under the | ||
## [XROrigin3D]. | ||
## | ||
## World-Grab areas inherit from the world_grab_area scene, or be [Area3D] | ||
## nodes with the [XRToolsWorldGrabArea] script attached to them. | ||
|
||
|
||
## Signal invoked when the player starts world-grab movement | ||
signal player_world_grab_start | ||
|
||
## Signal invoked when the player ends world-grab movement | ||
signal player_world_grab_end | ||
|
||
|
||
## Movement provider order | ||
@export var order : int = 15 | ||
|
||
## Smallest world scale | ||
@export var world_scale_min := 0.5 | ||
|
||
## Largest world scale | ||
@export var world_scale_max := 2.0 | ||
|
||
|
||
# Left world-grab handle | ||
var _left_handle : Node3D | ||
|
||
# Right world-grab handle | ||
var _right_handle : Node3D | ||
|
||
|
||
# Left pickup node | ||
@onready var _left_pickup_node := XRToolsFunctionPickup.find_left(self) | ||
|
||
# Right pickup node | ||
@onready var _right_pickup_node := XRToolsFunctionPickup.find_right(self) | ||
|
||
# Left controller | ||
@onready var _left_controller := XRHelpers.get_left_controller(self) | ||
|
||
# Right controller | ||
@onready var _right_controller := XRHelpers.get_right_controller(self) | ||
|
||
|
||
# Add support for is_xr_class on XRTools classes | ||
func is_xr_class(name : String) -> bool: | ||
return name == "XRToolsMovementGrabWorld" or super(name) | ||
|
||
|
||
# Called when the node enters the scene tree for the first time. | ||
func _ready(): | ||
# In Godot 4 we must now manually call our super class ready function | ||
super() | ||
|
||
# Do not initialise if in the editor | ||
if Engine.is_editor_hint(): | ||
return | ||
|
||
# Connect pickup funcitons | ||
if _left_pickup_node.connect("has_picked_up", _on_left_picked_up): | ||
push_error("Unable to connect left picked up signal") | ||
if _right_pickup_node.connect("has_picked_up", _on_right_picked_up): | ||
push_error("Unable to connect right picked up signal") | ||
if _left_pickup_node.connect("has_dropped", _on_left_dropped): | ||
push_error("Unable to connect left dropped signal") | ||
if _right_pickup_node.connect("has_dropped", _on_right_dropped): | ||
push_error("Unable to connect right dropped signal") | ||
|
||
|
||
## Perform player physics movement | ||
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool): | ||
# Disable world-grab movement if requested | ||
if disabled or !enabled: | ||
_set_world_grab_moving(false) | ||
return | ||
|
||
# Always set velocity to zero if enabled | ||
player_body.velocity = Vector3.ZERO | ||
|
||
# Check for world-grab handles being deleted while held | ||
if not is_instance_valid(_left_handle): | ||
_left_handle = null | ||
if not is_instance_valid(_right_handle): | ||
_right_handle = null | ||
|
||
# Disable world-grab movement if not holding the world | ||
if not _left_handle and not _right_handle: | ||
_set_world_grab_moving(false) | ||
return | ||
|
||
# World grabbed | ||
_set_world_grab_moving(true) | ||
|
||
# Handle world-grab movement | ||
var offset := Vector3.ZERO | ||
if _left_handle and not _right_handle: | ||
# Left-hand movement only | ||
var left_pickup_pos := _left_controller.global_position | ||
var left_grab_pos := _left_handle.global_position | ||
offset = left_pickup_pos - left_grab_pos | ||
elif _right_handle and not _left_handle: | ||
# Right-hand movement only | ||
var right_pickup_pos := _right_controller.global_position | ||
var right_grab_pos := _right_handle.global_position | ||
offset = right_pickup_pos - right_grab_pos | ||
else: | ||
# Get the world-grab handle positions | ||
var left_grab_pos := _left_handle.global_position | ||
var right_grab_pos := _right_handle.global_position | ||
var grab_l2r := (right_grab_pos - left_grab_pos).slide(player_body.up_player) | ||
var grab_mid := (left_grab_pos + right_grab_pos) * 0.5 | ||
|
||
# Get the pickup positions | ||
var left_pickup_pos := _left_controller.global_position | ||
var right_pickup_pos := _right_controller.global_position | ||
var pickup_l2r := (right_pickup_pos - left_pickup_pos).slide(player_body.up_player) | ||
var pickup_mid := (left_pickup_pos + right_pickup_pos) * 0.5 | ||
|
||
# Apply rotation | ||
var angle := grab_l2r.signed_angle_to(pickup_l2r, player_body.up_player) | ||
player_body.rotate_player(angle) | ||
|
||
# Apply scale | ||
var new_world_scale := XRServer.world_scale * grab_l2r.length() / pickup_l2r.length() | ||
new_world_scale = clamp(new_world_scale, world_scale_min, world_scale_max) | ||
XRServer.world_scale = new_world_scale | ||
|
||
# Apply offset | ||
offset = pickup_mid - grab_mid | ||
|
||
# Move the player by the offset | ||
var old_position := player_body.global_position | ||
player_body.move_body(-offset / delta) | ||
player_body.velocity = Vector3.ZERO | ||
#player_body.move_and_collide(-offset) | ||
|
||
# Report exclusive motion performed (to bypass gravity) | ||
return true | ||
|
||
|
||
## Start or stop world-grab movement | ||
func _set_world_grab_moving(active: bool) -> void: | ||
# Skip if no change | ||
if active == is_active: | ||
return | ||
|
||
# Update state | ||
is_active = active | ||
|
||
# Handle state change | ||
if is_active: | ||
emit_signal("player_world_grab_start") | ||
else: | ||
emit_signal("player_world_grab_end") | ||
|
||
|
||
## Handler for left controller picked up | ||
func _on_left_picked_up(what : Node3D) -> void: | ||
# Get the world-grab area | ||
var world_grab_area = what as XRToolsWorldGrabArea | ||
if not world_grab_area: | ||
return | ||
|
||
# Get the handle | ||
_left_handle = world_grab_area.get_grab_handle(_left_pickup_node) | ||
if not _left_handle: | ||
return | ||
|
||
|
||
## Handler for right controller picked up | ||
func _on_right_picked_up(what : Node3D) -> void: | ||
# Get the world-grab area | ||
var world_grab_area = what as XRToolsWorldGrabArea | ||
if not world_grab_area: | ||
return | ||
|
||
# Get the handle | ||
_right_handle = world_grab_area.get_grab_handle(_right_pickup_node) | ||
if not _right_handle: | ||
return | ||
|
||
|
||
## Handler for left controller dropped | ||
func _on_left_dropped() -> void: | ||
# Release handle and transfer dominance | ||
_left_handle = null | ||
|
||
|
||
## Handler for righ controller dropped | ||
func _on_right_dropped() -> void: | ||
# Release handle and transfer dominance | ||
_right_handle = null | ||
|
||
|
||
# This method verifies the movement provider has a valid configuration. | ||
func _get_configuration_warnings() -> PackedStringArray: | ||
var warnings := super() | ||
|
||
# Verify the left controller pickup | ||
if !XRToolsFunctionPickup.find_left(self): | ||
warnings.append("Unable to find left XRToolsFunctionPickup node") | ||
|
||
# Verify the right controller pickup | ||
if !XRToolsFunctionPickup.find_right(self): | ||
warnings.append("Unable to find right XRToolsFunctionPickup node") | ||
|
||
# Return warnings | ||
return warnings |
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,6 @@ | ||
[gd_scene load_steps=2 format=3 uid="uid://dg3gr6ofd8yx4"] | ||
|
||
[ext_resource type="Script" path="res://addons/godot-xr-tools/functions/movement_world_grab.gd" id="1_0qp8q"] | ||
|
||
[node name="MovementWorldGrab" type="Node" groups=["movement_providers"]] | ||
script = ExtResource("1_0qp8q") |
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,64 @@ | ||
@tool | ||
@icon("res://addons/godot-xr-tools/editor/icons/hand.svg") | ||
class_name XRToolsWorldGrabArea | ||
extends Area3D | ||
|
||
|
||
## XR Tools World-Grab Area | ||
## | ||
## This script adds world-grab areas to an environment | ||
## | ||
## For world-grab to work, the player must have an [XRToolsMovementWorldGrab] | ||
## node configured appropriately. | ||
|
||
|
||
## If true, the grip control must be held to keep holding the climbable | ||
var press_to_hold : bool = true | ||
|
||
## Dictionary of temporary grab-handles indexed by the pickup node. | ||
var grab_locations := {} | ||
|
||
|
||
# Add support for is_xr_class on XRTools classes | ||
func is_xr_class(name : String) -> bool: | ||
return name == "XRToolsWorldGrabArea" | ||
|
||
|
||
# Called by XRToolsFunctionPickup | ||
func is_picked_up() -> bool: | ||
return false | ||
|
||
func can_pick_up(_by: Node3D) -> bool: | ||
return true | ||
|
||
# Called by XRToolsFunctionPickup when user presses the action button while holding this object | ||
func action(): | ||
pass | ||
|
||
# Ignore highlighting requests from XRToolsFunctionPickup | ||
func request_highlight(_from, _on) -> void: | ||
pass | ||
|
||
# Called by XRToolsFunctionPickup when this is picked up by a controller | ||
func pick_up(by: Node3D, _with_controller: XRController3D) -> void: | ||
# Get the ID to save the grab handle under | ||
var id = by.get_instance_id() | ||
|
||
# Get or construct the grab handle | ||
var handle = grab_locations.get(id) | ||
if not handle: | ||
handle = Node3D.new() | ||
add_child(handle) | ||
grab_locations[id] = handle | ||
|
||
# Set the handles global transform. As it's a child of this | ||
# climbable it will move as the climbable moves | ||
handle.global_transform = by.global_transform | ||
|
||
# Called by XRToolsFunctionPickup when this is let go by a controller | ||
func let_go(_p_linear_velocity: Vector3, _p_angular_velocity: Vector3) -> void: | ||
pass | ||
|
||
# Get the grab handle | ||
func get_grab_handle(p: Node3D) -> Node3D: | ||
return grab_locations.get(p.get_instance_id()) |
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,6 @@ | ||
[gd_scene load_steps=2 format=3 uid="uid://57q7hhomocdh"] | ||
|
||
[ext_resource type="Script" path="res://addons/godot-xr-tools/objects/world_grab_area.gd" id="1_uxhq5"] | ||
|
||
[node name="WorldGrabArea" type="Area3D"] | ||
script = ExtResource("1_uxhq5") |
Oops, something went wrong.