Easy State Documentation

Easy State Icon


Table of Contents


Introduction

Easy State at its core is a programmer centric visual state machine editor. It tries to take care of all the repetitive code that comes with handling state machines but allow the programmer the most flexibility possible. As such, Easy State is more than a strict state machine as it also allows behavior tree like designs as well as utility based nodes. With the 3rd version of this tool Easy State has gotten an upgrade incorporating many quality of life changes and user suggested features yielding the most powerful flexible Easy State version yet. To get a quick overview jump to the General Workflow Overview section.

Lexicon

Here is a list of common words and their meanings in the EasyState universe.

Core Components

Data Type

DataType

In essence a data type is just a data container. Think of it like a blue print for your state machine. The way Easy State is designed each state machine is strongly typed by the DataType you create. If that doesn't make sense right now don't worry, it will later.

Action

This defines what your state machine does. If your state machine needs to increment a score or tick a timer it should be done inside of an Action.

Condition

Conditions dictate the flow of your state machine. They are boolean and therefore always evaluate as true or false.

Evaluator

Evaluators are used when you would rather score a transition instead of returning a simple true or false. This can be useful when trying to decide between more than two possibilities.


Designer Terms


Designer

The Designer is an Unity Editor window that you will use to create and edit designs.

Design

Designs are created in the Designer. A design defines a graph of nodes and connections that can be translated into a behavior to be consumed in Unity if the design is valid.

Node

A node or state references a point in the design graph where actions can be executed or update cycles delayed before traversing to the next node.

Connection

Connects two states or nodes together. There are two types of connections.

Update Cycle

Refers to when a state machine is told to update. Typically the first time a state machine is told to update it will start by updating the entry node(first node of the design) and continuing to update nodes until it updates a node that has a cycle type that is not Pass Through(more on this) or a node that is a leaf node. Leaf nodes automatically loop back to the entry node after being updated.


1. Editing


This section covers how to create components and use them inside the designer.

1.1 Creating Core Components


Create Components via Asset Menu

Start off by creating a Data Type. To do this you can either open the Assets Menu and navigate to EasyState/New Data Type or alternatively right clicking in the Unity's Project window and navigating to EasyState/New Data Type.

Create New Data Type

When you click on the New Data Type button you will be presented with a File Explorer popup to allow you to select where you want to save the new Data Type file. Once a location has been selected Easy State will generate two files.

Once created the new Data Type will be added to the Easy State Assets menu list. In the image above we have created a Data Type called 'DataType1'. You will notice this menu allows you to also create Actions, Conditions, and Evaluators. If created at the root level it will create a template script that will not compile until you manually set the Data Type that the component uses. If instead you navigate to your created Data Type menu item you can create components that automatically utilize your created data type.

Create New Data Type

Note: Easy State scans Unity's Assemblies for newly added core components. As such you can rename or delete generated scripts as you please but you should not edit the [EasyStateScript] attribute, as this is how the framework keeps track of which components have been added/moved/or deleted. Additionally be aware that deleting components can invalidate designs and thereby delete their associated behaviors that may be being used by Easy State Machine components in your Unity Scene.

Create Components Inside Data Data Type

You can add components to a data type by adding components directly to the DataTypeFunctionSet inside of the Data Type file inside the constructor. There are several key methods to add functions or component to a Data Type's set.

Actions You can add actions to the set by utilizing the AddAction method. This method requires an Action<T> where T is the Data Type and a unique name for the action.

Add action to data type

There is also an AddLoggingAction method that makes it easy to create logging actions for diagnostic purposes.

Conditions There are multiple ways to add conditions to a set. If a field or property is of type bool it will be automatically added to the function set and will be able to be used in the designer.

Add implicit conditions to data type

You can also define a conditional function using the AddCondition

Add conditional function to data type

Or use a fluent API approach:

Add conditional function to data type with fluent API

There are a few fluent API methods available to make simple comparison fast and easy to read.

Evaluator Evaluators can be added by using the AddEvaluator method. This method requires a Func<T,float> where T is the Data Type being used and the float that is returned is the score your evaluator yields. This method also requires a unique evaluator name.

Add conditional function to data type with fluent API

Inheritance

Inheritance is supported! This means that if you inherit from a Data type that you created, all the actions, conditions, and evaluators will be able to be used from the parent data type in the designer.

Tips

1.2 Designer


Designer Tab

The window can be found under the 'Window/EasyState/Editor' tab at the top of Unity.The window is a draggable/dockable and behaves in the same way as other Unity windows like 'Scene' and 'Game' work.

Controls

Pan : Ctrl + Left Mouse Zoom: Mouse wheel Select: Left Mouse Multi-Select Left Click Background + Drag OR Ctrl + Left Click Open Element Details : Double Left Click Drag Element : Left Click Element + Drag Save : Ctrl + S Context Menu : Right Click Delete Element : Del Duplicate Element : Ctrl + D Rebuild Editor : Ctrl + R Validate Design : Ctrl + V Group Nodes : Ctrl + G

Getting Started

When first opening the designer you will be greeted with a loader panel that allows you to manage your designs. Assuming you have created a Data Type already it should look something like this :

Designer Loader Panel

Once a design has been created your design will start off with an Entry Node already created. Entry nodes act as the starting point of all designs.

New Design Tab

With the new design open you can start adding nodes by right clicking on the designer background and picking the node preset you want.

Nodes

When you right click the background of the designer you are greeted with a context menu that has by default six options.

Context Menu

The top 4 options are all just different node presets(More on these later). While the bottom two represent different node types completely. If we create a state node and double click on it, it will open the detail panel for the node.

Node details

Node Settings Foldout

When the settings foldout is expanded you will see that nodes have three dropdowns that greatly alter the behavior of the node.

Node settings

Node Description Foldout

All of the above can be a lot to keep straight in your head. That is why each node has a dynamic node description that updates based on its settings to tell you how a particular node will behave.

Node description

Actions Foldout

A list of all actions that will be executed in this node. Display will change based on the Action Execution Type in the settings foldout.

Connections Foldout

A list of exit connections for the selected node. You can add/remove and set the destinations of the connections from this foldout.

Connections

Double click on the triangle handle of a connection to open up its details panel.

Connection Details

As visible from the details panel above you can make a conditional expression of almost arbitrary complexity. To add a connection right click on a node or click on the split icon inside the nodes. To delete a node you can either delete it from the Node/Connections foldout or right clicking on the connection handle or pressing "del" when the connection is selected.

Groups

Create a group by selecting more than one node or note and either hitting Ctrl+G or right clicking on a node and selecting "Group Selected". Change the group name by double clicking on the group name in the editor. Nodes and notes can be grouped.

Group

Notes

Note

Jumper Node

Jumper Node

This node allows you to transition from one design to another.

Designs vs Behaviors

A design is a Behavior in progress. The Unity State Machine components can only use Behaviors. Think of Behaviors as validated designs. When working on a design if it is valid, when you save changes to the design you will get a toast in the bottom right of the screen that indicates that a behavior was created or updated. This is a signal that everything in your design is valid. If you want to find out why your design is invalid either click the hamburger button menu at the right of the toolbar or use the shortcut key Ctrl + V to validate your design and see what is missing.

Save Design


2. In Unity


There are two main types of Unity MonoBehaviors that you will be using to interface between Unity and Easy State. The first one is the State Machine component which was created when you first created your Data Type. The second is an optional EventHandler that responds to all of the events generated by the State Machine. You will use this as an alternative to subscribing to events individually on the state machine component itself.

2.1 Easy State Machine

Assuming that you have a valid Design that has been converted into a Behavior the Easy State Machine component will look something like this:

Easy State Machine Component

Note: DataTypeBase the class all Data Types inherit from is itself a ScriptableObject. By default when loading up Easy State, it creates a clone of the Data Type so each state machine is using data for its own instance. This can be changed in the Settings foldout under the "Use instance data" check box. If this setting is unchecked all state machines will use the same instance of the Data Type.

Settings

Note: due to the way the Unity Editor works if trying to assign an event handler manually to a state machine, the event handler must first be on a gameObject in the scene before it can be assigned.

Easy State Machine Component in Play Mode

The Easy State Machine component looks slightly different when the Editor is in play mode.

The Easy State Machine in PlayMode

2.2 Event Handlers

Whether you decide to create an Event Handler or subscribe to the Unity Events individually the events will be the same.

2.3 Easy Refresher

Easy Refresher component

This component is a singleton object that will be created at runtime if one does not already exist in the scene. The picture above shows the component while in Play Mode. When in play mode it displays various stats about how many state machines it is updating. Part of a state machine's initialization process is to register itself with this component. Once registered the Easy Refresher component orchestrates all the scheduled updates for all the state machines in the scene. This has various benefits the main one being that instead of 2000 MonoBehavior's update methods being invoked you have one MonoBehavior that handles the update.

2.4 Debugger Window

This window is pretty simple. Before entering play mode, select the state machine from the open scene that you want to debug. Then enter play mode.

Note: while the debugger works at any refresh rate, it may be beneficial to slow down the update process by going to the state machine's inspector and underneath the settings foldout selecting a slower update rate so you have time to inspect the values and update paths.

Debugger Window

Format Data Popups

To format the data popups seen above implement the ICustomDataTypeFormatter interface on your Data Type. Rich text is supported, here is a full list of rich text tags that can be used.


3. Misc


3.1 Settings

The settings asset can be found at EasyState/Resources/Settings.asset. Each setting has a tooltip explaining more about its uses.

Easy Refresher component

3.2 Data

By default data is stored in Assets/EasyStateData folder. This can be changed by updating the settings and changing the "Easy State Data Folder" setting to a new home. This is best changed before you start creating designs and data types. The steps to move the data folder are:

3.3 Easy State v3 vs Easy State v2

Easy State version 3 is incompatible with version 2. But the concepts translate. In version 3 several 3rd party dependencies have been removed and more Unity native features have been leveraged to create a better experience. Essentially, the entire asset was re-written from scratch and is now much more stable and maintainable. Here is a list of new features that are in version 3 that were missing in version 2


4. Tutorials


General Workflow Overview

  1. Create a data type As mentioned in the 1.1 Creating Core Components section, you can create a data type by right clicking in the Project pane

    Create New Data Type

  2. Once that data type is created, open up the designer "Window/Easy State/Designer" and create a design using your new data type.

    Create New Design

  3. Create your design and save it. Make sure you get a "behavior created" or "behavior updated" toast on save.

    Save Design

  4. If you did not get the behavior confirmation message hit Ctrl+V to validate your design and see what the errors are.

    Validate Design

  5. Correct any errors and retry saving.

  6. Create an empty gameObject in your scene and attach a {YourDataType}StateMachine component to that gameObject.

    Create component

  7. Create a new data type asset or select an existing asset and set the "Data" field of the component.

    Create component

  8. Select an Update Rate

  9. Click play and your state machine should be running!

Create Your First State Machine

We are going to create a state machine that logs "Heads" or "Tails" to the console randomly. This will help cement the ideas in the above workflow section.

Steps

  1. Create a new folder in your Assets folder titled "FirstDataType".

  2. In that folder right click and select "Create New Data Type"

    Create New Data Type

  3. Select your newly created folder as a destination and name the data type "FirstDataType".

    Create New Data Type

  4. Double click on the newly created data type to open it up in your script editor.

  5. Let's add our random condition and two logging actions to our data type.

    Data Type script

  6. Open up the designer, if the design loader isn't visible click the plus icon in the left column to open it up. In the "Create New Design" section, select "FirstDataType" as your Data Type and create a design named "First Design".

    Create New Design

  7. Create the design, and add two state nodes to the design by right clicking the background and selecting "Create State Node". Name one state "Heads" and the other "Tails".

    Create New Design

  8. Click the split icon on the entry node to create a connection, then click on the heads node to make the connection.

  9. Double click on triangle handle in the new connection to bring up the connection details panel.

  10. Select the random condition as the connection's condition.

Create New Design

  1. Double click on the entry node to bring up its details panel and set the fallback connection to the "Tails" node.

Create New Design

  1. Double click on the Heads node and expand its actions foldout. Add the action "Log Heads".

Create New Design

  1. Add the "Log Tails" action to the "Tails" node.
  2. Hit Ctrl+S to save the design and make sure you receive the "Behavior Created" confirmation toast at the bottom right of the screen.

Save Design

  1. If you did not see this confirmation, hit Ctrl+V to validate your design and fix whatever errors are present. Then try saving again.
  2. Create a new gameObject and add a FirstDataTypeStateMachine component to it.
  3. Create a new Data Type asset by clicking the "Create New" button and saving it into your "FirstDataType" folder.
  4. Set the "Data" reference to the newly created Data Type asset.
  5. Enter play mode. With your new gameObject selected click the "Update" button on the state machine component to update it. It should be looking something like this :

First Data type component

  1. Check the console! You should be getting messages!

First Data type component

Where to Go from Here

Congratulations on your first operating state machine. From here you can fiddle around with various settings like refresh type. Maybe use the event handler to respond to different points in the update cycle. In the Samples folder there is a Unity package that can be imported into a fresh project. This sample includes this state machine you just made as well as several others showcasing various aspects of Easy State functionality.There will be some video tutorials posted on YouTube where you can find a video version of the above tutorial and more.

Note: Video tutorials will refer to this as EasyState v3. The other tutorials are for older versions of Easy State. They may be helpful to fill in the gaps as the interface and workflow has changed but the core concepts are the same.