]> Untitled Git - frog-ninja.git/blob - player/player.gd
Added debug overlay to test scene
[frog-ninja.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 := 10.0
8
9 @export var air_speed := 3.0
10 @export var acceleration := 50.0
11 @export var rotation_speed := 10.0
12 @export var idle_timeout := 5.0
13 @export var hard_landing_limit := 10.0
14
15 @export_group("Physics")
16 @export var push_force := 5.0
17
18 @export_group("Camera")
19 @export_range(1.0, 10.0) var camera_distance := 2.0
20 @export_range(0.0, 1.0) var mouse_sensitivity := 0.15
21 @export_range(0.0, 1.0) var mouse_sensitivity_x := 1.0
22 @export_range(0.0, 1.0) var mouse_sensitivity_y := 0.5
23 @export_range(0.0, 10.0) var joystick_sensitivity_x := 4.0
24 @export_range(0.0, 10.0) var joystick_sensitivity_y := 2.0
25
26
27 @onready var _camera_pivot: Node3D = %camera_pivot
28 @onready var _camera: Camera3D = %camera
29 @onready var _camera_spring: SpringArm3D = %spring
30 @onready var _skin: AnimatedSkin = %skin
31
32 var _last_movement_direction := rotation
33 var _floor_normal := Vector3.ONE
34 var _ground_slope_input := 0.0
35 var _camera_input_direction := Vector2.ZERO
36
37 enum {CAMERA_MOUSE_INPUT, CAMERA_JOYSTICK_INPUT}
38 var _camera_input_method := CAMERA_MOUSE_INPUT
39
40 var _idle_time: float = 0.0
41 var _player_speed: float = walk_speed
42
43
44 func _ready() -> void:
45         _camera_spring.spring_length = camera_distance
46
47
48 func _physics_process(delta: float) -> void:
49         _process_camera(delta)
50         _process_player(delta)
51
52
53 func _unhandled_input(event: InputEvent) -> void:
54         # If user clicks on the window, capture the mouse and direct the camera with it
55         if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED:
56                 return
57
58                         #_camera_input_direction *= mouse_sensitivity
59         if event is InputEventMouseMotion:
60                 _camera_input_method = CAMERA_MOUSE_INPUT
61                 _camera_input_direction = event.screen_relative * mouse_sensitivity
62         elif event is InputEventJoypadMotion:
63                 _camera_input_method = CAMERA_JOYSTICK_INPUT
64                 _camera_input_direction = Input.get_vector("camera-left", "camera-right", "camera-up", "camera-down")
65                 _camera_input_direction *= Vector2(joystick_sensitivity_x, -joystick_sensitivity_y)
66
67
68 func _input(event: InputEvent):
69         if event.is_action_pressed("player-dash"):
70                 _skin.transition_dash()
71         elif event.is_action_pressed("player-slash"):
72                 _skin.sword_visible()
73                 _skin.transition_slash()
74         elif event.is_action_pressed("player-shoot"):
75                 _skin.gun_visible()
76                 _skin.transition_gunfire()
77
78
79 # Get the XZ input direction based on player's input relative to the camera
80 func _get_player_move_direction() -> Vector3:
81         var input_dir := Input.get_vector("player-left", "player-right", "player-forward", "player-backward")
82         var forward := _camera.global_basis.z
83         var right := _camera.global_basis.x
84         var move_direction := (forward * input_dir.y + right * input_dir.x).normalized()
85         move_direction.y = 0
86         return move_direction
87
88
89 func _process_camera(delta: float) -> void:
90         # vertical camera rotation
91         _camera_pivot.rotation.x += _camera_input_direction.y * mouse_sensitivity_y * delta
92         _camera_pivot.rotation.x = clamp(_camera_pivot.rotation.x, -PI / 6, PI / 3)
93
94         # horizontal camera rotation
95         _camera_pivot.rotation.y -= _camera_input_direction.x * mouse_sensitivity_x * delta
96
97         # reset mouse movement vector if mouse input
98         if _camera_input_method == CAMERA_MOUSE_INPUT:
99                 _camera_input_direction = Vector2.ZERO
100                 
101         # change spring length depending on player speed
102         _camera_spring.spring_length = lerp(
103                 _camera_spring.spring_length, camera_distance + velocity.length() / 4, delta 
104         ) 
105
106
107 func _process_player_on_floor(delta: float):
108         var move_direction := _get_player_move_direction()
109         
110         # if we're not stuck, then it's okay to set the velocity
111         _floor_normal = get_floor_normal()
112         _ground_slope_input = (PI / 2) - velocity.angle_to(_floor_normal)
113         velocity = velocity.move_toward(
114                 move_direction * (_player_speed + _ground_slope_input * _player_speed),
115                 acceleration * delta
116                 )
117         var movement_speed := Vector3(velocity.x, 0, velocity.z).length()
118         
119         # also, if we're moving, we're not idle
120         if move_direction.length() < 0.2:
121                 if velocity == Vector3.ZERO:
122                         _idle_time += delta
123         else:
124                 _last_movement_direction = move_direction
125                 _idle_time = 0.0
126
127         # if camera is unlocked, rotate whole skin to face movement direction
128         var skin_target_angle := Vector3.BACK.signed_angle_to(_last_movement_direction, Vector3.UP)
129         _skin.global_rotation.y = lerp_angle(
130                 _skin.global_rotation.y,
131                 skin_target_angle,
132                 rotation_speed * delta
133                 )
134                 
135         # lean into player momentum just a little bit
136         _skin.rotation.z = lerp_angle(
137                 _skin.rotation.z,
138                 clamp(_last_movement_direction.signed_angle_to(velocity, Vector3.UP) * movement_speed * 0.08, -PI/4, PI/ 4),
139                 rotation_speed * delta * 0.25
140                 )
141         
142         # let skin know how fast player is moving along the ground
143         _skin.set_walking_speed(movement_speed)
144
145
146 func _process_player(delta: float) -> void:
147         if is_on_floor():
148                 _process_player_on_floor(delta)
149                 
150                 if _idle_time > idle_timeout:
151                         _skin.transition_move()
152                 else:
153                         _skin.transition_move()
154         else:
155                 _skin.transition_falling()
156                 velocity += get_gravity() * air_speed * delta
157
158         move_and_slide()