Skip to content

Commit

Permalink
Merge pull request GodotVR#378 from BastiaanOlij/improve_player_body
Browse files Browse the repository at this point in the history
Change player body to implement kinematic body on root level
  • Loading branch information
BastiaanOlij committed Feb 21, 2023
2 parents dd7dbd5 + ba90e4c commit b126edc
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 84 deletions.
1 change: 1 addition & 0 deletions VERSIONS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 3.3.0 (Development)
- Added reset-scene and scene-control functions to scene-base
- Fixed snap-zones stealing objects picked out of other near-by snap-zones
- Improved player body so it can be used to child objects to

# 3.2.0
- Minimum supported Godot version set to 3.5
Expand Down
6 changes: 3 additions & 3 deletions addons/godot-xr-tools/functions/movement_climb.gd
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bo
offset = right_pickup_pos - right_grab_pos

# Move the player by the offset
var old_position := player_body.kinematic_node.global_transform.origin
player_body.kinematic_node.move_and_collide(-offset)
var old_position := player_body.global_transform.origin
player_body.move_and_collide(-offset)
player_body.velocity = Vector3.ZERO

# Update the players average-velocity data
var distance := player_body.kinematic_node.global_transform.origin - old_position
var distance := player_body.global_transform.origin - old_position
_averager.add_distance(delta, distance)

# Report exclusive motion performed (to bypass gravity)
Expand Down
2 changes: 1 addition & 1 deletion addons/godot-xr-tools/functions/movement_wall_walk.gd
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export var stick_strength : float = 9.8

func physics_pre_movement(_delta: float, player_body: XRToolsPlayerBody):
# Test for collision with wall under feet
var wall_collision := player_body.kinematic_node.move_and_collide(
var wall_collision := player_body.move_and_collide(
player_body.up_player_vector * -stick_distance, true, true, true)
if !wall_collision:
return
Expand Down
119 changes: 74 additions & 45 deletions addons/godot-xr-tools/player/player_body.gd
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
tool
class_name XRToolsPlayerBody, "res://addons/godot-xr-tools/editor/icons/body.svg"
extends Node
extends KinematicBody


## XR Tools Player Physics Body Script
Expand Down Expand Up @@ -59,6 +59,9 @@ export var player_height_max : float = 2.2
## Eyes forward offset from center of body in player_radius units
export (float, 0.0, 1.0) var eye_forward_offset : float = 0.5

## Mix factor for body orientation
export (float, 0.0, 1.0) var body_forward_mix : float = 0.75

## Lets the player push rigid bodies
export var push_rigid_bodies : bool = true

Expand All @@ -68,13 +71,6 @@ export var physics : Resource setget set_physics
## Option for specifying when ground control is allowed
export (GroundControl) var ground_control : int = GroundControl.ON_GROUND

## Collision layer for the player body
export (int, LAYERS_3D_PHYSICS) var collision_layer : int = 1 << 19 setget set_collision_layer

## Collision mask for the player body
export (int, LAYERS_3D_PHYSICS) var collision_mask : int = 1023 setget set_collision_mask


## Player 3D Velocity - modifiiable by [XRToolsMovementProvider] nodes
var velocity : Vector3 = Vector3.ZERO

Expand Down Expand Up @@ -141,22 +137,25 @@ var _previous_ground_local : Vector3 = Vector3.ZERO
# Previous ground global position
var _previous_ground_global : Vector3 = Vector3.ZERO

# Player body Collision node
var _collision_node : CollisionShape


## ARVROrigin node
onready var origin_node : ARVROrigin = ARVRHelpers.get_arvr_origin(self)

## ARVRCamera node
onready var camera_node : ARVRCamera = ARVRHelpers.get_arvr_camera(self)

## Player body node
onready var kinematic_node : KinematicBody = $KinematicBody
## Left hand ARVRController node
onready var left_hand_node : ARVRController = ARVRHelpers.get_left_controller(self)

## Right hand ARVRController node
onready var right_hand_node : ARVRController = ARVRHelpers.get_right_controller(self)

## Default physics (if not specified by the user or the current ground)
onready var default_physics = _guaranteed_physics()

## Player body Collision node
onready var _collision_node : CollisionShape = $KinematicBody/CollisionShape


## Class to sort movement providers by order
class SortProviderByOrder:
Expand All @@ -171,15 +170,27 @@ func is_class(name : String) -> bool:

# Called when the node enters the scene tree for the first time.
func _ready():
# Set as toplevel means our PlayerBody is positioned in global space.
# It is not moved when its parent moves.
set_as_toplevel(true)

# Create our collision shape, height will be updated later
var capsule = CapsuleShape.new()
capsule.radius = player_radius
capsule.height = 1.4
_collision_node = CollisionShape.new()
_collision_node.shape = capsule
_collision_node.transform.basis = Basis(Vector3(0.5 * PI, 0.0, 0.0))
_collision_node.transform.origin = Vector3(0.0, 0.8, 0.0)
add_child(_collision_node)

# Get the movement providers ordered by increasing order
_movement_providers = get_tree().get_nodes_in_group("movement_providers")
_movement_providers.sort_custom(SortProviderByOrder, "sort_by_order")

# Propagate defaults
_update_enabled()
_update_player_radius()
_update_collision_layer()
_update_collision_mask()

func set_enabled(new_value) -> void:
enabled = new_value
Expand Down Expand Up @@ -209,24 +220,6 @@ func set_physics(new_value: Resource) -> void:
physics = new_value
default_physics = _guaranteed_physics()

func set_collision_layer(new_layer: int) -> void:
collision_layer = new_layer
if is_inside_tree():
_update_collision_layer()

func _update_collision_layer() -> void:
if kinematic_node:
kinematic_node.collision_layer = collision_layer

func set_collision_mask(new_mask: int) -> void:
collision_mask = new_mask
if is_inside_tree():
_update_collision_mask()

func _update_collision_mask() -> void:
if kinematic_node:
kinematic_node.collision_mask = collision_mask

func _physics_process(delta: float):
# Do not run physics if in the editor
if Engine.editor_hint:
Expand All @@ -246,7 +239,7 @@ func _physics_process(delta: float):
up_player_plane = Plane(up_player_vector, 0.0)

# Determine environmental gravity
var gravity_state := PhysicsServer.body_get_direct_state(kinematic_node.get_rid())
var gravity_state := PhysicsServer.body_get_direct_state(get_rid())
gravity = gravity_state.total_gravity

# Update the kinematic body to be under the camera
Expand All @@ -272,7 +265,7 @@ func _physics_process(delta: float):
_update_ground_information(delta)

# Get the player body location before movement occurs
var position_before_movement := kinematic_node.global_transform.origin
var position_before_movement := global_transform.origin

# Run the movement providers in order. The providers can:
# - Move the kinematic node around (to move the player)
Expand Down Expand Up @@ -301,7 +294,7 @@ func _physics_process(delta: float):
_apply_velocity_and_control(delta)

# Apply the player-body movement to the ARVR origin
var movement := kinematic_node.global_transform.origin - position_before_movement
var movement := global_transform.origin - position_before_movement
origin_node.global_transform.origin += movement

# Orient the player towards (potentially modified) gravity
Expand Down Expand Up @@ -341,7 +334,7 @@ func request_jump(skip_jump_velocity := false):
## This method moves the players body using the provided velocity. Movement
## providers may use this function if they are exclusively driving the player.
func move_body(p_velocity: Vector3) -> Vector3:
return kinematic_node.move_and_slide(
return move_and_slide(
p_velocity,
up_gravity_vector,
false,
Expand Down Expand Up @@ -371,7 +364,7 @@ func slew_up(up: Vector3, slew: float) -> void:
var current_origin := origin_node.global_transform

# Save the player foot global and local positions
var ref_pos_global := kinematic_node.global_translation
var ref_pos_global := global_translation
var ref_pos_local : Vector3 = current_origin.xform_inv(ref_pos_global)

# Calculate the target origin
Expand Down Expand Up @@ -399,6 +392,40 @@ func override_player_height(key, value: float = -1.0):
var override = _player_height_overrides.values().min()
_player_height_override = override if override != null else -1.0

# Estimate body forward direction
func _estimate_body_forward_dir() -> Vector3:
var forward = Vector3()
var camera_basis : Basis = camera_node.global_transform.basis
var camera_forward : Vector3 = -camera_basis.z;

var camera_elevation := camera_forward.dot(up_player_vector)
if camera_elevation > 0.75:
# User is looking up
forward = up_player_plane.project(-camera_basis.y).normalized()
elif camera_elevation < -0.75:
# User is looking down
forward = up_player_plane.project(camera_basis.y).normalized()
else:
forward = up_player_plane.project(camera_forward).normalized()

if (left_hand_node and left_hand_node.get_is_active()
and right_hand_node and right_hand_node.get_is_active()
and body_forward_mix > 0.0):
# See if we can mix in our estimated forward vector based on controller position
# Note, in Godot 4.0 we should check tracker confidence

var tangent = right_hand_node.global_transform.origin - left_hand_node.global_transform.origin
tangent = up_player_plane.project(tangent).normalized()
var hands_forward = up_player_vector.cross(tangent).normalized()

# Rotate our forward towards our hand direction but not more than 60 degrees
var dot = forward.dot(hands_forward)
var cross = forward.cross(hands_forward).normalized()
var angle = clamp(acos(dot) * body_forward_mix, 0.0, 0.33 * PI)
forward = forward.rotated(cross, angle)

return forward

# This method updates the player body to match the player position
func _update_body_under_camera():
# Calculate the player height based on the camera position in the origin and the calibration
Expand All @@ -421,24 +448,26 @@ func _update_body_under_camera():
_collision_node.transform.origin.y = (player_height / 2.0)

# Center the kinematic body on the ground under the camera
var curr_transform := kinematic_node.global_transform
var curr_transform := global_transform
var camera_transform := camera_node.global_transform
curr_transform.basis = origin_node.global_transform.basis
curr_transform.origin = camera_transform.origin
curr_transform.origin += up_player_vector * (player_head_height - player_height)

# The camera/eyes are towards the front of the body, so move the body back slightly
var forward_dir := up_player_plane.project(-camera_transform.basis.z)
# The camera/eyes are towards the front of the body,
# so move the body back slightly and face in the same direction
var forward_dir := _estimate_body_forward_dir()
if forward_dir.length() > 0.01:
curr_transform = curr_transform.looking_at(curr_transform.origin + forward_dir, up_player_vector)
curr_transform.origin -= forward_dir.normalized() * eye_forward_offset * player_radius

# Set the body position
kinematic_node.global_transform = curr_transform
global_transform = curr_transform

# This method updates the information about the ground under the players feet
func _update_ground_information(delta: float):
# Test how close we are to the ground
var ground_collision := kinematic_node.move_and_collide(
var ground_collision := move_and_collide(
up_gravity_vector * -NEAR_GROUND_DISTANCE, true, true, true)

# Handle no collision (or too far away to care about)
Expand Down Expand Up @@ -533,9 +562,9 @@ func _apply_velocity_and_control(delta: float):
velocity = move_body(local_velocity + ground_velocity)

# Perform bounce test if a collision occurred
if kinematic_node.get_slide_count():
if get_slide_count():
# Get the collider the player collided with
var collision := kinematic_node.get_slide_collision(0)
var collision := get_slide_collision(0)
var collision_node := collision.collider

# Check for a GroundPhysics node attached to the collider
Expand Down
16 changes: 3 additions & 13 deletions addons/godot-xr-tools/player/player_body.tscn
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=2 format=2]

[ext_resource path="res://addons/godot-xr-tools/player/player_body.gd" type="Script" id=1]

[sub_resource type="CapsuleShape" id=1]
radius = 0.4
height = 1.4

[node name="PlayerBody" type="Node"]
script = ExtResource( 1 )

[node name="KinematicBody" type="KinematicBody" parent="." groups=["player_body"]]
[node name="PlayerBody" type="KinematicBody" groups=["player_body"]]
collision_layer = 524288
collision_mask = 1023

[node name="CollisionShape" type="CollisionShape" parent="KinematicBody"]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0.9, 0 )
shape = SubResource( 1 )
script = ExtResource( 1 )
20 changes: 19 additions & 1 deletion project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ _global_script_classes=[ {
"path": "res://addons/godot-xr-tools/functions/movement_flight.gd"
}, {
"base": "XRToolsMovementProvider",
"class": "XRToolsMovementFootstep",
"language": "GDScript",
"path": "res://addons/godot-xr-tools/functions/movement_footstep.gd"
}, {
"base": "XRToolsMovementProvider",
"class": "XRToolsMovementGlide",
"language": "GDScript",
"path": "res://addons/godot-xr-tools/functions/movement_glide.gd"
Expand Down Expand Up @@ -239,7 +244,7 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/godot-xr-tools/objects/pickable.gd"
}, {
"base": "Node",
"base": "KinematicBody",
"class": "XRToolsPlayerBody",
"language": "GDScript",
"path": "res://addons/godot-xr-tools/player/player_body.gd"
Expand Down Expand Up @@ -274,6 +279,16 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/godot-xr-tools/xr/start_xr.gd"
}, {
"base": "Node",
"class": "XRToolsSurfaceAudio",
"language": "GDScript",
"path": "res://addons/godot-xr-tools/audio/surface_audio.gd"
}, {
"base": "Resource",
"class": "XRToolsSurfaceAudioType",
"language": "GDScript",
"path": "res://addons/godot-xr-tools/audio/surface_audio_type.gd"
}, {
"base": "Reference",
"class": "XRToolsVelocityAverager",
"language": "GDScript",
Expand Down Expand Up @@ -335,6 +350,7 @@ _global_script_class_icons={
"XRToolsMovementCrouch": "",
"XRToolsMovementDirect": "",
"XRToolsMovementFlight": "",
"XRToolsMovementFootstep": "",
"XRToolsMovementGlide": "",
"XRToolsMovementGrapple": "",
"XRToolsMovementJump": "",
Expand All @@ -353,6 +369,8 @@ _global_script_class_icons={
"XRToolsSnapZone": "",
"XRToolsStaging": "",
"XRToolsStartXR": "",
"XRToolsSurfaceAudio": "res://addons/godot-xr-tools/editor/icons/foot.svg",
"XRToolsSurfaceAudioType": "res://addons/godot-xr-tools/editor/icons/body.svg",
"XRToolsVelocityAverager": "",
"XRToolsVelocityAveragerLinear": "",
"XRToolsVignette": "",
Expand Down
Loading

0 comments on commit b126edc

Please sign in to comment.