{"id":92975,"date":"2021-11-29T16:50:54","date_gmt":"2021-11-29T16:50:54","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=92975"},"modified":"2021-11-29T16:50:54","modified_gmt":"2021-11-29T16:50:54","slug":"unity-state-machine-behaviours","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/featured\/unity-state-machine-behaviours\/","title":{"rendered":"Unity state machine behaviours"},"content":{"rendered":"<p>An animation in a Unity project is an opportunity to perform many additional actions on top of the animation itself. These actions can be anything the developer desires, including allowing specific user actions or changing variables to your needs. Some time back, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/using-c-functions-animations\/\">Unity&#8217;s animation events<\/a> were demonstrated as a tool for accomplishing some of these goals. They&#8217;re great for performing functions at specific points in an animation but not as helpful if there&#8217;s something more general you want to do at any point in the animation. Enter Unity State Machine Behaviours, a class of script that works specifically with the Unity Animator system.<\/p>\n<p>Working alongside the Animator, State Machine Behaviours lets you define actions for entering, exiting, and updating various animation states. This means you can have code that runs specifically during an object&#8217;s idle state and another script that only executes during the same object&#8217;s walking state. Not only does this allow you to perform specific actions during certain states, but it&#8217;s also a great way to keep your code organized too. Another great benefit is the ability to reuse these scripts on other state machines, so you don&#8217;t have to worry about a script being tied to a single object. As usual, the best way to see the benefits is to see it all in action. Follow along to see how to use these scripts in a project, and by the end, you&#8217;ll have a Unity project with a character that performs different actions based on whether it&#8217;s in an idle or walking state.<\/p>\n<p>There are many unique assets in this project, so to focus on the code and how it works, a sample project has been created to give you a starting point if you choose to follow along. Additionally, there is a completed version with all the code included to view and modify as you see fit. Links for these projects are listed below. Open the Unity Hub app and click the Add button, find the folder containing the Unity project, and choose <em>Select Folder<\/em>.<\/p>\n<p>Codeless project: <a href=\"https:\/\/github.com\/prof-smash\/SimpleTalk-StateMachines-Codeless\">https:\/\/github.com\/prof-smash\/SimpleTalk-StateMachines-Codeless<\/a><\/p>\n<p>Complete project: <a href=\"https:\/\/github.com\/prof-smash\/SimpleTalk-StateMachines-Complete\">https:\/\/github.com\/prof-smash\/SimpleTalk-StateMachines-Complete<\/a><\/p>\n<h2>Project overview<\/h2>\n<p>First, take a brief look at what&#8217;s included in the sample project. Upon opening the project, you should be greeted with a scene with a blue bot character standing on a plane. If you don&#8217;t see this, find the <em>SampleScene <\/em>asset in your <em>Assets <\/em>window and open it. This blue bot has an animator tied to it that allows playing idle and walking animations. As it walks, it will leave markers on the floor to indicate where it has been. The markers come from the prefab simply named <em>Marker<\/em> in the <em>Prefabs <\/em>folder.<\/p>\n<p>Additionally, some text displaying the number of walks the bot has taken is in the upper left corner of the screen. The exact location and appearance of the text may vary depending on your display, so feel free to adjust its position, color, and other properties in the <em>Inspector<\/em>. Finally, though you can&#8217;t see it at the moment, a nav mesh has been set up, which defines the area the bot can walk. If you wish to see this nav mesh, click the <em>Navigation <\/em>tab in the <em>Inspector <\/em>area (if you don&#8217;t see this tab, go the top menu and choose <em>Window-&gt;AI<\/em>-&gt;<em>Navigation<\/em>), and you should see a blue area in the <em>Scene<\/em> window that defines the nav mesh. All other assets in the <em>Assets <\/em>window include the texture for the marker and animations for the bot.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-92976\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/11\/a-screenshot-of-a-video-game-description-automati.jpeg\" alt=\"Image showing the project scene with the nav mesh shown.  Blue rectangle with a bot.\" width=\"970\" height=\"593\" \/><\/p>\n<p><strong>Figure 1: The project scene with the nav mesh shown<\/strong><\/p>\n<p>Those animations are brought together via the <em>bot_anim_controller <\/em>asset in the <em>Animations <\/em>folder. If you select this asset and open the <em>Animator <\/em>window (found by going to <em>Window-&gt;Animation-&gt;Animator<\/em>), you can see the animator immediately launches the bot into the idle state. If you can&#8217;t see this right away, you may need to navigate the Animator window using the Alt key plus mouse button to pan the view. The arrows pointing to and from the idle and walk states are transitions, with each given a condition that must be met for the transition to occur. In this case, two booleans named <em>idle<\/em> and <em>walk<\/em> are set to control when the bot is in the idle or walk state, respectively. These two states are where the scripts will be attached to give the bot unique behaviours based on the state.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-92977\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/11\/graphical-user-interface-description-automaticall.jpeg\" alt=\"Image showing bot_anim_controller, showing animation states, transitions, and booleans. Buttons for entry, idle, and walk\" width=\"889\" height=\"354\" \/><\/p>\n<p><strong>Figure 2: bot_anim_controller, showing animation states, transitions, and booleans<\/strong><\/p>\n<p>If you click on the idle state, you should see a button in the <em>Inspector <\/em>window that says <em>Add Behaviour<\/em>. Click this button, then click <em>New Script, <\/em>then give the script the name <em>IdleStateBehaviour<\/em>. The newly created script will be immediately added to the idle state, with the asset itself being placed in the <em>Assets <\/em>folder. Repeat this process for the walk state, this time calling the script <em>WalkStateBehaviour<\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-92978\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/11\/graphical-user-interface-application-description.jpeg\" alt=\"Image showing the location of the Add Behaviour button\" width=\"591\" height=\"385\" \/><\/p>\n<p><strong>Figure 3: Adding a new state machine behaviour script<\/strong><\/p>\n<p>By using this specific button to create the script, Unity will know to make this a script that inherits from the <em>StateMachineBehaviour <\/em>class, so we won&#8217;t have to worry about changing that. It also sets up a template for the state machine behaviour script that gives you all the methods you&#8217;ll need to code state behaviours. Double click the <em>IdleStateBehaviour <\/em>script to open it in Visual Studio and set up the first state behaviour.<\/p>\n<h2>IdleStateBehaviour code<\/h2>\n<p>As promised, the <em>IdleStateBehaviour <\/em>script is inheriting from <em>StateMachineBehaviour <\/em>from the start. Additionally, there are five methods, each with brief documentation of how they work. For this project, the focus will be placed on <code>OnStateEnter<\/code>, <code>OnStateUpdate<\/code>, and <code>OnStateExit<\/code>. As the names imply, these methods perform code based on when the bot enters or exits its idle state and is currently in the idle state. But that&#8217;s not all! Like the traditional <em>MonoBehaviour <\/em>scripts Unity users are used to, you can define variables that can be edited in the Unity editor right here in this script. These variables are a good place to start with the coding process. So, just above <code>OnStateEnter<\/code>, enter the following:<\/p>\n<pre class=\"lang:c# theme:vs2012\">[Range(2.0f, 10.0f)]\r\npublic float waitTime = 5f;\r\npublic GameObject marker;\r\nprivate float timeTillMove = 5f;\r\nprivate Vector3 stopPosition;<\/pre>\n<p>The first two variables will be for you to decide in the editor, with <code>waitTime<\/code> defining how much time there is between walks. Next is <em>marker<\/em>, which will be what the bot places down upon leaving its position. Finally, <code>timeTillMove<\/code> will be the actual timer that counts down between walks, with the time in question coming from <code>waitTime<\/code>, and <code>stopPosition<\/code> is the location the bot stops at, which will be used to place markers.<\/p>\n<p>Moving on to <code>OnStateEnter<\/code>, you&#8217;ll notice that an <code>Animator<\/code> is set in the method&#8217;s parameters. In fact, this is true of all the methods in the script. This will be helpful as it allows you easy access to the bot&#8217;s animator and the methods and data associated with it. Let&#8217;s put it to use along with some additional code:<\/p>\n<pre class=\"lang:c# theme:vs2012\">animator.SetBool(\"walk\", false);\r\nstopPosition = new Vector3(animator.transform.position.x, \r\n         0.01f, animator.transform.position.z);<\/pre>\n<p>Recall the booleans that control when transitions occur in the bot&#8217;s animator. When the bot is in its idle state, you wouldn&#8217;t want it to start walking in place. To prevent this, set the <em>walk <\/em>bool in the animator to false the moment the bot enters its idle state. You&#8217;ll also go ahead and get its current position for leaving a marker later on when exiting the idle state. The Y value of <code>stopPosition<\/code> will be set to 0.01 to allow the marker to be slightly above the floor so you can see it. Without this change, the marker will \u201cblend\u201d with the floor and stutter or even be impossible to see.<\/p>\n<p>Let&#8217;s move on to <code>OnStateUpdate<\/code>, which will execute continuously so long as the bot is in its idle state. This method is perfect for counting down the seconds before moving again. To execute this, enter the following:<\/p>\n<pre class=\"lang:c# theme:vs2012\">timeTillMove -= Time.deltaTime;\r\nif (timeTillMove &lt;= 0)\r\n{\r\n\ttimeTillMove = waitTime;\r\n\tanimator.SetBool(\"walk\", true);\r\n}<\/pre>\n<p>This code is pretty straightforward. The <code>timeTillMove<\/code> variable is subtracted until it reaches zero, at which point the timer is reset using <code>waitTime<\/code><em>,<\/em> and the <code>walk<\/code> bool in the animator is set to true. Notice that the timer will not count down until this state is active again, so there&#8217;s no need to worry about it counting down too early.<\/p>\n<p>Finally, let&#8217;s implement the code for <code>OnStateExit<\/code>. It consists of a single line that spawns the <code>marker<\/code> object into the world at the position recorded in <code>stopPosition<\/code>.<\/p>\n<pre class=\"lang:c# theme:vs2012\">Instantiate(marker, stopPosition, Quaternion.identity);<\/pre>\n<p>That&#8217;s all the code for <em>IdleStateBehaviour<\/em>. Now it&#8217;s time to move on to the code for the bot&#8217;s walk state.<\/p>\n<h2>WalkStateBehaviour code<\/h2>\n<p>Like the <em>IdleStateBehaviour <\/em>script, this script will keep its focus on <code>OnStateEnter<\/code>, <code>OnStateUpdate<\/code><em>, <\/em>and <code>OnStateExit<\/code>. However, the script as a whole will be slightly more involved than <code>IdleStateBehaviour<\/code> as it will search for objects in the scene, find a random position for the bot, and update statistics. It will also utilize the <code>UnityEngine.UI<\/code> and <code>UnityEngine.AI namespaces<\/code>. With that in mind, let&#8217;s set up the using statements now.<\/p>\n<pre class=\"lang:c# theme:vs2012\">using UnityEngine.UI;\r\nusing UnityEngine.AI;<\/pre>\n<p>Like before, there are also a few variables to declare before getting into the meat of the code, with one of them being a value you&#8217;ll set in the editor.<\/p>\n<p>[Range(2.5f, 7f)]<\/p>\n<p>public float maximumWalkingDistance = 4f;<\/p>\n<p>private Text walkText;<\/p>\n<p>private NavMeshAgent agent;<\/p>\n<p>private int walks = 0;<\/p>\n<p>Of particular note, here are the <code>walkText<\/code> and <em>agent <\/em>variables. The <code>walkText<\/code> variable will be given string data to display in the UI seen earlier in the scene. It will be updated with the values of <code>walks<\/code> whenever the bot has finished taking its short walk. Meanwhile, the <code>agent<\/code> variable will contain the <em>Nav Mesh Agent <\/em>component on the bot object. As you&#8217;ll see later on, we get the agent by using <code>GetComponent<\/code> whenever the bot enters the walk state. Why wouldn&#8217;t you just set <code>agent<\/code> in the editor? This is because scripts inheriting from <code>StateMachineBehaviour<\/code> do not allow you to pass in <code>gameobject<\/code> data into the script before runtime, including their components. Because the state machine behaviour lives within the animator asset, as opposed to within the scene, we cannot link objects within the scene to the script. Thus, we work around that by using methods like <code>GameObject<\/code><em>.Find<\/em> or reference the <code>gameobject<\/code> (and thus its components) where the animator is attached.<\/p>\n<p>Next, the code for <code>OnStateEnter<\/code><em>:<\/em><\/p>\n<p>animator.SetBool(&#8220;idle&#8221;, false);<\/p>\n<pre class=\"lang:c# theme:vs2012\">walkText = GameObject.Find(\"WalksText\").GetComponent&lt;Text&gt;();\r\nagent = animator.GetComponent&lt;NavMeshAgent&gt;();\r\nagent.destination = RandomPosition(animator.transform.position, \r\n             maximumWalkingDistance, -1);<\/pre>\n<p>First, we make sure the bot cannot accidentally go back to its idle animation while walking. Then you find the <code>WalksText<\/code> object in the scene and tell the script to use that for <code>walkText<\/code>. You then set up the <code>NavMeshAgent<\/code>, including its destination. The destination is found using the <code>RandomPosition<\/code>, a custom method for finding a random point in the Nav Mesh to walk. Now would be an excellent time to create the <code>RandomPosition<\/code> method.<\/p>\n<pre class=\"lang:c# theme:vs2012\">Vector3 RandomPosition(Vector3 origin, float distance, int layerMask)\r\n{\r\n\tVector3 randomDirection = Random.insideUnitSphere * distance;\r\n\trandomDirection += origin;\r\n\tNavMeshHit navHit;\r\n\tNavMesh.SamplePosition(randomDirection, out navHit, distance, \r\n               layerMask);\r\n\treturn navHit.position;\r\n}<\/pre>\n<p>Returning to the pre-generated methods, <code>OnStateUpdate<\/code> will be utilized to check how far away the bot is from its destination. If it has made it to its destination, then the bot will simply return to the idle state, and the process repeats.<\/p>\n<pre class=\"lang:c# theme:vs2012 \">float dist = agent.remainingDistance;\r\n        if (dist != Mathf.Infinity &amp;&amp; agent.pathStatus == \r\n              NavMeshPathStatus.PathComplete &amp;&amp; \r\n               agent.remainingDistance &lt;= 0.01f)\r\n{\r\n            animator.SetBool(\"idle\", true);\r\n}<\/pre>\n<p>After leaving this state and returning to the idle state, the number of walks taken is incremented by one. <code>walkText<\/code><em> is <\/em>updated to reflect this change. That code will be going in <code>OnStateExit<\/code>.<\/p>\n<pre class=\"lang:c# theme:vs2012\">walks++;\r\nwalkText.text = \"Walks Taken: \" + walks.ToString();<\/pre>\n<p>That ends all the code needed for this project. Be sure to save all scripts, then return to the Unity editor for some final tasks.<\/p>\n<h2>Finishing up<\/h2>\n<p>There&#8217;s not much else that needs to be done to complete the project. If it&#8217;s not open already, make sure the <em>Animator <\/em>window is open, and you have the <em>bot_anim_controller <\/em>asset selected. Start by clicking on the <em>idle <\/em>state. Set the time you want the bot to wait before starting its next walk and make sure the script knows where to find the <em>Marker <\/em>prefab.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-92979\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/11\/graphical-user-interface-text-description-automa.jpeg\" alt=\"Image showing how to drag the Marker to the Walk state\" width=\"832\" height=\"470\" \/><\/p>\n<p><strong>Figure 4: Assigning the marker<\/strong><\/p>\n<p>Likewise, select the <em>walk <\/em>state and set your maximum walking distance. If you only want the bot to take short walks, lower the walking distance. For the opposite, raise the <em>Maximum Walking Distance<\/em>. Once you&#8217;ve made your decision, test out the project using the play button at the top. The bot should start going to random spots on the floor, leaving markers along the way, and counting how many walks it has taken. It will continue to do this until you stop running the project.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-92980\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/11\/a-picture-containing-text-businesscard-descripti.jpeg\" alt=\"Image showing the game in action. The bot is walking and leaving X where it stops.\" width=\"882\" height=\"456\" \/><\/p>\n<p><strong>Figure 5: The project in action<\/strong><\/p>\n<h2>Conclusion<\/h2>\n<p>As stated before, the beauty of these scripts is that they can be assigned to any animation state. So you can have multiple objects use the code you&#8217;ve just written, even if they have different animators powering them. You just need to be mindful of items like animator booleans and any additional states within the animator. Additionally, these can also be used to give an object specific instructions in an organized, easy to access way. Whether it&#8217;s for a simple prototype or a large, complex project, state machine behaviours can be a valuable tool to help you achieve the goals of your Unity project.<\/p>\n<p>As stated before, the beauty of these scripts is that they can be assigned to any animation state. So you can have multiple objects use the code you&#8217;ve just written, even if they have different animators powering them. You just need to be mindful of items like animator booleans and any additional states within the animator. Additionally, these can also be used to give an object specific instructions in an organized, easy to access way. Whether it&#8217;s for a simple prototype or a large, complex project, state machine behaviours can be a valuable tool to help you achieve the goals of your Unity project.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Video games would not be much fun without animation. In this article, Lance Talbert demonstrates Unity state machine behaviours to define code running during specific animation states.&hellip;<\/p>\n","protected":false},"author":317499,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538,53],"tags":[5134],"coauthors":[52549],"class_list":["post-92975","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-featured","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/92975","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/317499"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=92975"}],"version-history":[{"count":4,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/92975\/revisions"}],"predecessor-version":[{"id":92982,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/92975\/revisions\/92982"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=92975"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=92975"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=92975"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=92975"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}