Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Need feedback on how to synchronize attack animation and hit detection in melee combat

Discussion in 'Scripting' started by dlips, Nov 18, 2020.

  1. dlips

    dlips

    Joined:
    Nov 8, 2019
    Posts:
    4
    Hello,

    I want to implement a simple melee combat system for a top-down 2.5D style game (it is rendered in 3D but uses orthographic projection). I have a player with a sword and an attack animation (using AnimatorController and Mecanim state machine), and I want to synchronize the animation movement with the detection of the hits (detection should be performed at the "peak" of the movement).
    I have figured out/found serveral possibilities to synchronize the attack animation with the hit detection, but I am not sure which one to chose and need feedback from some more experienced people. Here are the methods I have so far, with some notes:

    1) Check for the state of the animator controller with GetCurrentAnimatorStateInfo and then check the normalized time in each Update() to cross a specific value, say 0.6 (roughly determined by eye from playing the animation clip in preview), then perform the hit detection with a SphereOverlapCast and some additional checks to find nearby enemies infront of the player. This works but is kind of ugly, especially because AnimatorStateInfo is hard to work with. Also, if I think about implementing additional melee attacks (with different animations) in the future, this approach seems to be hard to extend.

    2) is a variant of 1). I get the length of the animation clip and speed from the animator and calculate the actual length of the clip and then implement my own timing with Time.deltaTime. Then use the same algorithm to detect hits as in 1)

    3) Use animation events to trigger the SphereOverlapCast. This was also implemented, but I found the animation events not trigger reliably.

    4) Use StateMachineBehaviours to trigger the SphereOverlapCast. I did not implement this approach yet. One reason is that I already have my own finite state machine on the player and use Mecanim more as a "view layer". So, I dont want to implement core gameplay logic in there. Maybe I am wrong here...

    5) I used an additional hitbox (BoxCollider) infront of the player and then animated its enabled value, activating it in the right animation frames. Hit detection is performed by reacting on the OnTriggerEnter message. This works reliable and seems to be a more cleaner approach than 1)-4). However, the animation of the enabled field is directly added to the animation clip, which means that I have problems with missing properties if I want to reuse the same animations on different characters.

    I would like to hear some feedback from someone who also implemented such a melee combat. Maybe you can give me some more pros and cons or know additional methods that could be used?
     
    Last edited: Nov 18, 2020
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    What I'd do is to fire off a coroutine when you start the attack. It waits for the duration up to when you want to enable the hitbox, and then checks if the attack animation is still being played. If that's the case, activate the hitbox.

    C# is a lot more reliable than animation events, and StateMachineBehaviour should not have gameplay state (they have no hard guarantees on Enter/Exit getting called properly, by design).

    You're going to have to use GetCurrentAnimatorStateInfo and friends, but I'd wrap it in an extension method so you don't have to fill the entire horizontal space of your screen to check if the animator is playing a specific animation.
     
  3. dlips

    dlips

    Joined:
    Nov 8, 2019
    Posts:
    4
    Thank you very much. I am still in the beginning of programming in C# and Unity and learn new things everyday. Now I know about Coroutines and how to use them. Also the extension methods in C# are pretty nice. Following your suggestion, I outsourced all the tedious and general stuff regarding the animator to extension methods, which makes the code much cleaner.

    I have quickly implemented the approach with the Coroutine using WaitForSeconds to wait until the right "time", then checking for collisions, and waiting again afterwards until the attack animation should be roughly finished to leave the attack state of my players finite state machine. How long do you activate the hitbox? I found that it needs a little bit of time. Enabling it for only one frame does not work. I think the reason is that the Coroutine is called every frame, but the collisions with the activated BoxCollider is only detected in the next physics update after the activation. So I changed from activating a hitbox to an OverlapBox cast within the Coroutine. This works reliably.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    Yeah. If you're going with the BoxCollider+OnTriggerEnter, you'd have to WaitForNextFixedFrame, so it'd get time to do it's thing.

    An overlap box is both faster, doesn't rely on having the physics world do anything, and doesn't risk getting hit by raycasts from other scripts, so it's generally better from a coding point of view.

    The downside is that with a hitbox, you could put a mesh renderer on it to visualize it, and tune it by scaling and moving it. For a OverlapBox, you either have to eyeball it, draw gizmos, or match it to a reference box, so tuning by hand gets harder.