Delay for damage to target according to animation

Hi

I’d like to make the player loosing HP not when an enemy collide with it but when the animation shows the enemy biting it. So I need delay.
I tried:

  • invoke
  • coroutine
  • adding even to animation

But none of them work. Two first just aren’t a fit to my code and the last one always trigger an issue.

Currently part of the code looks like that:

void OnCollisionEnter2D(Collision2D other)
{
    PlayerCombat player = other.gameObject.GetComponent<PlayerCombat>();

    if (player != null)
    {
        animator.SetTrigger("Attack");
        //StartCoroutine(player.ChangeHealth(-1) (7.0f));
        //Invoke (player.ChangeHealth(-1));, 2);
        //player.ChangeHealth(-1);
        
    }
}

void Attack(Collision2D other)
{
    PlayerCombat player = other.gameObject.GetComponent<PlayerCombat>();
    player.ChangeHealth(-1);
}

but it triggers error

Failed to call AnimationEvent Attack of class Enemy.
The function must have either 0 or 1 parameters and the parameter can only be: string, float, int, enum, Object and AnimationEvent.

I’m kinda tired of looking for solution for it myself so I decided to just ask.

This is what ChatGPT has to say and I’d probably start there.

The issue arises because you’re trying to call the Attack method through an animation event, which has strict limitations on the parameters that can be passed. According to the error message, the function being called must have either 0 or 1 parameter, and it can only be one of the types specified: string, float, int, enum, Object, or AnimationEvent.

Let’s break down the problem and fix it:

Issue Breakdown:

  • Your Attack method is defined as Attack(Collision2D other), but the animation event cannot pass a Collision2D object directly to the function.
  • The animation event expects either no parameters or one parameter of a supported type (e.g., int, float).

Possible Solution:

  • You should either:
    1. Remove the dependency on the Collision2D object in the Attack method.
    2. Use a parameter type that is compatible with the animation event (e.g., an int for damage).

Solution 1: Simplify Attack to use no parameters:

If you want to trigger the attack without relying on Collision2D, you can modify Attack to simply perform the damage logic without needing to pass any parameters from the animation event.

For example:

void OnCollisionEnter2D(Collision2D other)
{
    PlayerCombat player = other.gameObject.GetComponent<PlayerCombat>();

    if (player != null)
    {
        animator.SetTrigger("Attack");
        // Call Attack without parameters
        Attack();
    }
}

void Attack()
{
    // Get the player again or make sure player is in range
    PlayerCombat player = FindObjectOfType<PlayerCombat>(); // Or other way to get the player
    if (player != null)
    {
        player.ChangeHealth(-1);
    }
}

In this approach:

  • Attack no longer expects any parameters, and it simply damages the player when called.
  • You may also need to find the player object in a different way depending on your game structure.

Solution 2: Use a parameter compatible with the animation event:

If you need to use an animation event with a parameter, you can modify Attack to accept a parameter like int (for damage), and then pass the value from the animation event.

For example:

void OnCollisionEnter2D(Collision2D other)
{
    PlayerCombat player = other.gameObject.GetComponent<PlayerCombat>();

    if (player != null)
    {
        animator.SetTrigger("Attack");
        // Instead of calling Attack with the full collision, pass a simple value (e.g., damage).
        Attack(-1); // Pass -1 as damage
    }
}

void Attack(int damage)
{
    // Get the player or ensure you're in range
    PlayerCombat player = FindObjectOfType<PlayerCombat>(); // Or other way to get the player
    if (player != null)
    {
        player.ChangeHealth(damage); // Apply damage
    }
}

In this case:

  • The Attack method accepts an int parameter (damage).
  • You call Attack(-1) to apply damage.
  • This method can now be linked to an animation event that passes an int parameter (e.g., for damage).

Conclusion:

Choose one of these approaches to ensure the Attack method is compatible with the animation event system, and remove the dependency on Collision2D or change it to use a supported parameter type like int or float.

1 Like

Thank you so much!

So first off: Don’t ever use animation events for deterministic gameplay related stuff. They are for audio/visual flourishes only. The reason being is that they are absolutely not guaranteed to trigger when your animation hits a particular point. This is due to the way the animation system interpolates through the timeline of the animation. During low framerates or sudden spikes in frametime you’ll find animation frames are skipped in order to maintain consistent timing and this results in both missed animation frames as well as event triggers.

The correct ways to do this would either be to use one of the methods you attempted before or to use something like a Task with a delay or another self-made timer callback/promise system. In terms of performance I’d recommend either Tasks using the UniTask library or your own custom rolled timer callback system. But they can be a bit of an advanced topic so it might be best to learn more about that stuff before digging too deep. All of these methods have strengths and pitfalls so it can take some time to learn the ins and outs of each.

The simplest approach in your case would be to use Corotuines, however, it looks like both your Invoke and Coroutine are not written properly and won’t compile. You should look up the details found in the link below to understand better how to use them. In your specific case Invoke won’t help since it doesn’t allow you to pass a parameter. I’d recommend Coroutines for now just to get things working and later on when you are more comfortable I’d suggest looking into Tasks and the UniTask library.

If you are having trouble understanding how to use coroutines then feel free to ask for more details here. In the meantime, the links below should hopefully be enough to get you started.

https://docs.unity3d.com/6000.0/Documentation/Manual/coroutines.html

https://docs.unity3d.com/6000.0/Documentation/ScriptReference/MonoBehaviour.Invoke.html