Hiya y'all,
I've recently jumped into game dev, and have all the animations for my character fleshed out with multiple spritesheets form.
Specifically, I have an animation called "stab" where the player charges up an attack by holding down a key. I want so that while they're holding down the key, for them to be paused on the 3rd frame of the entire stab animation, then, once they release the key, for them to advance to the next frame, while they fly across the screen. Only once they've slowed to a stop (which should take 1 second) should the rest of the animation continue.
My first thought would be to "hard code" the pause in the stab animation in the animation itself. This would entail just padding the animation with 20 frames of the same frame to appear like the animation has paused on the frame, but in reality its just cycling through the same one. This sort of gets the job done, but I was wanting to have an upgrade system where an upgrade decreases up your stab duration & cooldown via pausing on the frame for less time. If I "hard code" it here, then I can't really do that. (Unless I make discrete animation variants, where each one has different number of pause frames, something like 20 for the base, 12 for upgraded, and 8 for upgraded twice or something.) But that's not a very flexible system, and I'm already wanting to program additional logic around this "paused frame" scenario, such as making all inputs do nothing during the duration of the stab.
I've tried googling some examples of this done soundly, as my spaghetti code isn't really getting the job done. Would anyone happen to know any resources for this sort of specific animation control?
For reference, here is my current Player.gd
extends CharacterBody2D
@onready var _animated_sprite = $AnimatedSprite2D
@onready var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var speed = 400
var current_anim = "idle"
var direction = 0
var jump_velocity = 500
var jump_max_epsilon = jump_velocity / 10
var stab_velocity = 500
var stab_velocity_decay = 10
var is_stabbing = false
var facing = 1
func _process(delta):
var to_play = "idle"
direction = 0
if Input.is_action_pressed("ui_right"):
to_play = "run"
_animated_sprite.flip_h = false
facing = 1
direction = 1
elif Input.is_action_pressed("ui_left"):
to_play = "run"
direction = -1
_animated_sprite.flip_h = true
facing = -1
if Input.is_action_pressed("ui_accept"):
# this blocks itself (cannot repeat it once being done)
to_play = "attack"
if Input.is_action_pressed("ui_down"):
# this blocks itself (cannot repeat it once being done)
to_play = "stab"
is_stabbing = true
direction = 0
if _animated_sprite.frame >= 3:
_animated_sprite.frame = 3
if Input.is_action_just_released("ui_down"):
velocity.x += facing * stab_velocity
print("initiating stab with velocity", velocity, is_stabbing)
if Input.is_action_just_pressed("ui_up") and is_on_floor():
velocity.y -= jump_velocity
if not is_on_floor():
if velocity.y < -jump_max_epsilon:
to_play = "jump_ascend"
elif velocity.y > jump_max_epsilon:
to_play = "jump_descend"
else:
to_play = "jump_max"
# TODO: this is currently not working, need a fix!
if is_stabbing:
velocity.x += sign(velocity.x) * (-stab_velocity_decay)
print(velocity)
if abs(velocity.x) < stab_velocity_decay:
velocity.x = 0
is_stabbing = false
else:
velocity.x = direction * speed
current_anim = to_play
_animated_sprite.play(to_play)
func _physics_process(delta):
velocity.y += gravity
move_and_slide()