Game Devlog - #0
Synopsis
In the wake of my game development journey, I have implemented a myriad of features to my character controller, however I have a ways to go before I can make my game fun, let alone playable.
State Machine
A “Finite State Machine” (FSM) declares potential and active states for a character controller to access. Inactive states contain functions that are inaccessible to the character controller until the set as the active state. There can only be one active state for this state machine (currently), this allows to separate player actions depending on set state without requiring a large nested logic gate.
Unfortunately, the code is not very easy to highlight important portion of the code as this is more so a core component and not a implemented feature. However a very useful guide for FSMs can be found on GDQuest.com. Side note, it seems that this website is also powered by Hugo.
Coyote Time
“Coyote Time” is the allowance for the player to jump even if they are technically in a falling state. The concept stems from the character “Wile E. Coyote” from Looney Tones. Right before he falls to gravity, there’s a brief reprieve before the character is fully effected by gravity.
Now not immediately applying gravity would be a bit goofy, but being extremely strict with jumping last second off a ledge can feel unfair. This is where we implement this feature.
Player Controller Script:
coyote_timer declaration:
@export_category("Jump Assistance")
@export var coyote_time : float = 0.3
#...
@onready var coyote_timer : Timer = %CoyoteTimer
jump method:
func jump() -> void:
self.velocity.y = self.jump_vel
jump_available = false
Aerial State Script:
Connecting a shut-off timer:
player.coyote_timer.timeout.connect(_coyote_timeout)
player.coyote_timer.start(player.coyote_time)
_coyote_timeout method:
func _coyote_timeout():
player.jump_available = false
Input handler:
if Input.is_action_just_pressed("jump"):
if player.jump_available:
player.velocity.y = player.jump_vel
player.jump_available = false
Grounded State Script:
enter method:
func enter(_previous_state_path: String, _data := {}) -> void:
player.jump_available = true
This results in the jump still being available, even within the aerial script, as long as the coyote_timer has not gone off and the user has not already jumped.
Jump Buffer
Buffering is when an input is placed in a queue while a action is currently being processed, then that input is either dumped if too much time has passed, or it will be popped and detected as soon as the previous action is completed. This is most commonly felt in the Soulslike game where a user will attempt to roll, but due to being hit or being out of stamina, the roll is placed in a buffer queue, and once the user is able to roll, the user instantly rolls afterwards.
The importance of the buffer is to give a smooth input feel in the game, as without it, the user will feel as if the game is not responsive due to the user pressing the input too early.
Player Controller Script:
var declaration:
@export_category("Jump Assistance")
@export var coyote_time : float = 0.3
@export var jmp_buffer_time : float = 0.3
Aerial State Script:
_jmp_buffer_timeout method:
func _jmp_buffer_timeout():
player.jump_buffer = false
Input handler:
if Input.is_action_just_pressed("jump"):
if player.jump_available:
player.velocity.y = player.jump_vel
player.jump_available = false
else:
player.jump_buffer = true
get_tree().create_timer(player.jmp_buffer_time).timeout.connect(_jmp_buffer_timeout)
Physics handler:
if player.is_on_floor():
if player.jump_buffer:
player.jump()
player.state_change.emit(GROUND,{})
This causes a global timer to start whenever the player is in the air and the user presses the “jump” button. If the user touches the ground before the timer is up, the user immediately triggers the jumping action before switching to the grounded state, allowing for all flags triggered from touching the ground to be reset.
Enhanced Jump
Initially, I thought the only way of implementing jumping as applying a velocity to the character controller while applying a constant gravitational velocity, and this is definitely a valid approach, however it does require a lot more trial and error in order to obtain a specific jump height and jump distance.
A solution to this is by instead of applying a linear velocity, we instead apply high school physics into our gravity.
Video tutorial:
As the code is basically the same as in the video, I feel the need to re-paste it here is unnecessary.