-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain_character.gd
404 lines (301 loc) · 12.5 KB
/
main_character.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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
extends CharacterBody2D
class_name PlatformerController2D
signal jumped(is_ground_jump: bool)
signal hit_ground()
@export var can_pick = true # can the player pick and item ?
# Set these to the name of your action (in the Input Map)
## Name of input action to move left.
@export var input_left : String = "move_left_1"
## Name of input action to move right.
@export var input_right : String = "move_right_1"
## Name of input action to jump.
@export var input_jump : String = "jump_1"
@export var input_up : String = "up_1"
@export var input_down : String = "down_1"
@export var input_dash : String = "dash_1"
@export var interact : String = "interact_1"
@export var sprite_idle : String = "default"
@export var sprite_jump : String = "jump"
@export var sprite_run : String = "run"
@export var sprite_wall_jump : String = "wall_jump"
@onready var sprite_2d = $Sprite2D
const DEFAULT_MAX_JUMP_HEIGHT = 110
const DEFAULT_MIN_JUMP_HEIGHT = 60
const DEFAULT_DOUBLE_JUMP_HEIGHT = 80
const DEFAULT_JUMP_DURATION = 0.3
var _max_jump_height: float = DEFAULT_MAX_JUMP_HEIGHT
## The max jump height in pixels (holding jump).
@export var max_jump_height: float = DEFAULT_MAX_JUMP_HEIGHT:
get:
return _max_jump_height
set(value):
_max_jump_height = value
default_gravity = calculate_gravity(_max_jump_height, jump_duration)
jump_velocity = calculate_jump_velocity(_max_jump_height, jump_duration)
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
release_gravity_multiplier = calculate_release_gravity_multiplier(
jump_velocity, min_jump_height, default_gravity)
var _min_jump_height: float = DEFAULT_MIN_JUMP_HEIGHT
## The minimum jump height (tapping jump).
@export var min_jump_height: float = DEFAULT_MIN_JUMP_HEIGHT:
get:
return _min_jump_height
set(value):
_min_jump_height = value
release_gravity_multiplier = calculate_release_gravity_multiplier(
jump_velocity, min_jump_height, default_gravity)
var _double_jump_height: float = DEFAULT_DOUBLE_JUMP_HEIGHT
## The height of your jump in the air.
@export var double_jump_height: float = DEFAULT_DOUBLE_JUMP_HEIGHT:
get:
return _double_jump_height
set(value):
_double_jump_height = value
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
var _jump_duration: float = DEFAULT_JUMP_DURATION
## How long it takes to get to the peak of the jump in seconds.
@export var jump_duration: float = DEFAULT_JUMP_DURATION:
get:
return _jump_duration
set(value):
_jump_duration = value
default_gravity = calculate_gravity(max_jump_height, jump_duration)
jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration)
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
release_gravity_multiplier = calculate_release_gravity_multiplier(
jump_velocity, min_jump_height, default_gravity)
## Multiplies the gravity by this while falling.
@export var falling_gravity_multiplier = 1.5
## Amount of jumps allowed before needing to touch the ground again. Set to 2 for double jump.
@export var max_jump_amount = 2
@export var max_dash_amount = 1
@export var max_acceleration = 7000
@export var friction = 25
@export var can_hold_jump : bool = true
## You can still jump this many seconds after falling off a ledge.
@export var coyote_time : float = 0.1
## Pressing jump this many seconds before hitting the ground will still make you jump.
## Only neccessary when can_hold_jump is unchecked.
@export var jump_buffer : float = 0.1
@export var dash_time : float = 1
# These will be calcualted automatically
# Gravity will be positive if it's going down, and negative if it's going up
var default_gravity : float
var jump_velocity : float
var double_jump_velocity : float
# Multiplies the gravity by this when we release jump
var release_gravity_multiplier : float
var facing_left = true
var jumps_left : int
var holding_jump := false
var number_of_dash = max_dash_amount
enum JumpType {NONE, GROUND, AIR}
## The type of jump the player is performing. Is JumpType.NONE if they player is on the ground.
var current_jump_type: JumpType = JumpType.NONE
# Used to detect if player just hit the ground
var _was_on_ground: bool
var acc = Vector2()
# coyote_time and jump_buffer must be above zero to work. Otherwise, godot will throw an error.
@onready var is_coyote_time_enabled = coyote_time > 0
@onready var is_jump_buffer_enabled = jump_buffer > 0
@onready var is_dash_enabled = dash_time > 0
@onready var coyote_timer = Timer.new()
@onready var dash_timer = Timer.new()
@onready var jump_buffer_timer = Timer.new()
func _init():
default_gravity = calculate_gravity(max_jump_height, jump_duration)
jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration)
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
release_gravity_multiplier = calculate_release_gravity_multiplier(
jump_velocity, min_jump_height, default_gravity)
func _ready():
if is_coyote_time_enabled:
add_child(coyote_timer)
coyote_timer.wait_time = coyote_time
coyote_timer.one_shot = true
if is_jump_buffer_enabled:
add_child(jump_buffer_timer)
jump_buffer_timer.wait_time = jump_buffer
jump_buffer_timer.one_shot = true
if is_jump_buffer_enabled:
add_child(dash_timer)
dash_timer.wait_time = dash_time
dash_timer.one_shot = true
func _input(_event):
acc.x = 0
if Input.is_action_pressed(input_left):
acc.x = -max_acceleration
facing_left = false
if Input.is_action_pressed(input_right):
acc.x = max_acceleration
facing_left = true
if Input.is_action_just_pressed(input_jump):
holding_jump = true
start_jump_buffer_timer()
if (not can_hold_jump and can_ground_jump()) or can_double_jump() or can_wall_jump():
jump()
sprite_2d.animation = sprite_jump
if Input.is_action_just_released(input_jump):
holding_jump = false
func _physics_process(delta):
if velocity.x > 1 || velocity.x < -1 :
sprite_2d.animation= sprite_run
else :
sprite_2d.animation= sprite_idle
if is_coyote_timer_running() or current_jump_type == JumpType.NONE:
jumps_left = max_jump_amount
number_of_dash = max_dash_amount
if is_feet_on_ground() and current_jump_type == JumpType.NONE:
start_coyote_timer()
# Check if we just hit the ground this frame
if not _was_on_ground and is_feet_on_ground():
current_jump_type = JumpType.NONE
if is_jump_buffer_timer_running() and not can_hold_jump:
jump()
hit_ground.emit()
#responsable for the dash
if Input.is_action_pressed(input_dash) and number_of_dash > 0 and dash_timer.is_stopped()==true:
if Input.is_action_pressed(input_left):
velocity.x=-max_acceleration*0.5
elif Input.is_action_pressed(input_right) :
velocity.x=max_acceleration*0.5
start_dash_timer()
if can_ground_jump() and can_hold_jump:
jump()
sprite_2d.animation= sprite_jump
# Cannot do this in _input because it needs to be checked every frame
if Input.is_action_pressed(input_jump):
if can_ground_jump() and can_hold_jump:
jump()
sprite_2d.animation=sprite_jump
var gravity = apply_gravity_multipliers_to(default_gravity)
acc.y = gravity
# Apply friction
velocity.x *= 1 / (1 + (delta * friction))
velocity += acc * delta
_was_on_ground = is_feet_on_ground()
move_and_slide()
sprite_2d.flip_h=facing_left
## Use this instead of coyote_timer.start() to check if the coyote_timer is enabled first
func start_coyote_timer():
if is_coyote_time_enabled:
coyote_timer.start()
## Use this instead of jump_buffer_timer.start() to check if the jump_buffer is enabled first
func start_jump_buffer_timer():
if is_jump_buffer_enabled:
jump_buffer_timer.start()
func start_dash_timer():
if is_jump_buffer_enabled:
dash_timer.start()
## Use this instead of `not coyote_timer.is_stopped()`. This will always return false if
## the coyote_timer is disabled
func is_coyote_timer_running():
if (is_coyote_time_enabled and not coyote_timer.is_stopped()):
return true
return false
## Use this instead of `not jump_buffer_timer.is_stopped()`. This will always return false if
## the jump_buffer_timer is disabled
func is_jump_buffer_timer_running():
if is_jump_buffer_enabled and not jump_buffer_timer.is_stopped():
return true
return false
func is_dash_timer_running():
if is_dash_enabled and not dash_timer.is_stopped():
return true
return false
func can_ground_jump() -> bool:
if jumps_left > 0 and current_jump_type == JumpType.NONE:
return true
elif is_coyote_timer_running():
return true
return false
func can_wall_jump() -> bool:
if is_on_wall_only():
return true
else :
return false
func can_double_jump():
if jumps_left <= 1 and jumps_left == max_jump_amount:
# Special case where you've fallen off a cliff and only have 1 jump. You cannot use your
# first jump in the air
return false
if jumps_left > 0 and not is_feet_on_ground() and coyote_timer.is_stopped():
return true
return false
## Same as is_on_floor(), but also returns true if gravity is reversed and you are on the ceiling
func is_feet_on_ground():
if is_on_floor() and default_gravity >= 0:
return true
if is_on_ceiling() and default_gravity <= 0:
return true
return false
## Perform a ground jump, or a double jump if the character is in the air.
func jump():
if can_wall_jump():
wall_jump()
elif can_double_jump():
double_jump()
else:
ground_jump()
## Perform a double jump without checking if the player is able to.
func double_jump():
if jumps_left == max_jump_amount:
# Your first jump must be used when on the ground.
# If your first jump is used in the air, an additional jump will be taken away.
jumps_left -= 1
velocity.y = -double_jump_velocity
current_jump_type = JumpType.AIR
jumps_left -= 1
jumped.emit(false)
func wall_jump():
if jumps_left == max_jump_amount:
# Your first jump must be used when on the ground.
# If your first jump is used in the air, an additional jump will be taken away.
jumps_left -= 1
sprite_2d.animation = sprite_wall_jump
velocity.y = -double_jump_velocity
if facing_left:
velocity.x = -max_acceleration*0.3
else :
velocity.x = max_acceleration*0.3
current_jump_type = JumpType.AIR
jumped.emit(false)
## Perform a ground jump without checking if the player is able to.
func ground_jump():
velocity.y = -jump_velocity
current_jump_type = JumpType.GROUND
coyote_timer.stop()
jumped.emit(true)
func apply_gravity_multipliers_to(gravity) -> float:
if velocity.y * sign(default_gravity) > 0: # If we are falling
gravity *= falling_gravity_multiplier
# if we released jump and are still rising
elif velocity.y * sign(default_gravity) < 0:
if not holding_jump:
if not current_jump_type == JumpType.AIR: # Always jump to max height when we are using a double jump
gravity *= release_gravity_multiplier # multiply the gravity so we have a lower jump
return gravity
## Calculates the desired gravity from jump height and jump duration. [br]
## Formula is from [url=https://www.youtube.com/watch?v=hG9SzQxaCm8]this video[/url]
func calculate_gravity(p_max_jump_height, p_jump_duration):
return (2 * p_max_jump_height) / pow(p_jump_duration, 2)
## Calculates the desired jump velocity from jump height and jump duration.
func calculate_jump_velocity(p_max_jump_height, p_jump_duration):
return (2 * p_max_jump_height) / (p_jump_duration)
## Calculates jump velocity from jump height and gravity. [br]
## Formula from
## [url]https://sciencing.com/acceleration-velocity-distance-7779124.html#:~:text=in%20every%20step.-,Starting%20from%3A,-v%5E2%3Du[/url]
func calculate_jump_velocity2(p_max_jump_height, p_gravity):
return sqrt(abs(2 * p_gravity * p_max_jump_height)) * sign(p_max_jump_height)
## Calculates the gravity when the key is released based off the minimum jump height and jump velocity. [br]
## Formula is from [url]https://sciencing.com/acceleration-velocity-distance-7779124.html[/url]
func calculate_release_gravity_multiplier(p_jump_velocity, p_min_jump_height, p_gravity):
var release_gravity = pow(p_jump_velocity, 2) / (2 * p_min_jump_height)
return release_gravity / p_gravity
## Returns a value for friction that will hit the max speed after 90% of time_to_max seconds. [br]
## Formula from [url]https://www.reddit.com/r/gamedev/comments/bdbery/comment/ekxw9g4/?utm_source=share&utm_medium=web2x&context=3[/url]
func calculate_friction(time_to_max):
return 1 - (2.30259 / time_to_max)
## Formula from [url]https://www.reddit.com/r/gamedev/comments/bdbery/comment/ekxw9g4/?utm_source=share&utm_medium=web2x&context=3[/url]
func calculate_speed(p_max_speed, p_friction):
return (p_max_speed / p_friction) - p_max_speed