0
\$\begingroup\$

When the user presses the fire button, I set a trigger:

_animator.SetTrigger("HG_Shoot");

This trigger causes a "pistol shoot" animation to be played.

The gun should only be able to shoot again as soon as the shoot animation has finished.

I could check at each Update() if that certain animation is still being played like this:

 bool isAnimationStatePlaying(Animator anim, int animLayer, string stateName)
 {
     if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) &&
             anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f)
         return true;
     else
         return false;
 }

But I don't like the frequent checks. I would rather like to have a variable to would be set by the animator. Or I would set it at the beginning of the shoot animation.

Because there are other animations that I have to check that would disallow shooting of the gun, too, like reloading the gun.

This would make everything even more complicated.

How can I solve this problem?

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Is this code really showing up in your profiler as a significant cost in your game? \$\endgroup\$
    – DMGregory
    May 16 at 12:45
  • \$\begingroup\$ @DMGregory No, but my instincts tell me, and my programmer heart hurts because my current approach looks so horribly unprofessional. I want to check a variable only that would be set by the animator if a state changes or so. \$\endgroup\$
    – tmighty
    May 16 at 12:47
  • \$\begingroup\$ @DMGregory Yes, but I have so many other conditions that I would have to check: "IsDying", "IsClimbing", "IsJumping". See? That's why I was thinking about a variable that I could simply query. \$\endgroup\$
    – tmighty
    May 16 at 13:05
  • \$\begingroup\$ Any reason you can't just use Animation Events to Enable/Disable Firing? \$\endgroup\$ May 16 at 13:06
2
\$\begingroup\$

You could use Unity's StateMachineBehavior component. I'll tell about it later.
However, it's actually might be an overkill feature that might bring up more complexity to the project than needed.
There's nothing wrong with your current approach as far as you keep things more or less readable and properly structured. The only other alternative (in addition to your current approach and StateMachineBehaviour approach) would be a timer that would count down in every frame to check if the shoot cooldown is over, or reload time is over, and it's also a commonly used approach.
As for the isJumping, isClimbing, etc states when the gun shouldn't be able to shoot as well, these states should be managed in the character scripts while the gun's isShooting, isReloading, isJammed states should be in the gun's scripts. That's how you keep things manageable. Player clicks Fire button, you call Shoot() method in the character script that checks the character's states such as isJumping, etc, and then if everything is fine and the character is not busy you call the gun's Shoot() method that checks if the gun is not already shooting or reloading currently.
Okay. The StateMachineBehaviour. This is a thing that allows you to get certain predefined events from the animator states (nodes in the animator controller window) so you can do your stuff when the animator has entered an animation state, is currently performing some animation or the animation is over and the control is going to transition to some other state.
Here is the official manual: https://docs.unity3d.com/Manual/StateMachineBehaviours.html
The basic workflow is: You open up the animator controller window of the gun's animator, select your 'Shoot' node, than in the inspector window you add a StateMachineBehavior component to the node.
It should generate a script that has several methods like this:

//OnStateExit is called when a transition ends and the state machine finishes evaluating this state
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    
}

(Not sure if this is exactly how it's done though, I didn't use the StateMachineBehaviour for a long time).

That's what you're looking for - an event that fires when the 'Shoot' animation is over. There is also the OnStateEnter(...) method available as well.
So, you could manage the gun's 'canShoot` boolean flag like this:

override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    GunScript gun = animator.GetComponent<GunScript>();
    gun.canShoot = false;
}

override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    GunScript gun = animator.GetComponent<GunScript>();
    gun.canShoot = true;
}

You would need to add similar components to other animator nodes like the gun's 'Reload' node, the player's 'Jump' node, etc, so whenever these animations are played the gun's canShoot flag will be set to false and get back to true when the animation is over.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you, your timer approach is actually what I need. It just came to my mind again that the user can achieve a higher shooting speed (for example by weapon upgrades), so I definitively need the timer approach. Thank you very much, also for the state machine explanations. \$\endgroup\$
    – tmighty
    May 19 at 15:09
2
\$\begingroup\$

You could add an animation event to your shooting animation action which calls back your controller script and signals it that the player can shoot again.

However, I generally prefer to not design my gameplay using the animation system. I instead design the gameplay first and then have the animation match the gameplay. A game is far easier to balance when all the relevant numbers are in one place. So I would usually define the fire rate of each weapon in the same place where I define the damage, range, accuracy and so on. Then I would experiment with those values until the game is properly balanced, and then take care of matching the animations to the gameplay. Matching animations to gameplay can also be automated by calculating the playback speed of animations based on gameplay variables.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the explanation. The pro tipp of having the animation match the game play and not the other way around will stay with me forever. Thank you!!! \$\endgroup\$
    – tmighty
    May 19 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.