Basic character movement controls

This commit is contained in:
Rob Kelly 2025-09-29 13:32:23 -06:00
parent 589922d6b8
commit 7e998e76dc
10 changed files with 178 additions and 4 deletions

View File

@ -59,8 +59,57 @@ config/input/mouse_sensitivity_y=0.3
config/input/camera_acceleration=40.0 config/input/camera_acceleration=40.0
config/input/invert_pitch=false config/input/invert_pitch=false
[input]
move_forward={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
move_left={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
move_back={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
move_right={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
jump={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
sprint={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
interact={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
fire={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(147, 21),"global_position":Vector2(156, 67),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
pause={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
[layer_names] [layer_names]
3d_render/layer_1="Global Noise Layer" 3d_render/layer_1="Global Noise Layer"
3d_physics/layer_1="World Geometry" 3d_physics/layer_1="World Geometry"
3d_physics/layer_2="Player" 3d_physics/layer_2="Player"
3d_physics/layer_3="Layer 3"

View File

@ -3,6 +3,8 @@ class_name CameraController extends Node3D
const PITCH_LIMIT := deg_to_rad(85.0) const PITCH_LIMIT := deg_to_rad(85.0)
@export var yaw_root: Node3D
@onready var _target := Vector2(rotation.x, rotation.y) @onready var _target := Vector2(rotation.x, rotation.y)
@ -17,5 +19,5 @@ func camera_motion(motion: Vector2) -> void:
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
var accel: float = ProjectSettings.get_setting("game/config/input/camera_acceleration") var accel: float = ProjectSettings.get_setting("game/config/input/camera_acceleration")
var weight := 1.0 - exp(-accel * delta) var weight := 1.0 - exp(-accel * delta)
rotation.y = lerp_angle(rotation.y, _target.y, weight) yaw_root.rotation.y = lerp_angle(yaw_root.rotation.y, _target.y, weight)
rotation.x = lerp_angle(rotation.x, _target.x, weight) rotation.x = lerp_angle(rotation.x, _target.x, weight)

View File

@ -0,0 +1,61 @@
class_name MovementController extends Node
## Character movement controller
@export var movement_enabled := true
@export_group("Speed")
@export var run_speed := 80.0
@export var sprint_speed := 160.0
@export var air_speed_factor := 0.1
@export_group("Jump")
@export var jump_force := 4.0
@export_group("Friction")
@export var ground_friction := 0.3
@export var air_friction := 0.04
var sprinting := false
var movement := Vector2.ZERO
@onready var character: CharacterBody3D = get_parent()
func get_speed() -> float:
var speed := run_speed
if sprinting:
speed = sprint_speed
if not character.is_on_floor():
speed *= air_speed_factor
return speed
func get_friction() -> float:
return ground_friction if character.is_on_floor() else air_friction
## Make the character jump.
func jump() -> void:
if movement_enabled and character.is_on_floor():
character.velocity.y = jump_force
func _physics_process(delta: float) -> void:
if not character.is_on_floor():
# Apply gravity
character.velocity += character.get_gravity() * delta
if movement_enabled:
# Apply movement input
var rel_movement := movement.rotated(-character.global_rotation.y)
var direction := Vector3(rel_movement.x, 0, rel_movement.y).normalized()
var motion := direction * get_speed() * delta
character.velocity.x += motion.x
character.velocity.z += motion.z
# Apply friction
var weight := 1.0 - exp(-get_friction() * 60 * delta)
character.velocity.x = lerpf(character.velocity.x, 0, weight)
character.velocity.z = lerpf(character.velocity.z, 0, weight)
character.move_and_slide()

View File

@ -0,0 +1 @@
uid://c0nxcunx0fo4r

9
src/player/player.gd Normal file
View File

@ -0,0 +1,9 @@
class_name Player extends CharacterBody3D
## The instance of the current player in the game world.
## Global singleton accessor
static var instance: Player
func _init() -> void:
Player.instance = self

1
src/player/player.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://bihaf1xlwpkdo

View File

@ -1,9 +1,12 @@
[gd_scene load_steps=8 format=3 uid="uid://dtbulshrxetes"] [gd_scene load_steps=12 format=3 uid="uid://dtbulshrxetes"]
[ext_resource type="Script" uid="uid://bihaf1xlwpkdo" path="res://src/player/player.gd" id="1_5f2sf"]
[ext_resource type="Script" uid="uid://f3kssctvn2bo" path="res://src/player/camera_controller.gd" id="1_mf2ua"] [ext_resource type="Script" uid="uid://f3kssctvn2bo" path="res://src/player/camera_controller.gd" id="1_mf2ua"]
[ext_resource type="Script" uid="uid://csjccrhj5wnx7" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="1_rxibo"] [ext_resource type="Script" uid="uid://csjccrhj5wnx7" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="1_rxibo"]
[ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="2_mf2ua"] [ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="2_mf2ua"]
[ext_resource type="Script" uid="uid://032jyhgkb2rv" path="res://src/player/mouselook_controller.gd" id="4_244u8"] [ext_resource type="Script" uid="uid://032jyhgkb2rv" path="res://src/player/mouselook_controller.gd" id="4_244u8"]
[ext_resource type="Script" uid="uid://c0nxcunx0fo4r" path="res://src/player/movement_controller.gd" id="6_jiejy"]
[ext_resource type="Script" uid="uid://d11erhxna68vd" path="res://src/player/player_input_controller.gd" id="7_o822w"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_qnmj6"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_qnmj6"]
@ -15,20 +18,27 @@ duration = 0.4
transition = 7 transition = 7
ease = 0 ease = 0
[sub_resource type="CapsuleMesh" id="CapsuleMesh_o822w"]
radius = 0.1
height = 1.0
[node name="Player" type="CharacterBody3D"] [node name="Player" type="CharacterBody3D"]
collision_layer = 3 collision_layer = 3
script = ExtResource("1_5f2sf")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("CapsuleShape3D_qnmj6") shape = SubResource("CapsuleShape3D_qnmj6")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="."]
gi_mode = 0
mesh = SubResource("CapsuleMesh_4anbu") mesh = SubResource("CapsuleMesh_4anbu")
[node name="CameraOffset" type="Node3D" parent="."] [node name="CameraOffset" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
[node name="CameraController" type="Node3D" parent="CameraOffset"] [node name="CameraController" type="Node3D" parent="CameraOffset" node_paths=PackedStringArray("yaw_root")]
script = ExtResource("1_mf2ua") script = ExtResource("1_mf2ua")
yaw_root = NodePath("../..")
[node name="PhantomCamera3D" type="Node3D" parent="CameraOffset/CameraController"] [node name="PhantomCamera3D" type="Node3D" parent="CameraOffset/CameraController"]
script = ExtResource("1_rxibo") script = ExtResource("1_rxibo")
@ -37,6 +47,19 @@ tween_resource = SubResource("Resource_244u8")
noise_emitter_layer = 1 noise_emitter_layer = 1
metadata/_custom_type_script = "uid://csjccrhj5wnx7" metadata/_custom_type_script = "uid://csjccrhj5wnx7"
[node name="MeshInstance3D" type="MeshInstance3D" parent="CameraOffset/CameraController/PhantomCamera3D"]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 0, -0.45)
gi_mode = 0
mesh = SubResource("CapsuleMesh_o822w")
[node name="MouselookController" type="Node" parent="CameraOffset/CameraController"] [node name="MouselookController" type="Node" parent="CameraOffset/CameraController"]
script = ExtResource("4_244u8") script = ExtResource("4_244u8")
metadata/_custom_type_script = "uid://032jyhgkb2rv" metadata/_custom_type_script = "uid://032jyhgkb2rv"
[node name="MovementController" type="Node" parent="."]
script = ExtResource("6_jiejy")
metadata/_custom_type_script = "uid://c0nxcunx0fo4r"
[node name="PlayerInputController" type="Node" parent="MovementController"]
script = ExtResource("7_o822w")
metadata/_custom_type_script = "uid://d11erhxna68vd"

View File

@ -0,0 +1,13 @@
class_name PlayerInputController extends Node
## Player controller component using project-defined player inputs.
@onready var movement_controller: MovementController = get_parent()
func _physics_process(_delta: float) -> void:
if Input.is_action_just_pressed("jump"):
movement_controller.jump()
movement_controller.movement = Input.get_vector(
"move_left", "move_right", "move_forward", "move_back"
)
movement_controller.sprinting = Input.is_action_pressed("sprint")

View File

@ -0,0 +1 @@
uid://d11erhxna68vd

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=9 format=3 uid="uid://daxngklaqlyba"] [gd_scene load_steps=11 format=3 uid="uid://daxngklaqlyba"]
[ext_resource type="PackedScene" uid="uid://dtbulshrxetes" path="res://src/player/player.tscn" id="1_1k4gi"] [ext_resource type="PackedScene" uid="uid://dtbulshrxetes" path="res://src/player/player.tscn" id="1_1k4gi"]
[ext_resource type="Script" uid="uid://bd046eokvcnu2" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="2_6fy3g"] [ext_resource type="Script" uid="uid://bd046eokvcnu2" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="2_6fy3g"]
@ -28,6 +28,11 @@ size = Vector2(16, 16)
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_1k4gi"] [sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_1k4gi"]
[sub_resource type="BoxMesh" id="BoxMesh_6fy3g"]
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_6fy3g"]
data = PackedVector3Array(-0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5)
[node name="World" type="Node3D"] [node name="World" type="Node3D"]
[node name="WorldEnvironment" type="WorldEnvironment" parent="."] [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
@ -56,3 +61,12 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
process_priority = 300 process_priority = 300
process_physics_priority = 300 process_physics_priority = 300
script = ExtResource("2_6fy3g") script = ExtResource("2_6fy3g")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5, 0.5, 4.5)
mesh = SubResource("BoxMesh_6fy3g")
[node name="StaticBody3D" type="StaticBody3D" parent="MeshInstance3D"]
[node name="CollisionShape3D" type="CollisionShape3D" parent="MeshInstance3D/StaticBody3D"]
shape = SubResource("ConcavePolygonShape3D_6fy3g")