What I would like to know is, is it ok to use Coroutines within all states instead of having them run in Update() with lots of if-else conditionals?
What are some drawbacks with using lots of coroutines if there are any? Will it get less manageable and prone to error?
Currently I created an IEnemyStates interface which all the states of the enemy(EnemyStateAttack, EnemyStateWalk etc…) will implement.
Then within each state I’ll have corresponding coroutines i.e. walking, chasing, attacking etc…
Is this an acceptable approach?
Also, should I check Animator booleans and trigger values within each state itself?
Or instead of making things more complex, should I create a huge Enemy class and throw all the coroutines inside it?
My main concern with this method is, all the attributes, properties etc within my Enemy class should be public in order the states themselves to be able to use them accordingly. Which is something I’m not comfotable with since I get used to use all of my variables in private form as a rule of thumb.
I’ll be happy if someone can shed some light on this and share a workflow model.
For enemies with simple AIs like this, isn’t it more common and easier to use some sort of State Machine? I feel splitting everything up to its own coroutine could work and could be treated as a State Machine, however having coroutines start other coroutines can quickly get out of hand and it’s hard to track. For example, it would be extremely hard to interrupt a coroutine if you weren’t carefully keeping track of it.
Definitely wouldn’t use co-routines for AI or state machines. Generally AI state machines states have an ‘Execute’ method, which is their parallel to update, and called by their containing monobehaviour’s Update method.
If you have performance issues, you tick this Execute method less often; but naturally that should be something you only consider if you’re experiencing performance issues.
As for components such as Animator, you can have your state machine pass around a reference to the containing monobehaviour to GetComponent() from, or references to the components themselves. If all your states use the Animator component of its parent game object, might as well keep passing that reference around.
Also consider that you don’t know the state of your coroutine when it comes to serialization. So your units cant “remember” what they were doing when you load a saved game.
On the other hand I don’t really see a “benefit” compared to traditional aproaches. So why would you want to use them in the first place?
People tend to make each state is a self-contained class with overridden OnEnter/OnUpdate/OnExit functions, but you can also make one without classes, storing the functons in a Dictionary.
Thank you for pointing that out and saving me from future headaches.
I definitely don’t have a supportive thesis for using them. I’m just new to building an AI system and Unity in general. Just asked this specific question to plan things better beforehand.
As I said in my first post, I’m building states as classes which implement IEnemyStates interface. I have two more questions though to make better use of my current model.
1) Would it be beneficial to include actual methods and the other lines of code inside each State? or in the Enemy class that controls states switching?
Like Patrol() method inside EnemyStatePatrol
or does this render having seperate states useless and just leave the states as is with Execute, Enter methods, and place the actual methods inside Enemy class and call them from their corresponding states?
2) Should I shred all Enemy actions into the smallest possible granular states?
i.e. I have a “Draw Gun” action and an actual “Fire” action which together make an Attack action as a whole.
Should I divide those into separate states or make them one with some conditionals? This is more of a manageability question. Is it overdoing to break states into that many small parts or would it be easier to manage the whole conditionals and transitions?
public void Execute()
{
if (!playerInSight)
{
Patrol()
}
else
{
//some other stuff
}
}
private void Patrol()
{
//do patrol stuff
}
It just comes down to how you organise you code, but it doesn’t invalidate your state machine. You need to think of your states as the objects that encapsulate different behaviour. It’s as much a organisational thing as it is a coding pattern.
2: Stuff like a ‘DrawGun’ (and its opposite) action/animation would perhaps be something you put in the Enter/Exit methods of your state machine. Ergo, when entering an attacking/ready state, you check if the gun is drawn, and if not, fire off the ‘DrawGun’ animation.
Actually that second example is quite similar the way I organized it right now so my states both borrow some methods/properties from the enemy class and not, having their own special methods inside each. i.e. SeekingHero() is a common method with a bool return that I use, so it resides in enemy class to serve all other states to prevent rewriting same conditional logic for each state.
So, can we say Enter() method would act similarly to Start() method? I mean kind of preparation for the rest of the state? Currently I’m using it just to point to my Enemy class. EDIT : After having a look at the link you shared, through the end part, I see how Enter() method is used for each state. Thanks!
Also is it ok to group Methods of Mecanim like ResetTrigger(), SetBool("", false) etc in the Exit() Method of each state?
Also in a video on YouTube I have seen that a guy just creates “constants” out of Animation Clip Name strings. And he literally “plays” them as needed when calling corresponding methods, instead of diving into spiderweb world of Mecanim. Is this applicable in all scenarios where a 3D model and more complex animations are being used? Or is this approach limited to Sprite based 2D games with simple animations? Probably the latter?
I ask this because my only concern now is that all Mecanim based parameters start to clutter my States a bit. Not that they’re out of control atm but I suspect I’ll face that inevitable destiny in the future with more complex animations.
If you still on your way to a coroutine-based-solution, I would advice to have a look at UnitTask. I’m using it daily and I’m grateful this package exists. Basically, using this package you can turn a coroutine into an async method. Which will make your code much more cleaner to read, and, above all, easier to debug. Note that unlike Coroutine, which is a feature specific to Unity, async/await is a builtin C# feature.