Click here to Skip to main content
Click here to Skip to main content
Go to top

Mastering Unity 2D Game Development–AI and State machines

, 2 Sep 2014
Rate this:
Please Sign up or sign in to vote.
It is finally here, my first title has now been published on Packt’s publishing site and all of the leading online bookstores (probably a few less reputable as well ??).  If you like what you see in these snippets, they you are going to love the full book. Here’s an overview:

It is finally here, my first title has now been published on Packt’s publishing site and all of the leading online bookstores (probably a few less reputable as well ??).  If you like what you see in these snippets, they you are going to love the full book. Here’s an overview of what’s in store for you.

The sample project and code for this snippet can be found here: – Mecanim State Machines.zip

Mastering Unity 2D Game Development

About the book

The book was an interesting challenge for me and is written with the same flair I use within my blog, I have always felt it’s better to educate and show you, not only how to do things but also why you should do it one way over another, plus if there are any alternatives, I’ll point them out.  As a reader you should be informed about your choices (and then make your own mind up ).

Through its pages you will build an RPG game framework which you can then extend and make your own, the aim is to give you enough hints, tips and help to build your own finished game.

Here’s what you should expect from the title:

  • A run through the new improvements in Unity 4.3 and 2D game development (plus everything else in case you missed it)
  • A deep dive in to the new Sprite system and the Animation improvements (the first of my chapters that got so big it had to be split in twain)
  • Working with 2D camera’s, scenes and sprite layering plus some advanced coding techniques which lead up to building your own RPG conversation system.
  • We cover building a map and exploration system with the eventual conclusion of running in to some nasty goblins who have a really mean steak
  • If shopping is your thing, you’ve come to the right place, can I interest you in this lovely lv 1 sword. Learn to build a shopping system and then head back out in to the fray
  • In the second chapter that also got two big for its boots and had to be severed right down the middle we cover turn based battle systems, including some was to use Mecanim that you may have never considered before (State battle machines and AI anyone?)
  • With the game framework done we look at finishing your title and looking at the editor to see how we can extend it to help build our game for us (editor scripting, yummy), rounding up with an in depth report on enabling in-app purchasing the right way
  • Finally, we round up with a look at extending and deploying to platforms, cram packed with help on serialisation (saving and loading), making code only run on specific platforms or the editor and masses of hints and tips on marketing

My only regret with this book is that it couldn’t be bigger  there is more than enough information within this titles pages to get you 90% there with your own game, all you got to do is finish it and add lots more content!.

As with everything I do, if there’s more you want to know on any subject within the book, drop me a line or comment on my blog and I’ll be more than happy to write even more on the subject.

Enough about the book – Where’s my snippet

The second snippet in this series is more of a tease than a full snippet, simply because it’s such an interesting subject, only the book does it true justice with a full working example:

Mecanim state machines

(I still keep typing Mechanim and keep having to delete the H, lol )

For any of you who have played with Mecanim you will have found it to be a great 3D animation system to animate your 3D models using rigs and prepared animations, plus it can blend those animations together to give a more realistic view. At its heart though, Mecanim is nothing more than a very fancy state machine with a wonderful graphical interface.  It was they enhanced in Unity 4.3 to include 2D sprite animation as well.

What you may not realise is that you don’t have to use Mecanim for just animation, you can use it for almost any scenario that requires a state machine, from Game state to even AI machines! Each have their own types of implementations and their own little tips and tricks to make the best of them.

A simple game state machine

The simplest example of a non-animator Mecanim system is a game’s battle state machine, there are a few complexities to the approach (mostly due to the way Mecanim handles current state) that we need to handle but in the end we get a much better system with a easy to manage interface,

If we try to do a simple game state system in code, we usually end up with a complicated mess of switch or if statements all competing to figure out what it supposed to happen in each game update (it’s not always messy but it can easily get that way).

Say we have the current flow:

In normal code we would most likely start with an Enum definition for each state (or just use strings if you’re daring) like this:

enum BattleState
{
    Battle_start,
    Intro,
    Player_attack,
    Opponent_attack,
    Player_dead,
    Opponent_dead,
    Battle_result,
    Battle_over
}

Then in the update look implement something like this:

void Update () {
    //wait loop when a pause is required
    if (timer > 0)
    {
        timer -= Time.deltaTime;
        return;
    }
    // Set the next state;
    currentBattleState = nextBattleState;
    //What to do in the current state and where to go next
    switch (currentBattleState)
    {
        case BattleState.Battle_start:
            playerHealth = 10;
            opponentHealth = 10;
            nextBattleState = BattleState.Intro;
            break;
        case BattleState.Intro:
            timer = 3f;
            nextBattleState = BattleState.Player_attack;
            break;
        case BattleState.Player_attack:
            if (Input.GetKeyDown(KeyCode.Space))
            {
                opponentHealth -= 5;
                if (opponentHealth <= 0)
                {
                    nextBattleState = BattleState.Opponent_dead;
                }
                else if (playerHealth <= 0)
                {
                    nextBattleState = BattleState.Player_dead;
                }
                else
                {
                    nextBattleState = BattleState.Opponent_attack;
                }
            }
            break;
        case BattleState.Opponent_attack:
            playerHealth -= Random.Range(0, 10);
            if (opponentHealth <= 0)
            {
                nextBattleState = BattleState.Opponent_dead;
            }
            else if (playerHealth <= 0)
            {
                nextBattleState = BattleState.Player_dead;
            }
            else
            {
                nextBattleState = BattleState.Player_attack;
            }
            timer = 1f;
            break;
        case BattleState.Player_dead:
        case BattleState.Opponent_dead:
            timer = 3f;
            nextBattleState = BattleState.Battle_over;
            break;
        case BattleState.Battle_result:
            timer = 2f;
            nextBattleState = BattleState.Battle_over;
            break;
    }
}

As soon as we want to add a little change, we end up trawling through all the paths to check what state we are in, to the state we need to be and what flags we need to set. (in my experience you also end up with unintended effects that cause you to add more states to track different conditions and usually making things worse or harder to diagnose in the future )

You can see this more fully by checking out the OldStyleStateMachine.cs script attached to the BattleStateMachine GameObject in the example scene with the downloadable project. Just turn it on to see the basic example. (ensuring you turn off the Mecanim script)

A very basic example indeed, but just imaging it 50x bigger with several complicated paths, each part of the code needing to know everything around it in order to make the right decision, now you may start to see the larger picture.

So what has Mecanim ever done for us?

Using Mecanim itself to implement the flow of the state machine is very simple, in fact we’ve practically drawn it above. Taking our outline in the image, we can convert this to a Mecanim design board looking something like this:

We’ve replicated the flow of our game state design as empty Mecanim states and added some parameters to the Animator to track health, whether we are in battle and a trigger to denote an attack has occurred.  So what about the code, how does using Mecanim simplify things?

Putting it simply it removes all the choice and decision from code, that is now all moved to Mecanim and we simply need to tell the Animator when something changes.  If we then apply this Animator to the BattleStateMachine GameObject in our example scene, using the above controller assigned to the controller property, we can then look to exploit it through script.

Even better, any state can be quickly joined to any other state with a simple and quick transition

Still using our Enum as before (as it is the code’s guide to what is going on), our code is simply reduced to:

void Update()
{
    currentBattleState = battleStateHash[battleStateMachine.GetCurrentAnimatorStateInfo(0).nameHash];
    switch (currentBattleState)
    {
        case BattleState.Battle_start:
            playerHealth = 10;
            opponentHealth = 10;
            battleStateMachine.SetInteger("Player_health", playerHealth);
            battleStateMachine.SetInteger("Opponent_health", opponentHealth);
            battleStateMachine.SetBool("Battle_inprogress", true);
            break;
        case BattleState.Player_attack:
            Attacking = false;
            if (Input.GetKeyDown(KeyCode.Space) && !keyPressed)
            {
                keyPressed = true;
                opponentHealth -= Random.Range(3, 5);
                battleStateMachine.SetTrigger("Attack");
            }
            battleStateMachine.SetInteger("Opponent_health", opponentHealth);
            break;
        case BattleState.Opponent_attack:
            keyPressed = false;
            if (!Attacking)
            {
                playerHealth -= Random.Range(0, 10);
                battleStateMachine.SetTrigger("Attack");
                Attacking = true;
            }
            battleStateMachine.SetInteger("Player_health", playerHealth);
            break;
        case BattleState.Battle_result:
            battleStateMachine.SetBool("Battle_inprogress", false);
            break;
    }
}

This removes a lot of the complexity for the state machine from the code and you simply need to state what you want to happen at each gate.

There are a few gotcha’s we need to be aware of which are mainly to do with the power and speed of Mecanim:

  • Update runs faster than Mecanim, so if you have singular actions in your update code you need to cater for this (as shown by a few private boolean’s)
  • Inputs (like keyboard) can be true for several update loops (hence the boolean’s for keyboard input)
  • Mecanim will do EXACTLY what you tell it to, which can cause confusion and multiple paths can be true at the same time!

Now that’s almost the end of the story as you will note that at the beginning of each update we get the Animator’s current state.  The problem is that Mecanim currently does not work with state names at all, it actually uses a hashing mechanism to track not only the current state the Animator is currently positioned at but also the exact point in the states life it’s currently up to (useful if you are doing blending or animation, less useful if you just want the state).  As we like to work with names (optional of course, you can work with just the hashed numbers) we need to cache them, in the script I create a dictionary and cache them with the script starts, if you wished, you could cache them at build time.

In testing I’ve not seen much of a hit but if you have a lot of states, it may be something to consider caching state names at build time.

So in the example the Start and Awake methods look as follows, to cache the state names and hash keys:

Animator battleStateMachine;
private Dictionary<int, BattleState> battleStateHash = new Dictionary<int, BattleState>();
private BattleState currentBattleState;

void Awake()
{
    //Get the Animator state machine, error if none found on this GO
    battleStateMachine = (Animator)GetComponent(typeof(Animator));
    if (!battleStateMachine || !battleStateMachine.runtimeAnimatorController)
    {
        Debug.LogError("State Machine Missing or not configured)");
    }
}

void Start () {
    //Cache all the hashes of the States in our State Machine (case sensitive!)
    foreach (BattleState state in (BattleState[])System.Enum.GetValues(typeof(BattleState)))
    {
        battleStateHash.Add(Animator.StringToHash("Base Layer." + state.ToString()), state);
    }
}

It’s a little hassle but as it’s such a minor thing that’s easy to work with.

As you can hopefully see this is very powerful and makes complex decisions through Mecanim a lot easier to implement, just be aware that with great power comes great responsibility! .

This article truly only scratches the surface of what is possible with Mecanim using it as a pure state machine, in the book we explore a full system such as the above and even delve in to a basic AI system implemented through Mecanim

We hope you enjoyed the show

I do hope you like this little snippet, just one of (hopefully) many little break out sections from the book.  These snippets do have a lot more detail as I have more space to work with (it is really surprising how restricting 500+ pages gives you ) but everything you need to know is covered in each section.

I’m so glad this title is finally published and out there for people to grab, any queries / questions / thoughts, just drop me a line using the Contact page on my blog and I promise to get back to you.

The sample project and code for this snippet can be found here: – Mecanim State Machines.zip

Wait, There’s more!

Now that Unity has finally pulled back the covers on the new shiny and advanced UI system, I can formally announce that I’m already most of the way through on my second title with Packt which will be an in depth overview and guide to the new UI system.

So if you want a leg up on how to make the best out of the new UI system and learn some cunning tips and tricks from many months of suffering through the beta then this will be a title for you.

If you want more details or have any particular requests just let me know, I’ll do my best to cover as much as I can (Although being me, I’m already over budget in a lot of areas with more detail than you could ever need but that won’t stop me )

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Simon Jackson
Architect ZenithMoon Studios
United Kingdom United Kingdom
Long time game developer / IT maniac.
By day working as a lowly Technical Architect for a healthcare software firm, by night Masquerading as the Master Chief of ZenithMoon Studios.
 
At heart I'm a community developer breaking down lots of fun and curious technologies and bringing them to the masses.
 
I'm also a contributor to several open source projects, most notably the AdRotator advertising rotator project for Windows and Windows Phone.
 
After a hefty break with Windows Phone and Windows 8 I'm heading back to my XNA roots to show how beginners and current XNA devs can take their skills forward with XNA's bleak future.
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalYvan Rodrigues28-Sep-14 8:11 
BugHashes aren't guaranteed unique PinmemberChad3F5-Sep-14 9:56 
AnswerRe: Hashes aren't guaranteed unique PinmemberSimon Jackson5-Sep-14 13:07 
BugCan we delete this draft please? PinmemberSimon Jackson29-Aug-14 22:43 
GeneralRe: Can we delete this draft please? PinprofessionalRage1-Sep-14 3:52 
GeneralRe: Can we delete this draft please? PinmemberSimon Jackson1-Sep-14 4:45 
GeneralRe: Can we delete this draft please? PinprofessionalRage1-Sep-14 4:52 
GeneralRe: Can we delete this draft please? PinmemberSimon Jackson1-Sep-14 4:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140926.1 | Last Updated 2 Sep 2014
Article Copyright 2014 by Simon Jackson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid