How to trigger animation on enemy collision?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ilike2game

So I’m a noob and I’m trying to make it so when the player hits an enemy, they’re repelled back and a certain animation of the AnimatedSprite plays, I have a function called “ouch” on my enemy;

     func _on_sidechecker_body_entered(body): 	
     print("Ow!")
     body.ouch(position.x)

        

that triggers when you touch the side of the enemy, the repulsion works, its knocks you back when you touch it, and the print("ow!) works, but I can’t get the animation to play. I tried using the “ouch” function to reassign my “anim” variable, but I think it might be getting overridden by some of my if statements. Any help would be appreciated, here’s my player code, I thought the “anim = “hurt”” under my func ouch would work but it doesn’t.

 extends KinematicBody2D
var Velocity = Vector2(0,0)
var anim = "idle"
var cigs = 0
const Speed = 300
const Gravity = 35
const Jumpforce = -850

func _physics_process(delta):
	
	if Input.is_action_pressed("Right"):
		Velocity.x = Speed
		$Sprite.flip_h = false
		anim = "walk"
	elif Input.is_action_pressed("Left"):
		Velocity.x = -Speed
		$Sprite.flip_h = false
		anim = "walkleft"
	else:
		$Sprite.flip_h = false
		anim = "idle"
	if not is_on_floor():
		$Sprite.flip_h = false
		anim = "air"
	$Sprite.play(anim)
		
	Velocity.y = Velocity.y + Gravity
	if Input.is_action_pressed("Jump") and is_on_floor():
		Velocity.y = Jumpforce
	if Input.is_action_pressed("Left") && !is_on_floor():
		$Sprite.flip_h = true
		
	Velocity = move_and_slide(Velocity,Vector2.UP)
	
	Velocity.x = lerp(Velocity.x,0,0.2)

    func _on_Area2D_body_entered(body):
    	get_tree().change_scene("res://level.tscn")
    func bounce():
    	Velocity.y = Jumpforce * 0.7
    func add_cigs():
    	cigs = cigs + 1
    func ouch(var enemyposx):
    	anim = "hurt"
            $Sprite.play(anim)
    	if position.x < enemyposx:
    		Velocity.x = -1000
    	elif position.x > enemyposx:
    		Velocity.x = 1000
    	Input.action_release("Left")
    	Input.action_release("Right")
:bust_in_silhouette: Reply From: Lola

Hello,

Firstly, note the AnimatedSprite node already has an animation property that is the direct equivalent of the anim property you are recreating.

You are right about the problem in your code: the ouch function sets the animation to hurt only once but your physics_process resets it the frame after.

The solution to your problem depends on wether your hurt anim is a loop (the animation changes after a given time, potentially looping) or a one-shot (the animation finishes after a given number of frames).

If your animation is a loop, a solution would be to use a Timer to force playing the animation for a minimal duration. By using a one-shot timer and firing at hurt time you can then check in your _physics_process if the timer (say HurtTimer) is stopped or not, interpreting this as wether you’re allowed to change the animation to idle/movement/… or not.

If you are dealing with a “fixed” animation, you should use a boolean flag that store the current hurt state (var is_hurt := false) and check it before changing anim. You would set it to true at hurt time and to false when your sprite fires its animation_finished signal.