So, I have a character who has an animator and a script attached to him (EnemyScriptTemplate)
EnemyScriptTemplate has a function called AnimatorAttack.
What I want is that when the animator reaches an animation state, upon entering, it starts the coroutine.
Quite simply, I want
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.gameObject.GetComponent<EnemyScriptTemplate>().StartCoroutine(AnimatorAttack());
}
This is what I have, but it isn’t working as it says that “AnimatorAttack does not exist in the current context.”
So, I’m missing something. Perhaps it’s not possible in a StateMachineBehaviour?
Let’s break down what you’re doing here. Anytime you have a function that takes a parameter, you can (almost) always take that parameter out, store it in a variable, and pass that variable instead. So in this case:
var attackCoroutine = AnimatorAttack();
animator.gameObject.GetComponent<EnemyScriptTemplate>().StartCoroutine(attackCoroutine);
Does this help you see the problem? Just because StartCoroutine is being called on the EnemyScriptTemplate object, doesn’t mean you can skip that part for the AnimatorAttack function. I recommend storing the result of GetComponent (so you don’t have to do that twice), then:
EnemyScriptTemplate est = animator.gameObject.GetComponent<EnemyScriptTemplate>();
est.StartCoroutine(est.AnimatorAttack() );
1 Like
Your StateMachineBehaviour seems to know about some Monobehaviour and some method it has.
You typically don’t want to do that. while it can be done, and it will work… it’ll quickly cause your animation system to become tightly coupled with your code. At most you’ll want your StateMachineBehaviour implement a sort of messaging system instead. have it scream “red light…green light” in the general direction and let the listeners behave as they should. It’ll make things far easier to maintain and expand on if your animation isn’t dependent on your code and vice versa. Trust me if your animator tree becomes even moderately complex making the two systems less dependent on each other will help in the long run.
typically a simple AnimationEvent or send message will do the trick. so instead, your function would look like this:
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.SendMessage("AnimateAttack",SendMessageOptions.DontRequireReceiver);
}
See? the StateMachineBehaviour doesn’t even know what script its calling this on. Now, if the Object being animated has has any script with a method named “AnimateAttack”, that takes no parameters (since none were given) then those functions will be called, regardless if it was public or not. don’t make the StateMachineBehaviour worry or even know that its working with a coroutine, make it the scripts job to handle that behind the scenes so EnemyScriptTemplate would look something like this
public class EnemyScriptTemplate: MonoBehaviour
{
private Coroutine handle;
public void AnimateAttack()
{
if(handle!=null) StopCoroutine(handle);
handle = StartCoroutine(AnimatorAttack());
}
private IEnumerator AnimatorAttack()
{
yield return null;
//some other code
}
}
2 Likes
Followed what you said JoshuaMcKenzie and it’s working for me.
That took some serious thought to not overthink (wrap your head around that) but I understand now.
And yes, I could see the danger of my system mounting with more complex code inside animation states.
While, as you say, it should just be saying some basic info.
Bit of reorganizing of what I’ve made and this could really turn out great.
And this is performance optimal? Or rather, not resource intensive.
Like I try to keep best practices and avoid some of the more typical pitfalls like using GameObject.Find.
Basically want a good clean way of the animation states to talk to my enemy script. 
BIG thanks
it really depends on the scale of your game, and to lesser extent the target device.
SendMessage isn’t the fastest way to do messaging (since it relies on Reflection) but its sufficiently fast enough for most games and works out of the box. Simply use SendMessage for now and if it turns out to not be quick enough upgrade to a more specific Messaging System, concept itself is a tried and true design pattern (Observer Pattern).
GameObject.Find() is can be acceptable as long as you cache the result. but if at any point that you can inject the object, you’ll want try and go for that instead. its by far the fastest and most stable way a script can work
The thing is, there is always a way to inject the object. Always. The only exceptions I’ve found in my 10 years of using Unity have been in debug and editor code. For everything meant to run at runtime, there is always, always an objectively better technique than GameObject.Find.
Agreed. In fact in all my time in using unity I never once resorted to using Find(), always felt it made my code far too dependent to specific cases.
For that matter, I don’t use tags either. I didn’t like that you can only assign one tag per GameObject. I instead test if a Gameobject contains a Component that implements an interface or abstract class.
2 Likes
Agreed. I’ve always used MonoBehaviours as tag replacements. You can have multiple per GameObject, you can inherit them, and maintaining a static list of objects is much much faster than .FindWithTag.
1 Like