{"id":110,"date":"2022-06-13T16:25:17","date_gmt":"2022-06-13T20:25:17","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/?p=110"},"modified":"2022-06-13T16:31:22","modified_gmt":"2022-06-13T20:31:22","slug":"day-20-scene-management","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/?p=110","title":{"rendered":"Day 20: Scene Management"},"content":{"rendered":"\n<p>Today we took a look at the Scene Manager, which allows us to make the jump out of our scene and into new ones.  This introduces some challenges when we want to retain information or objects between these scenes, and so we look at how we can use the singleton pattern to help us manage this process.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h3 class=\"wp-block-heading\">Part 1 &#8211; Scenes<\/h3>\n\n\n\n<p>In the early days of gaming, developers would divide a game up into small chunks called&nbsp;<strong>\u201clevels\u201d<\/strong>.&nbsp; &nbsp;While they were useful for players to measure their progress, their real purpose was to minimize the amount of data a game had to hold in memory in order to be playable.&nbsp; &nbsp;Even today, as games allow us to explore cities, countries, worlds, galaxies\u2026 the \u201clevel\u201d is commonly used as a way to divide up content.&nbsp; It would be silly to hold the final boss battle of a game in active memory when a player is hours away from encountering it.&nbsp; &nbsp;Each draw call would have to consider every polygon in the entire game, every object and behavior would have to be set just so.&nbsp; In short, it would be chaos.&nbsp; Computationally expensive chaos.&nbsp; Why spend all of those cycles considering things that are hours away from use?<\/p>\n\n\n\n<p>So instead we break up our game into smaller chunks and these load into memory when they are needed, hence the \u201cloading\u201d screen that so many games have.&nbsp; (You may be thinking \u201cbut what about sandbox games?\u201d&nbsp; Well, those are divided into smaller areas as well \u2013 and not just areas, even objects inside those areas have various levels of detail that can be called up.&nbsp; The loading of that content happens in the background, and will swap places with a lower quality model when ready, usually fading between the two so as to disguise the effect and not distract the eye.&nbsp; Most often this happens in the distance, and the game is using predictive algorithms to determine which content you are most likely to need yet and start to load it.)<\/p>\n\n\n\n<p>When Unity was first created, they&nbsp;also included a level system, which became known as&nbsp;<strong>Scenes.&nbsp;&nbsp;<\/strong>You will recognize this today as the format that we save our files in.&nbsp; Scenes have evolved to be much much more than just game levels, and are frequently seen used for other cases such as UI screens\/menus, code and asset loading, and animated cutscenes.&nbsp; Sometimes entire games will be saved as scenes.&nbsp; &nbsp;Projects (the things we open when we launch Unity, and where our Assets and such are stored) can contain multiple scenes, and when we build our game we will designate which of the scenes we create will be included in the build.&nbsp; (This is particularly useful when you\u2019re working on branches or variations of your game \u2013 you can simply swap one out for another if you need to!)<\/p>\n\n\n\n<p>For our in-class demo, we changed the name of our main game scene from the Unity default \u201cSampleScene\u201d to &#8220;Level01&#8221;.  We also created a new scene (&#8220;StartMenu&#8221;) to house our UI, and a quick small game scene we named &#8220;Tutorial&#8221;.   <\/p>\n\n\n\n<p class=\"has-very-light-gray-background-color has-background has-small-font-size\"><strong>NOTE <\/strong>\u2013 Don\u2019t worry too much today about the buttons, we will take a deeper dive into the UI system in the next class.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 2 &#8211; Setting up Buttons<\/h3>\n\n\n\n<p>Unity\u2019s UI buttons are easy to generate by going to&nbsp;<strong>Create &gt; UI &gt; Button<\/strong>&nbsp;or in the top menu&nbsp;<strong>GameObject &gt; UI &gt; Button<\/strong>.&nbsp; This will create a new button object as a child of the Canvas object (which will also be created if you don\u2019t already have one).&nbsp; Buttons consist of two parts, the&nbsp;<strong>Button<\/strong>&nbsp;object, and a child&nbsp;<strong>Text<\/strong>&nbsp;object.&nbsp; &nbsp;The Button object has two Components to note \u2013 the \u201cImage (Script)\u201d component where you define the look of your button, and the \u201cButton (Script)\u201d component where you can define the behaviors and transitions of the buttons.&nbsp; The default button transition is a color tint, but If you have pre-made images for your button states, you can use the \u201cSprite Swap\u201d transition to show them.&nbsp; The child text object is optional.<\/p>\n\n\n\n<p>Now for the complicated part \u2013 adding an action to your button.&nbsp; Buttons have an \u201cOnClick()\u201d message that they send when a user clicks them but accessing them is not as simple as accessing collider messages.&nbsp; At the bottom of the \u201cButton (Script)\u201d component you will see an \u201cOnClick()\u201d panel that is empty.&nbsp; If you click the \u201c+\u201d button it will create a new action.&nbsp; This action requires an object to be associated with it in order to access its scripted functions.&nbsp; &nbsp;You can add a script to the Button object, but then you will have to self-associate the button with itself.&nbsp; &nbsp;<strong>NOTE: The object HAS to be an object currently in the hierarchy.&nbsp; Linking to a prefab does not work.<\/strong><\/p>\n\n\n\n<p>In class, I created an empty game object (&#8220;LevelManager&#8221;) and added a script (also called &#8220;LevelManager\u201d) which contained a single function that we would use to move to the next scene (&#8220;btn_StarTheGame&#8221;).  In the&nbsp;<strong>OnClick( )&nbsp;<\/strong>menu I associate the script object and select a function from the dropdown on the right by going to the specific component \u201cLevelManager\u201d and selecting the function I want to use.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2020\/wp-content\/uploads\/2020\/06\/image-35.png\" alt=\"\" class=\"wp-image-168\"\/><\/figure><\/div>\n\n\n<p class=\"has-very-light-gray-background-color has-background has-small-font-size\"><strong>NOTE:<\/strong>&nbsp; Any function that will be accessed by a Button must be PUBLIC, and VOID.&nbsp; It can only take up to one (1) parameter, that parameter can only be a Float, Int, String, or Object.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 3 &#8211; Scene Manager<\/h3>\n\n\n\n<p>Now that we have a button in place, let\u2019s connect some scenes.\u00a0 I\u2019m going to make a &#8220;LevelManager&#8221; script handle our first scene jump.<\/p>\n\n\n\n<p>First, I have to add a new library to the code, so I start the file by adding this line:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using UnityEngine.SceneManagement;<\/pre>\n\n\n\n<p>Now we can access the scene management commands.&nbsp; &nbsp;I can create a button command, so looking at our button example above to make us jump to the Level01 scene, I create the following in button script.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public void btn_StartTheGame()\n{\n    SceneManager.LoadScene(\"TutorialLevel\");\n}<\/pre>\n\n\n\n<p>Again, please note that this must public and void for the editor to see it.&nbsp; Here, ours takes no arguments.&nbsp; &nbsp; And the command itself is rather simple \u2013 we run the&nbsp;<strong>LoadScene( )<\/strong>&nbsp;function and pass in the string name of our level.&nbsp; &nbsp;We can also pass in an integer that corresponds with the scene index.&nbsp; Speaking of\u2026<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 4 &#8211; Adding Scenes to the Build<\/h3>\n\n\n\n<p>In order to load scenes, Unity has to have those scenes associated with the build.&nbsp; You can do this by going to&nbsp;<strong>File &gt; Build Settings<\/strong>, and dragging the scenes you will be using from the Asset panel into the scene list in Build Settings.&nbsp; Notice that when you add a scene, it also assigns it an indexed location.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2021\/wp-content\/uploads\/2021\/06\/image-8.png\" alt=\"\" class=\"wp-image-125\"\/><\/figure><\/div>\n\n\n<p>Once you have associated the scenes, you can close the window.&nbsp; (There is no save button to worry about, your changes are automatically registered.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 5 \u2013 Staying Alive (DontDestroyMeBro!)<\/h3>\n\n\n\n<p>If we are making a game, chances are we want to do things like keep track of the score, or the number of lives remaining.&nbsp; A GameManager script seems like the best candidate for this. We created an empty object, assigned it a GameManager script which sets up a singleton, and converted it into a prefab.   We made sure to include this prefab in both of our platform game scenes, as we will need it to handle our logic for events like player deaths, inventory, and of course, the score.<\/p>\n\n\n\n<p>We edited our Level Manager to also be a singleton (so that we can automatically access it within a scene) and added a public string to hold the name of the next scene we should visit upon successful completion of the level.<\/p>\n\n\n\n<p>If our player successfully reaches the end of a level, we reward them by sending them to the next one. To support this, we set a trigger at the end of the level, and tagged it with a &#8220;EndOfLevel&#8221; tag.  If the Player object enters the trigger with that tag, it calls the <strong>NextLevel( )<\/strong> command from the <strong>LevelManager <\/strong>and that sends the player to the appropriate scene.  <\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">private void OnTriggerEnter2D(Collider2D collision)\n{\n    if (collision.gameObject.tag == \"EndOfLevel\")\n    {\n        LevelManager.S.NextLevel();\n    } \n}<\/pre>\n\n\n\n<p>In our GameManager script, we define our scene jump like so:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public void NextLevel()\n{\n    SceneManager.LoadScene(nextScene);\n}<\/pre>\n\n\n\n<p>But now we have a problem.  GameManager only exists in the scene that I created it in.&nbsp; As soon as I jump to a new scene, it gets destroyed just like everything else in its host scene and THAT scene\u2019s GameManager instance appears.&nbsp;  I spent all that time collecting those coins, and now they&#8217;re gone!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2021\/wp-content\/uploads\/2021\/06\/image-9.png\" alt=\"\" class=\"wp-image-126\"\/><figcaption>I earned those points!<\/figcaption><\/figure><\/div>\n\n\n<p>If I want to hold onto things like my score, remaining lives, and inventory, I need to make GameManager persist across the transition from one scene to the next.<\/p>\n\n\n\n<p>To accomplish this, we spare GameManager from an untimely deletion by adding the following line to the&nbsp;<strong>Start ( )<\/strong>&nbsp;function in the GameManager script.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">DontDestroyOnLoad (this);\n<\/pre>\n\n\n\n<p><strong>DontDestroyOnLoad&nbsp;( )<\/strong>&nbsp;preserves a game object across transitions.&nbsp; It actually creates a little side scene in the Hierarchy and moves the object into it during runtime, as seen below:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2020\/wp-content\/uploads\/2021\/06\/image.png\" alt=\"\" class=\"wp-image-226\"\/><figcaption>Before: Our GameManager is just another object in the scene<\/figcaption><\/figure><\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2020\/wp-content\/uploads\/2021\/06\/image-1.png\" alt=\"\" class=\"wp-image-227\"\/><figcaption>After: Our GameManager object has been moved into a side scene<\/figcaption><\/figure><\/div>\n\n\n<p>Now when we jump scenes, this object remains, and our score persists as we play through.<\/p>\n\n\n\n<p>But\u2026 (you knew there would be one, didn\u2019t you?)<\/p>\n\n\n\n<p>For our game to run correctly, we need to have GameManager running in every scene. This means that either we would ALWAYS have to start our game from the very beginning in the scene where we first instantiate it and play all the way through \u2013 not fun, especially when we just want to test one tiny thing in level 5 \u2013 OR we have to find a way to include it in EVERY scene. This is why we chose to make our Game Manager a prefab, and drop an instance of it into every level.<\/p>\n\n\n\n<p>But\u2026 if we are not destroying our Game Manager and we load a scene with another instance of Game Manager, won\u2019t there be two Game Managers?<\/p>\n\n\n\n<p>Yes there will, and that is a problem.&nbsp; Because now not only do we have two game managers, the new one has just declared itself the singleton (S = this) and overwritten the old one, meaning the new object\u2019s score is the one we display and that\u2019s not what we want at all, is it?&nbsp; &nbsp;And if we move scenes again, since both of these now live within the DontDestroyOnLoad section, we will be adding a third Game Manager and overwritting our Singleton yet again and this seems bad.<\/p>\n\n\n\n<p>So to avoid this situation, we create a small workaround.&nbsp; We build in a check in our Game Manager start script that checks to see if any instance of the singleton exists.&nbsp; If none exists, great \u2013 we\u2019re first to the party and we set up shop.&nbsp; But if there is already a singleton defined, we kill this instance of the manager as it is not needed.&nbsp; The script for that looks like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">private void Awake()\n{\n    if (S)\n    {\n        Destroy(this.gameObject);  \n        \/\/ Singleton already exists, remove this instance\n    } else { \n        S = this;  \/\/ Singleton Definition\n    }\n}<\/pre>\n\n\n\n<p>Here we check for the existence of another version of GameManager.&nbsp; If none exists, GameManager.S will return \u201cnull\u201d.&nbsp; If not null, someone is already here.&nbsp; We destroy this new version, and we submit a \u201creturn\u201d command, which exits our script rather than continuing on.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2020\/wp-content\/uploads\/2021\/06\/image-2.png\" alt=\"\" class=\"wp-image-228\"\/><figcaption>Way After: We load the next level but our GameManager continues to exist in this pocket scene.  Because a Singleton was already in place, the Level01 GameManager politely removed itself.<\/figcaption><\/figure><\/div>\n\n\n<p>Once we\u2019ve passed our null-check, the script is still executing so we know we must be the one true game manager. We assign Singleton status and then in the&nbsp;<strong>Start( )&nbsp;<\/strong>method we declare our intention to live forever.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 6 &#8211; I&#8217;m Ready to Go Now<\/h3>\n\n\n\n<p>What about when we DO want something to disappear?  In the case of our game flow, how do we reset a game if we reach a win or lose state?  How to do we go from zero lives remaining back to three, to full health, to an empty inventory?    We could write an elaborate script to reset our conditions internally, but the easier method would be to simply let the GameManager die, return to the Title Screen, and let the next GameManager that comes along start the process all over again.<\/p>\n\n\n\n<p>It turns out that the answer to this is quite simple &#8211; we <strong>Destroy( )<\/strong> the Game Manager from within itself.  <strong>DontDestroyOnLoad <\/strong>only preserves objects across transitions &#8211; it does not make them immortal.<\/p>\n\n\n\n<p>In this example, we are returning to the title screen (for whatever reason) and we remove the GameManager object.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">private void OnTriggerEnter2D(Collider2D collision)\n{\n    if (collision.gameObject.tag == \"EndOfLevel\")\n    {\n        LevelManager.S.NextLevel();\n    } else if (collision.gameObject.tag == \"EndOfGame\")\n    {\n        Debug.Log(\"EndOfGame Reached\");\n        \/\/ tell the level manager to go back to the start menu\n        LevelManager.S.GoToStartMenu();\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Part 7 &#8211; Restarting a Level<\/h3>\n\n\n\n<p>My favorite method for managing death and respawning in a platform game is to simply restart the level by reloading it.  This means that all objects in the scene will be destroyed and re-instantiated.  <\/p>\n\n\n\n<p>Here is a sample script of how you could handle this, by checking for lives remaining.  (NOTE: this is not what we implemented in class today)<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public void PlayerDied()\n{\n    livesRemaining--;\n    Debug.Log(\"Lives Remaining = \" + livesRemaining);\n\n    if (livesRemaining &lt;= 0)\n    {\n        LevelManager.S.GoToStartMenu();\n\n    } else\n    {\n        LevelManager.S.RestartLevel();\n    }\n}<\/pre>\n\n\n\n<p>Here, if my Player dies but I still have lives left, my GameManager script calls <strong>RestartLevel( )<\/strong> which simply loads the same scene by passing it the name of the current active scene.  It is a simple and clean solution, if you don&#8217;t need to preserve the state of objects or items within the level.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public void RestartLevel()\n{\n    SceneManager.LoadScene(SceneManager.GetActiveScene().name);\n}<\/pre>\n\n\n\n<p>If you DO need to preserve states or items , you can store this information in your undestroyed game manager and when the level restarts, have the objects check for relevant data.  For instance, if you were building checkpoints into your game you could store a &#8220;checkpoint&#8221; value with a unique ID for where to place the player. <\/p>\n\n\n\n<p>Better yet, you could make a Level Manager which would store the states of the objects in the level itself, but which would destroy itself when your script moved on to the next level.   Then you have a GameManager that lives across the game, and a LevelManager that lives across attempts at a level.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">GameManager.cs<\/pre>\n\n\n\n<p class=\"has-luminous-vivid-amber-background-color has-background\">In class, the GameManager was not properly destroying itself.  This is due to a missing &#8220;else&#8221; statement on line 22.  This has been corrected for the code below.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"22\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing TMPro;\n\npublic class GameManager : MonoBehaviour\n{\n    public string gameManagerName;\n\n    private int score = 0;\n    public TextMeshProUGUI scoreText;\n\n    public static GameManager S; \/\/ singleton\n\n\n    private int livesRemaining = 3;\n\n    private void Awake()\n    {\n        if (S) {\n            Destroy(this.gameObject);\n        } else\n        {\n            S = this; \/\/ singleton definition\n        }\n    }\n\n    \/\/ Start is called before the first frame update\n    void Start()\n    {\n       DontDestroyOnLoad(this);\n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        scoreText.text = \"Score: \" + score;\n    }\n\n    public void AddScore(int scoreValue)\n    {\n        score += scoreValue;\n    }\n\n    public void PlayerDied()\n    {\n        livesRemaining--;\n        Debug.Log(\"Lives Remaining = \" + livesRemaining);\n\n        if (livesRemaining &lt;= 0)\n        {\n            LevelManager.S.GoToStartMenu();\n\n        } else\n        {\n            LevelManager.S.RestartLevel();\n        }\n    }\n}\n<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">LevelManager.cs<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.SceneManagement;\n\npublic class LevelManager : MonoBehaviour\n{\n    public static LevelManager S; \/\/ singleton\n\n    public string levelName;\n\n    public string nextSceneName;\n\n\n    private void Awake()\n    {\n        S = this;\n    }\n\n    \/\/ Start is called before the first frame update\n    void Start()\n    {\n        \n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        \n    }\n\n    public void btn_StartTheGame()\n    {\n        SceneManager.LoadScene(\"TutorialLevel\");\n\n    }\n\n    public void NextLevel()\n    {\n        SceneManager.LoadScene(nextSceneName);\n    }\n\n    public void GoToStartMenu()\n    {\n        SceneManager.LoadScene(0);\n        Destroy(GameManager.S.gameObject);\n    }\n\n    public void RestartLevel()\n    {\n        SceneManager.LoadScene(SceneManager.GetActiveScene().name);\n    }\n\n}\n<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">PlayerMovement.cs<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\npublic class PlayerMovement : MonoBehaviour\n{\n    private CharacterController2D controller;\n    public float speed;\n\n    private float horizontalMove = 0.0f;\n    private bool jump = false;\n\n    private Animator animator;\n\n    \/\/ Start is called before the first frame update\n    void Start()\n    {\n        controller = GetComponent&lt;CharacterController2D>();\n        animator = GetComponent&lt;Animator>();\n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        horizontalMove = Input.GetAxis(\"Horizontal\") * speed;\n\n        if (Input.GetButtonDown(\"Jump\"))\n        {\n            jump = true;\n            \/\/ set the jump\n            animator.SetBool(\"isOnGround\", false);\n        }\n\n        \/\/ set the animator speed \n        float ourSpeed = Input.GetAxis(\"Horizontal\");\n        animator.SetFloat(\"speed\", Mathf.Abs(ourSpeed));\n\n    }\n\n    private void FixedUpdate()\n    {\n        controller.Move(horizontalMove * Time.fixedDeltaTime, false, jump);\n        jump = false;\n    }\n\n    public void PlayerLanded()\n    {\n        \/\/ land the player\n        animator.SetBool(\"isOnGround\", true);\n    }\n\n    private void OnCollisionEnter2D(Collision2D collision)\n    {\n        if (collision.gameObject.tag == \"Enemy\")\n        {\n            \/\/ player has died\n            GetComponent&lt;Rigidbody2D>().isKinematic = true;\n            animator.SetTrigger(\"Death\");\n            \n        }\n    }\n\n    private void OnTriggerEnter2D(Collider2D collision)\n    {\n        if (collision.gameObject.tag == \"EndOfLevel\")\n        {\n            LevelManager.S.NextLevel();\n        } else if (collision.gameObject.tag == \"EndOfGame\")\n        {\n            Debug.Log(\"EndOfGame Reached\");\n            \/\/ tell the level manager to go back to the start menu\n            LevelManager.S.GoToStartMenu();\n        }\n    }\n\n    public void ProcessPlayerDeath()\n    {\n        GameManager.S.PlayerDied();\n    }\n\n}\n<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n","protected":false},"excerpt":{"rendered":"<p><a class=\"more-link\"  href=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/?p=110\"><span class=\"more-text\"><\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"greenlet_layout":[]},"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/posts\/110"}],"collection":[{"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=110"}],"version-history":[{"count":1,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/posts\/110\/revisions"}],"predecessor-version":[{"id":111,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/posts\/110\/revisions\/111"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=110"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=110"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}