-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathPlayer.gd
284 lines (221 loc) · 10.3 KB
/
Player.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
extends CharacterBody3D
@onready var body = $Body
@onready var head = $Body/Head
@onready var camera = $Body/Head/CameraMarker3D/Camera3D
@onready var camera_target = $Body/Head/CameraMarker3D
@onready var head_position: Vector3 = head.position
var mouse_sensitivity: float = 0.1
const ACCELERATION_DEFAULT: float = 7.0
const ACCELERATION_AIR: float = 1.0
const SPEED_DEFAULT: float = 7.0
const SPEED_ON_STAIRS: float = 5.0
var acceleration: float = ACCELERATION_DEFAULT
var speed: float = SPEED_DEFAULT
var gravity: float = 9.8
var jump: float = 5.0
var direction: Vector3 = Vector3.ZERO
var main_velocity: Vector3 = Vector3.ZERO
var gravity_direction: Vector3 = Vector3.ZERO
var movement: Vector3 = Vector3.ZERO
const STAIRS_FEELING_COEFFICIENT: float = 2.5
const WALL_MARGIN: float = 0.001
const STEP_DOWN_MARGIN: float = 0.01
const STEP_HEIGHT_DEFAULT: Vector3 = Vector3(0, 0.6, 0)
const STEP_HEIGHT_IN_AIR_DEFAULT: Vector3 = Vector3(0, 0.6, 0)
const STEP_MAX_SLOPE_DEGREE: float = 40.0
const STEP_CHECK_COUNT: int = 2
const SPEED_CLAMP_AFTER_JUMP_COEFFICIENT = 0.4
const SPEED_CLAMP_SLOPE_STEP_UP_COEFFICIENT = 0.4
var step_height_main: Vector3
var step_incremental_check_height: Vector3
var is_enabled_stair_stepping_in_air: bool = true
var is_jumping: bool = false
var is_in_air: bool = false
var head_offset: Vector3 = Vector3.ZERO
var camera_target_position : Vector3 = Vector3.ZERO
var camera_lerp_coefficient: float = 1.0
var time_in_air: float = 0.0
var update_camera = false
var camera_gt_previous : Transform3D
var camera_gt_current : Transform3D
class StepResult:
var diff_position: Vector3 = Vector3.ZERO
var normal: Vector3 = Vector3.ZERO
var is_step_up: bool = false
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
camera_target_position = camera.global_transform.origin
camera.set_as_top_level(true)
camera.global_transform = camera_target.global_transform
camera_gt_previous = camera_target.global_transform
camera_gt_current = camera_target.global_transform
func update_camera_transform():
camera_gt_previous = camera_gt_current
camera_gt_current = camera_target.global_transform
func _process(delta: float) -> void:
if update_camera:
update_camera_transform()
update_camera = false
var interpolation_fraction = clamp(Engine.get_physics_interpolation_fraction(), 0, 1)
var camera_xform = camera_gt_previous.interpolate_with(camera_gt_current, interpolation_fraction)
camera.global_transform = camera_xform
var head_xform : Transform3D = head.get_global_transform()
camera_target_position = lerp(camera_target_position, head_xform.origin, delta * speed * STAIRS_FEELING_COEFFICIENT * camera_lerp_coefficient)
if is_on_floor():
time_in_air = 0.0
camera_lerp_coefficient = 1.0
camera.position.y = camera_target_position.y
else:
time_in_air += delta
if time_in_air > 1.0:
camera_lerp_coefficient += delta
camera_lerp_coefficient = clamp(camera_lerp_coefficient, 2.0, 4.0)
else:
camera_lerp_coefficient = 2.0
camera.position.y = camera_target_position.y
func _input(event):
if event is InputEventMouseMotion:
body.rotate_y(deg_to_rad(-event.relative.x * mouse_sensitivity))
head.rotate_x(deg_to_rad(-event.relative.y * mouse_sensitivity))
head.rotation.x = clamp(head.rotation.x, deg_to_rad(-89), deg_to_rad(89))
func _physics_process(delta):
update_camera = true
var is_step: bool = false
var input = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
direction = (body.global_transform.basis * Vector3(input.x, 0, input.y)).normalized()
if is_on_floor():
is_jumping = false
is_in_air = false
acceleration = ACCELERATION_DEFAULT
gravity_direction = Vector3.ZERO
else:
is_in_air = true
acceleration = ACCELERATION_AIR
gravity_direction += Vector3.DOWN * gravity * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
is_jumping = true
is_in_air = false
gravity_direction = Vector3.UP * jump
main_velocity = main_velocity.lerp(direction * speed, acceleration * delta)
var step_result : StepResult = StepResult.new()
is_step = step_check(delta, is_jumping, step_result)
if is_step:
var is_enabled_stair_stepping: bool = true
if step_result.is_step_up and is_in_air and not is_enabled_stair_stepping_in_air:
is_enabled_stair_stepping = false
if is_enabled_stair_stepping:
global_transform.origin += step_result.diff_position
head_offset = step_result.diff_position
speed = SPEED_ON_STAIRS
else:
head_offset = head_offset.lerp(Vector3.ZERO, delta * speed * STAIRS_FEELING_COEFFICIENT)
if abs(head_offset.y) <= 0.01:
speed = SPEED_DEFAULT
movement = main_velocity + gravity_direction
set_velocity(movement)
set_max_slides(6)
move_and_slide()
if is_step and step_result.is_step_up and is_enabled_stair_stepping_in_air:
if is_in_air or direction.dot(step_result.normal) > 0:
main_velocity *= SPEED_CLAMP_AFTER_JUMP_COEFFICIENT
gravity_direction *= SPEED_CLAMP_AFTER_JUMP_COEFFICIENT
if is_jumping:
is_jumping = false
is_in_air = true
func step_check(delta: float, is_jumping_: bool, step_result: StepResult):
var is_step: bool = false
step_height_main = STEP_HEIGHT_DEFAULT
step_incremental_check_height = STEP_HEIGHT_DEFAULT / STEP_CHECK_COUNT
if is_in_air and is_enabled_stair_stepping_in_air:
step_height_main = STEP_HEIGHT_IN_AIR_DEFAULT
step_incremental_check_height = STEP_HEIGHT_IN_AIR_DEFAULT / STEP_CHECK_COUNT
if gravity_direction.y >= 0:
for i in range(STEP_CHECK_COUNT):
var test_motion_result: PhysicsTestMotionResult3D = PhysicsTestMotionResult3D.new()
var step_height: Vector3 = step_height_main - i * step_incremental_check_height
var transform3d: Transform3D = global_transform
var motion: Vector3 = step_height
var test_motion_params: PhysicsTestMotionParameters3D = PhysicsTestMotionParameters3D.new()
test_motion_params.from = transform3d
test_motion_params.motion = motion
var is_player_collided: bool = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if is_player_collided and test_motion_result.get_collision_normal().y < 0:
continue
transform3d.origin += step_height
motion = main_velocity * delta
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if not is_player_collided:
transform3d.origin += motion
motion = -step_height
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if is_player_collided:
if test_motion_result.get_collision_normal().angle_to(Vector3.UP) <= deg_to_rad(STEP_MAX_SLOPE_DEGREE):
is_step = true
step_result.is_step_up = true
step_result.diff_position = -test_motion_result.get_remainder()
step_result.normal = test_motion_result.get_collision_normal()
break
else:
var wall_collision_normal: Vector3 = test_motion_result.get_collision_normal()
transform3d.origin += wall_collision_normal * WALL_MARGIN
motion = (main_velocity * delta).slide(wall_collision_normal)
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if not is_player_collided:
transform3d.origin += motion
motion = -step_height
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if is_player_collided:
if test_motion_result.get_collision_normal().angle_to(Vector3.UP) <= deg_to_rad(STEP_MAX_SLOPE_DEGREE):
is_step = true
step_result.is_step_up = true
step_result.diff_position = -test_motion_result.get_remainder()
step_result.normal = test_motion_result.get_collision_normal()
break
if not is_jumping_ and not is_step and is_on_floor():
step_result.is_step_up = false
var test_motion_result: PhysicsTestMotionResult3D = PhysicsTestMotionResult3D.new()
var transform3d: Transform3D = global_transform
var motion: Vector3 = main_velocity * delta
var test_motion_params: PhysicsTestMotionParameters3D = PhysicsTestMotionParameters3D.new()
test_motion_params.from = transform3d
test_motion_params.motion = motion
test_motion_params.recovery_as_collision = true
var is_player_collided: bool = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if not is_player_collided:
transform3d.origin += motion
motion = -step_height_main
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if is_player_collided and test_motion_result.get_travel().y < -STEP_DOWN_MARGIN:
if test_motion_result.get_collision_normal().angle_to(Vector3.UP) <= deg_to_rad(STEP_MAX_SLOPE_DEGREE):
is_step = true
step_result.diff_position = test_motion_result.get_travel()
step_result.normal = test_motion_result.get_collision_normal()
elif is_zero_approx(test_motion_result.get_collision_normal().y):
var wall_collision_normal: Vector3 = test_motion_result.get_collision_normal()
transform3d.origin += wall_collision_normal * WALL_MARGIN
motion = (main_velocity * delta).slide(wall_collision_normal)
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if not is_player_collided:
transform3d.origin += motion
motion = -step_height_main
test_motion_params.from = transform3d
test_motion_params.motion = motion
is_player_collided = PhysicsServer3D.body_test_motion(self.get_rid(), test_motion_params, test_motion_result)
if is_player_collided and test_motion_result.get_travel().y < -STEP_DOWN_MARGIN:
if test_motion_result.get_collision_normal().angle_to(Vector3.UP) <= deg_to_rad(STEP_MAX_SLOPE_DEGREE):
is_step = true
step_result.diff_position = test_motion_result.get_travel()
step_result.normal = test_motion_result.get_collision_normal()
return is_step