This seems like a super-basic problem, and I guess I’m not sure why my solution locks up the boolean. Here is the simplified attack function in Update():
if (!attacking)
{
if (Input.GetButtonDown("Fire1"))
{
attacking = true;
player_anim.Play("attack1", attack_layer_number);
}
}
Now, I have an event on “attack1” at the point which a connection either occurred or did not, and another attack should be allowed. This event triggers the following:
public void Hit()
{
attacking = false;
}
I see the boolean “attacking” turn on and off in the inspector, but if I really mash Fire1, then it locks up. I’m guessing that since attack is in Update() and the animation event is called by the animation, that is the root of the problem. But in that case, why even bother with animation events in the first place? What is best practice for this problem that would be in 90% of games (I would imagine).
To react to animation events that aren’t related to animations that play as a result of the player pushing buttons?
…
The problem as far as I can tell is that this ‘event’ on ‘attack1’ occurs mid-way through the animation, instead of at the end. So if you button mash the player can press the ‘Fire1’ button on the back end (after the animation passed the event), but still isn’t complete.
Calling Play on an already Playing animation does not restart it. But effectively does nothing. It’s already playing… so it just continues doing what it’s already doing.
But you’ve gone and set attacking true, and you’re assuming that event is going to come, but it never does.
You should wait until the animation is done playing completely before resetting the ‘attacking’. Or even test if ‘attack1’ is currently playing and skipping over it. Or you can always stop the animation and restart it… not sure if that would look jerky or not.
This though could result in player button presses that feel unresponsive.
How I deal with this is to have another bool called something like ‘doubleUpAttack’… that basically stores that the ‘Fire1’ button was pressed mid attack. So you should play it 1 more time once that is done. Something like this:
bool isAttacking;
bool doubleUpAttack;
void Update()
{
if(!isAttacking)
{
if(Input.GetButtonDown("Fire1") || doubleUpAttack)
{
attacking = true;
doubleUpAttack = false;
player_anim.Play("attack1", attack_layer_number);
}
}
else if(Input.GetButtonDown("Fire1"))
{
doubleUpAttack = true;
}
}
Of course, in this example code I don’t show how to disable ‘attacking’, because you have to resolve that on your own terms by either moving the event, or just testing for if the ‘attack1’ animation is playing.
I don’t know if you’re using Animator or legacy Animation…
Thanks for the thorough answer. I see I that I need to rethink how events work under the hood. It’s true that “hit” occurs before the animation finishes, so this must be the problem, as you say. I’ll remove the event (using animator btw), and perhaps I’ll try two booleans, as you suggest.
If using the Animator, another option is that if “attacking” is being set to false as the result of an animation event, set it to true as the result of an animation event- when I use the term “animation event” though, I’m not actually referring to the kind where you bake a SendMessage call into the animation file itself (the way Unity does it), but rather a timed occurrence in/as a result of the animation state, using a StateMachineBehaviour. I don’t like using AnimationEvents because they tie the event to the base animation file, which could be used for multiple character but require slightly different timings for balance reasons, or be a part of a blended animation which may fire the event multiple times, or not at all. I would also advocate actually calling for new animator states with triggers, as opposed to Play, so things are a bit more abstracted and the player controller doesn’t need to know anything about the specific animation files being played or the animator state names, and you can swap out animation states and even whole animator controllers without any problems.
To clarify: make a new StateMachineBehaviour for “attack” animation states and, in OnStateEnter for that behaviour, set “attacking” in your controller script with something like animator.gameObject.GetComponent().Attacking(); You can then go a couple of different directions for processing the wait time for becoming “ready for next attack input”- I personally like passing a float value to the Attacking() function which starts a coroutine that waits that amount of time, then changes the state to “AttackWaiting”- signifying another attack can occur to combo before the animation is complete. Alternatively, you can count down the time yourself in the OnStateUpdate function of the StateMachineBehaviour, and fire .AttackWaiting() the same way you did with .Attacking() when it hits zero. Either way, in OnStateExit, you can run the last .AttackFinished() function and tell the controller to change state again. While in the AttackWaiting stage, another attack trigger would interrupt the current animation and move into another attack state.
This will allow you to chain together complex pre-selected combos, but allow the animator to control the specific timings. You can adapt it to work with only 1 combo in the game (a single attack trigger, which leads into the next attack state in the sequence as long as it occurs before AttackFinished), or different combos depending entirely on the timing (multiple “AttackWaiting” stages before the attack completes, each leading in a different direction if the attack trigger is received during that phase), or using multiple triggers like AttackA, AttackB, AttackC, each which lead in different directions if they’re received while in the AttackWaiting phase, the way most fighting games work. The important thing is that the state transitions are controlled in two stages- the first is your MonoBehaviour controller which actually fires the triggers, and the second is your animator state machine- the animator state machine would technically allow for infinite combos in a thousand different sequences (it only cares about the transitions between them and timings), but your controller keeps track of the specific sequence and simply won’t fire the triggers for the next attack if it’s not allowed.
Just some ideas to consider- sorry for the wall of text.
Walls of text are certainly welcome – not a problem at all. You’ve given me a lot to think about. I’ve found available examples to be either too simple or too complex to answer certain questions I’ve had, which generally are associated with how to script animations effectively around the editor. Thus, here I am in the forums. Certainly, Mechanim is a powerful tool, but in my humble opinion, Unity should choose to focus more heavily on either scripting or working within the editor – not necessarily both. Making the two play well together requires two different skill sets which are not inherently complimentary. I’ll keep at it though, and thank you for the help @lordofduct and @DonLoquacious .