Heart damage animation not working

Godot Version

4.6

Question

My hit animation for my heart won’t work it just disappears i don’t know why, is it the order in which i have put the scripts. I’m trying to have it where the animation plays before the heart disappears

extends CharacterBody2D

class_name PlayerController

@onready var animation_player: AnimationPlayer = $Animation_Controls/AnimationPlayer
@onready var currenthealth: int = max_health
@onready var particles_player: CPUParticles2D = $Particles/CPUParticles2D

# Movement Variables
@export var acceleration := 2400.0
@export var speed : float = 300.0
@export var jump_force : float = -250.0
@export var jump_time : float =  0.25
@export var coyote_time : float = 0.075
@export var gravity_multiplier : float = 3.0
@export var max_health = 3
@export var health_parent : HBoxContainer

var reset_position : Vector2
var direction = 0
var is_jumping : bool = false
var jump_timer : float = 0
var coyote_timer : float = 0
var can_control : bool = true
var can_take_damage : bool = true
var hearts_list : Array[TextureRect]



***func _ready():***
***	print(get_path())***
***	reset_position = Vector2(3.0, -63.0)***
***	var hearts_parent = health_parent***
***	for child in hearts_parent.get_children():***
***		hearts_list.append(child)***
***	print(hearts_list)***

***func update_hearts():***
***	for i in range(hearts_list.size()):***
***		hearts_list[i].visible = i < currenthealth***

func _physics_process(delta: float) -> void:

	if not can_control: return
	# Add the gravity.
	if not is_on_floor() and not is_jumping:
		velocity += get_gravity() * gravity_multiplier * delta
		coyote_timer += delta
	else:
		coyote_timer = 0



	# Handle jump.
	if Input.is_action_just_pressed("Jump") and (is_on_floor() or coyote_timer < coyote_time):
		particles_player.emitting = true
		velocity.y = jump_force 
		is_jumping = true
	elif  Input.is_action_pressed("Jump") and is_jumping:
		velocity.y = jump_force

	if is_jumping and Input.is_action_pressed("Jump") and jump_timer < jump_time:
		jump_timer += delta
	else:
		is_jumping = false
		jump_timer = 0


	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	direction = Input.get_axis("Left", "Right")
	velocity.x = move_toward(velocity.x, direction * speed, acceleration * delta)

	move_and_slide()


func handle_danger():
	**currenthealth -= 1**
**	if currenthealth == 3:**
**		hearts_list[0].get_child(2).play("Hit")** -- This is where the animation plays 
**	update_hearts()**
	if currenthealth == 0:
		velocity.x = 0
		print("You Died")
		visible = false
		can_control = false
		await get_tree().create_timer(1).timeout
		reset_player()
		return

func reset_player() -> void:
	currenthealth = 3
	update_hearts()
	velocity.x = 0
	global_position = reset_position
	visible = true
	can_control = true

I think the problem is that before the .play you didn’t put animation_player.

If this is correct, it happens because you put .play but you don’t say who has to do it.

the animation plays but the heart disappears before it can play, if i assign it to a different heart the animation plays fine

Okay, sorry, I didn’t understand that well.

your good, ill never say no to someone trying to help

1 Like

Using .play will not wait for the animation to finish, only start it. If your update_hearts sets it’s visibility then it will disappear before the animation finishes. Another issue may be checking currenthealth == 3 which should never happen if the max health is 3, then it could at highest ever equal 2 at this point in the code.

so how do i have it check the array and know which heart plays the animation first, lets say i have 3 hearts how would it know what heart it is on so the i can automatically have it play per heart the is being lost

here’s what i got so far i just need to figure out when to update_hearts()

func handle_danger():
	if currenthealth == 3:
		hearts_list[2].get_child(0).play("Hit")
		currenthealth -= 1
	elif currenthealth == 2:
		hearts_list[1].get_child(0).play("Hit")
		currenthealth -= 1
	elif currenthealth == 1:
		hearts_list[0].get_child(0).play("Hit")
		currenthealth -= 1

You could reduce this using the current health as the index (assuming currenthealth does not go above the max or below zero)

func handle_danger():
	currenthealth -= 1
	hearts_list[currenthealth].get_child(0).play("Hit")

Maybe you could connect or await the animation finish signal, then call update hearts. Or make the visibility change a part of the animation

can you give me an example with the await im not really sure on how to do that

after this ill go read it up on documentation

Sure, after getting the animation player (or is it a AnimatedSprite2D?) you can use it’s animation_finished signal. await will stop the code until the signal is emitted

func handle_danger():
	currenthealth -= 1
	var heart_player = hears_list[currenthealth].get_child(0)
	heart_player.play("Hit")
	await heart_player.animation_finished
	update_hearts()
1 Like

i get an error when i subtract the health below heart_player var:

	var heart_player = hearts_list[currenthealth].get_child(0)
	heart_player.play("Hit")
	await heart_player.animation_finished
	update_hearts()
	if heart_player.animation_finished:
		currenthealth -= 1

Out of bounds get index ‘3’ (on base: ‘Array[TextureRect]’)

subtracting before means you should get a range of 0-2, where subtracting after gives you a range of 1-3, the former is valid.

if heart_player.animation_finished: does not wait for the animation to finish, it always evaluates to true, you should avoid using if with signals as it is very confusing if ever correct.

i took the if away it said the same thing

Yes, you also need to subtract health before indexing

2 Likes

thanks for everything

1 Like