]> Untitled Git - baabarian.git/blob - player/player.gd
* Added dust clouds
[baabarian.git] / player / player.gd
1 extends CharacterBody3D
2 class_name Player
3
4
5 # player settings
6 @export_group("Movement")
7 @export var walk_speed := 3.0
8 @export var jog_speed := 6.0
9 @export var charge_speed := 20
10
11 @export var air_speed := 3.0
12 @export var acceleration := 20.0
13 @export var rotation_speed := 10.0
14 @export var idle_timeout := 5.0
15 @export var hard_landing_limit := 10.0
16
17 @export_group("Physics")
18 @export var push_force := 5.0
19
20 @export_group("Camera")
21 @export_range(1.0, 10.0) var camera_distance := 2.0
22 @export_range(0.0, 1.0) var mouse_sensitivity := 0.15
23 @export_range(0.0, 1.0) var mouse_sensitivity_x := 1.0
24 @export_range(0.0, 1.0) var mouse_sensitivity_y := 0.5
25 @export_range(0.0, 10.0) var joystick_sensitivity_x := 4.0
26 @export_range(0.0, 10.0) var joystick_sensitivity_y := 2.0
27
28
29 @onready var _debug: CanvasLayer = %debug
30 @onready var _camera_pivot: Node3D = %camera_pivot
31 @onready var _camera: Camera3D = %camera
32 @onready var _camera_spring: SpringArm3D = %spring
33 @onready var _skin: AnimatedSkin = %skin
34 @onready var _dust: GPUParticles3D = %dust
35
36 var _last_movement_direction := rotation
37 var _floor_normal := Vector3.ONE
38 var _camera_input_direction := Vector2.ZERO
39
40 enum {CAMERA_MOUSE_INPUT, CAMERA_JOYSTICK_INPUT}
41 var _camera_input_method := CAMERA_MOUSE_INPUT
42
43 var _idle_time: float = 0.0
44 var _player_speed: float = 0
45
46
47 func _ready() -> void:
48         _debug.draw.add_vector(self, "velocity", 1, 1, Color(0,1,0,1))
49         _debug.draw.add_vector(self, "_floor_normal", 1, 1, Color(0, 0, 1, 1))
50         _debug.draw.add_vector(self, "_last_movement_direction", 1, 1, Color(1,0,0,1))
51         _debug.stats.add_property(self, "velocity", "length")
52         _debug.stats.add_property(self, "_idle_time", "round")
53         
54         _camera_spring.spring_length = camera_distance
55         _player_speed = jog_speed
56         _skin.set_grounded()
57
58
59 func _physics_process(delta: float) -> void:
60         _process_camera(delta)
61         _process_player(delta)
62
63
64 func _unhandled_input(event: InputEvent) -> void:
65         # If user clicks on the window, capture the mouse and direct the camera with it
66         if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED:
67                 return
68
69                         #_camera_input_direction *= mouse_sensitivity
70         if event is InputEventMouseMotion:
71                 _camera_input_method = CAMERA_MOUSE_INPUT
72                 _camera_input_direction = event.screen_relative * mouse_sensitivity
73         elif event is InputEventJoypadMotion:
74                 _camera_input_method = CAMERA_JOYSTICK_INPUT
75                 _camera_input_direction = Input.get_vector("camera-left", "camera-right", "camera-up", "camera-down")
76                 _camera_input_direction *= Vector2(joystick_sensitivity_x, -joystick_sensitivity_y)
77
78
79 func _input(event: InputEvent):
80         if event.is_action_pressed("player_run"):
81                 _player_speed = walk_speed
82         elif event.is_action_released("player_run"):
83                 _player_speed = jog_speed
84         
85         if event.is_action_pressed("player_attack") and velocity.length() > jog_speed * .75:
86                 _player_speed = charge_speed
87         elif event.is_action_released("player_attack"):
88                 _player_speed = jog_speed
89
90
91 # Get the XZ input direction based on player's input relative to the camera
92 func _get_player_move_direction() -> Vector3:
93         var input_dir := Input.get_vector("player_left", "player_right", "player_forward", "player_backward")
94         var forward := _camera.global_basis.z
95         var right := _camera.global_basis.x
96         var move_direction := (forward * input_dir.y + right * input_dir.x).normalized()
97         move_direction.y = 0
98         return move_direction
99
100
101 func _process_camera(delta: float) -> void:
102         # vertical camera rotation
103         _camera_pivot.rotation.x += _camera_input_direction.y * mouse_sensitivity_y * delta
104         _camera_pivot.rotation.x = clamp(_camera_pivot.rotation.x, -PI / 6, PI / 3)
105
106         # horizontal camera rotation
107         _camera_pivot.rotation.y -= _camera_input_direction.x * mouse_sensitivity_x * delta
108
109         # reset mouse movement vector if mouse input
110         if _camera_input_method == CAMERA_MOUSE_INPUT:
111                 _camera_input_direction = Vector2.ZERO
112                 
113         # change spring length depending on player speed
114         _camera_spring.spring_length = lerp(
115                 _camera_spring.spring_length, camera_distance + velocity.length() / 4, delta 
116         ) 
117
118
119 func _process_player_on_floor(delta: float):
120         var move_direction := _get_player_move_direction()
121         
122         # if we're not stuck, then it's okay to set the velocity
123         _floor_normal = get_floor_normal()
124         var ground_angle := move_direction.angle_to(_floor_normal * Vector3(1, 0, 1))
125         #print(str(ground_angle))
126         velocity = velocity.move_toward(move_direction * (_player_speed + ground_angle), acceleration * delta)
127         var movement_speed := Vector3(velocity.x, 0, velocity.z).length()
128         
129         # also, if we're moving, we're not idle
130         if move_direction.length() < 0.2:
131                 if velocity == Vector3.ZERO:
132                         _idle_time += delta
133                 if _idle_time > idle_timeout:
134                         _skin.set_idle()
135         else:
136                 _last_movement_direction = move_direction
137                 _idle_time = 0.0
138                 _skin.set_grounded()
139
140         # if camera is unlocked, rotate whole skin to face movement direction
141         var skin_target_angle := Vector3.BACK.signed_angle_to(_last_movement_direction, Vector3.UP)
142         _skin.global_rotation.y = lerp_angle(
143                 _skin.global_rotation.y,
144                 skin_target_angle,
145                 rotation_speed * delta
146                 )
147                 
148         # lean into player momentum just a little bit
149         _skin.rotation.z = lerp_angle(
150                 _skin.rotation.z,
151                 clamp(_last_movement_direction.signed_angle_to(velocity, Vector3.UP) * movement_speed * 0.08, -PI/4, PI/ 4),
152                 rotation_speed * delta * 0.25
153                 )
154         
155         # let skin know how fast player is moving along the ground
156         _skin.set_grounded_speed(movement_speed)
157         
158         # timescale tweaking for fun effect!
159         if _player_speed == charge_speed:
160                 _skin.set_timescale(2.0)
161         else:
162                 _skin.set_timescale(1.0)
163
164
165 func _process_player(delta: float) -> void:
166         if is_on_floor():
167                 _process_player_on_floor(delta)
168         
169                 _dust.emitting = velocity.length() > (0.75 * charge_speed) and is_on_floor()
170                 _dust.amount_ratio = velocity.length()
171         else:
172                 _skin.set_falling()
173                 velocity += get_gravity() * air_speed * delta
174
175         # now actually move!
176         var velocity_length := velocity.length()
177         move_and_slide()
178
179         # handle collisions 
180         for i in get_slide_collision_count():
181                 var c := get_slide_collision(i)
182                 if c.get_collider() is RigidBody3D:
183                         var col: RigidBody3D = c.get_collider()
184                         col.apply_central_impulse(-c.get_normal() * velocity_length * push_force)