Hello. My sword animation is breaking. I have read through the code about 20 times now, and I have no idea what’s going on. Nothing I do fixes it. The sword basically just only works sometimes, always stops working, or the player starts bugging out. Here is my scene tree.
Using await like that creates an unmanageable pile of coroutines.
As one example: What happens if “swordSlash” starts getting pressed while “swordArmReturnOne” is playing from line 66? You’ll have one coroutine waiting in line 69, while the pressed button starts a new animation and waits for the same signal in line 52. When that animation is finished, both coroutines will continue: One sets spellcastingYN to false, the other will start playing the slash animations.
And this is just one example, there are probably dozens of ways this could break in similar ways.
I expect it to only hit once if I click once, and then go back and forth if I hold down my mouse. Here is my code(please tell me if I formatted it wrong. I am new to using this forum)
extends CharacterBody2D
class_name Player
@onready var animatedSprite = $AnimatedSprite2D
@onready var animationPlayer = $AnimationPlayer
@onready var swordCollisionShape = $swordSlashCollision/CollisionShape2D
@onready var Fireball = preload("res://tscnFolder/fireball.tscn")
@onready var heal=preload("res://tscnFolder/heal_body.tscn")
@onready var bullet = preload("res://tscnFolder/bullet.tscn")
@export var playerHealth = 100
@export var mana = 100
@export var speed=100
@export var swordDamage = 50
@export var bulletDamage = 25
var shootingContinue = false
var slashingContinue = false
var slashAnimNumber: int
var spellcastingYN = false
var invincible = false
var spellcast1Ready = true
var spellcast2Ready = true
func _velocityControl():
if velocity.y>0:
velocity.y-=1
elif velocity.y<0:
velocity.y+=1
if velocity.x>0:
velocity.x-=1
func _ready() -> void:
swordCollisionShape.disabled = true
position = Vector2(0,0)
func move():
var input_direction = Input.get_vector("Left","Right","Up","Down")
if input_direction:
if spellcastingYN == false:
$AnimatedSprite2D.play("walking")
elif spellcastingYN == false:
$AnimatedSprite2D.play("idle")
velocity = input_direction * speed
func _input(event):
if event.is_action_pressed("swordSlash"):
spellcastingYN = true
animationPlayer.play("swordSlashInitial")
await animationPlayer.animation_finished
slashingContinue = true
slashAnimNumber = 1
while slashingContinue:
await animationPlayer.animation_finished
animationPlayer.play("swordSlashContinueOne")
slashAnimNumber = 2
await animationPlayer.animation_finished
animationPlayer.play("swordSlashContinueTwo")
slashAnimNumber = 1
if event.is_action_released("swordSlash"):
slashingContinue = false
await animationPlayer.animation_finished
if slashAnimNumber == 1:
animatedSprite.play("swordArmReturnOne")
elif slashAnimNumber == 2:
animationPlayer.play("RESET")
await animatedSprite.animation_finished
spellcastingYN = false
I guess the easiest way is to use an AnimationTree with an AnimationNodeStateMachine for controlling the animation player. You’d have to create your animation sequence in their, then just pass the relevant inputs into it and it will handle the sequencing.
If you don’t want to use an AnimationTree, you can do something like dragonforge suggested. The animation_finished signal will even give you the name of the finished animation as parameter, so you can just use the previous animation and the current input to set the next animation.
This works well, thank you. I have a question though: Why does the function get called on ready, rather than delta? How does the function continue after ready? Additionally, how would I make this loop? After ~an hour of trying to make it loop, I am very confused. Thank you.
You should read this step-by-step guide of connecting signals in the Godot docs. The reason it works, is you’re telling it every time the signal happens to call that method.
To get it working with multiple animations and looping you can do this:
The constants aren’t necessary, but it’s good programming practice. Also, now you don’t need the slashAnimNumber variable anymore. You can delete it.
While we are on the subject of naming conventions, in Godot the standard is snake_case for variable names. You are using what’s known as camelCase. Using standards like this makes your code more readable by others, and it makes it easier for you to understand other code due to the cognitive load of reading code being later. For example, when I see SWORD_SLASH_CONTINUE_2 in code I know that it is an immutable constant in code that cannot be changed - and I know I can find the defition at the very top of the file if I want to see what it is or edit it.
P.S. Glad that solution worked for you. That’s my 150th accepted solution on here! Woo!