{"id":80,"date":"2022-06-02T18:01:10","date_gmt":"2022-06-02T22:01:10","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/?p=80"},"modified":"2022-06-02T18:01:10","modified_gmt":"2022-06-02T22:01:10","slug":"day-13-singleton-game-manager","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/?p=80","title":{"rendered":"Day 13: Singleton Game Manager"},"content":{"rendered":"\n<p>Today we mapped out our game flow and looked at how our various object interactions would move us through our state machine, and then used another singleton, the GameManger, to help coordinate this journey, then spent a little time at the end making cooler enemies.  (You don&#8217;t have to do this for your game, but you may want to!) <\/p>\n\n\n\n<!--more-->\n\n\n\n<h3 class=\"wp-block-heading\">Part 1: Game Flow<\/h3>\n\n\n\n<p id=\"block-32ad2945-77a0-45f4-b2cd-3b5bda0ae1c9\">First, we mapped out our game flow so that we know what states we intend to use, and how we can move between them.  We implemented some (but not all) of these steps, and those that remain are your responsibility for this week&#8217;s assignment.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1024\" height=\"793\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/wp-content\/uploads\/2022\/06\/image-1024x793.png\" alt=\"\" class=\"wp-image-81\" srcset=\"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/wp-content\/uploads\/2022\/06\/image-1024x793.png 1024w, https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/wp-content\/uploads\/2022\/06\/image-300x232.png 300w, https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/wp-content\/uploads\/2022\/06\/image-768x595.png 768w, https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/wp-content\/uploads\/2022\/06\/image.png 1211w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<ol id=\"block-36a36bd8-b1ce-4bda-af99-9189d714db64\"><li>We start in an inert state that invites us to &#8220;Press S to Start&#8221;. This is the <strong>Menu<\/strong> state and eventually we will replace it with a separate scene with way better UI.<\/li><li>Pressing S takes us to a new game, where the number of lives are set, and the objects for the first round are prepared. Once ready, we will go into the <strong>GetReady<\/strong> state and hold there momentarily.<\/li><li>Upon completion of the <strong>GetReady <\/strong>state, we move into <strong>Playing<\/strong>,<\/li><li>During <strong>Playing<\/strong>, the player will have control of the ship and be able to shoot, and the enemies will begin their attack. There are three possible ways to exit this state.<\/li><li>The first way occurs in case of a win condition &#8211; the player destroys all of the enemy ships &#8211; and are taken to the <strong>GameOver<\/strong> state with a win message.<\/li><li>The other two methods are losing conditions &#8211; the player object is destroyed by an enemy, or the enemy ship reaches the ground. In this case, the player loses a life, and the round is reset after an oops state, or the game is over if the player has run out of lives.<\/li><\/ol>\n\n\n\n<p id=\"block-14801aac-49d8-4e2f-b20e-66c9564941d8\">The first step to build out this structure is for us to create our GameManager object (again created from an empty gameobject), and create the GameManager.cs script for it.<\/p>\n\n\n\n<p id=\"block-46a0dcd6-efee-4dac-a31a-f98c0218e4da\">We declare our variable to hold the Singleton&#8230;<\/p>\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=\"\">public static GameManager S; \/\/ set up the singleton<\/pre>\n\n\n\n<p id=\"block-34414586-23b0-4c81-bbfd-f631569a74a3\">&#8230; and we assign it. This time, we add in some extra protection to make sure that a version of this singleton does not already exist. This can happen when we move between scenes, if we were to preserve our GameManager object, we might enter a scene that already has a GameManager inside of it. We will test for the instance and destroy if we find one already in place.<\/p>\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=\"\">private void Awake()\n{\n    if (GameManager.S)\n    {\n        \/\/ singleton already exists, destroy this instance\n        Destroy(this.gameObject);\n    } else\n    {\n        \/\/ define the singleton\n        S = this;\n    }\n}<\/pre>\n\n\n\n<p id=\"block-48e7ffa7-17a5-49e6-830c-2fa3d33cd2c1\">Next we define a number of enumerated values for our various gamestates:<\/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 enum GameState { menu, getReady, playing, oops, gameOver};<\/pre>\n\n\n\n<p id=\"block-f47ce235-6496-4e15-84e4-d3e1755be31b\">To implement Step 1, we set up a GameState variable in our script and test against it at appropriate times. For instance, pressing &#8220;S&#8221; would mean one thing during our Menu stage (it should be interpreted as the &#8220;start&#8221; command) but might mean something different during gameplay, if we are using our Input.GetAxis(&#8220;Vertical&#8221;), which relies on pressing WASD keys.  And for the assignment, you can do the same sort of check for the GameOver state looking for a user to press the R key to restart. <\/p>\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=\"\">void Update()\n{\n    if (gameState == GameState.menu)\n    {\n        \/\/ game is in menu state, press S to start will advance\n        if (Input.GetKeyDown(KeyCode.S)) { StartANewGame(); }\n    } else if (gameState == GameState.gameOver)\n    {\n        \/\/ press r to restart the game\n\n    }\n}<\/pre>\n\n\n\n<p id=\"block-3a0e9c26-2cc0-4a01-832b-1f6ca0d094e2\">We can begin to set up the stages. We created a StartNewGame() function that resets the lives left (and later will also reset the score, which is part of this week&#8217;s assignment)<\/p>\n\n\n\n<p id=\"block-3f0b6a8e-e7ba-47a0-86f1-51cbbdd9f95f\">We also create<strong> ResetRound( ) <\/strong>and <strong>StartRound( ) <\/strong>functions. <strong>ResetRound( )<\/strong> is Step 2, and will place the block of enemies in the starting position. We make our Mothership object a prefab, and remove it from the scene, but add the prefab to a GameObject variable in this script. <strong>ResetRound <\/strong>instantiates the prefab and stores it in another GameObject variable that is our &#8220;currentMothership&#8221;. Before we instantiate, we test to see if one already exists. If this returns true, then we probably have arrived here after a player lost a round. We want to reset the enemy objects, so we destroy whatever already exists.<\/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 ResetRound()\n{\n    \/\/ spawn a new set of enemies\n    if (currentMotherShip) { Destroy(currentMotherShip); }\n    currentMotherShip = Instantiate(motherShipPrefab);\n\n    \/\/ put us in the get ready state\n    gameState = GameState.getReady;\n\n    \/\/ run the GetReadyState coroutine\n    StartCoroutine(GetReadyState());\n}<\/pre>\n\n\n\n<p id=\"block-9ced3ecf-9b53-4ce5-ad13-32cd2b79752e\">Next we transition to the <strong>GetReady<\/strong> state, and kicking off a coroutine that will let us pause for 3 seconds, then trigger <strong>StartRound( )<\/strong>, which completes Step 3.  <strong>StartRound( )<\/strong> moves us to the <strong>Playing<\/strong> state, and we remain here until it is time to end a round. <\/p>\n\n\n\n<p>Now that we can get to the playing state, it is time to adjust some of our object behaviors, and make them state dependent.<\/p>\n\n\n\n<p>First, we want to restrict the player movement so that they can only move and shoot while we are in the <strong>Playing<\/strong> state.  To accomplish this, we add a gamestate check in the Player&#8217;s update function. By wrapping the contents with the following &#8220;if&#8221; statement, we ensure that motion only occurs while in the <strong>Playing<\/strong> state.<\/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=\"\">if (GameManager.S.gameState == GameState.playing) { \n   ...\n}<\/pre>\n\n\n\n<p><strong>Again, note that there is no need to associate the game manager with any object in our Player script, and no references to be made. &#8220;GameManager.S&#8221; is available to be called globally.<\/strong><\/p>\n\n\n\n<p id=\"block-435a352c-cddb-4dae-af0e-08537c6673c0\">The next problem is that our enemy object starts moving and shooting during the get ready state. I also don&#8217;t like that it continues to move after my player has been destroyed. I want to prevent the possibility of multiple state requests coming in while we are in our <strong>Oops<\/strong> state, so I am going to create new scripts in the Enemy object that will be responsible for starting and stopping the coroutines.<\/p>\n\n\n\n<p id=\"block-e30f4e2b-eed7-4bd1-9449-2d73d5f97fa5\">Since I am instantiating the Mothership in GameManager, I have access to the object that is the instance in our scene. I add public functions to <strong>StartTheAttack( )<\/strong> [which starts the coroutines previously held in Start( )] and<strong> StopTheAttack( )<\/strong> in the MotherShip.cs script, and then call those from GameManager using:<\/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=\"\">currentMotherShip.GetComponent&lt;MotherShip>().StopTheAttack();<\/pre>\n\n\n\n<p id=\"block-c9ce3fa3-75f1-4cf5-bc69-64f5e3646800\">In our StopTheAttack( ) function, we call the StopAllCoroutines( ) function. This will cease any coroutines currently running from the component.<\/p>\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=\"\">public void StopTheAttack()\n{\n    StopAllCoroutines();\n}<\/pre>\n\n\n\n<p>Next, we implemented one of the three conditions that will get us out &#8211; specifically the case where an enemy bomb hits the player.  We created a <strong>PlayerDestroyed( )<\/strong> function and then call it from a OnCollisionEnter that we add to the Player 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=\"\">private void OnCollisionEnter(Collision collision)\n{\n    if (collision.transform.tag == \"EnemyBomb\")\n    {\n        \/\/ make the player explode\n        Destroy(this.gameObject);\n\n        \/\/ make the boom boom noise\n        SoundManager.S.MakePlayerExplosion();\n\n        \/\/ inform the manager that the player exploded\n        GameManager.S.PlayerDestroyed();\n    }\n}<\/pre>\n\n\n\n<p>Now when the Player collides with an EnemyBomb, the PlayerDestroyed( ) script is called on the GameManager.  PlayerDestroyed removes a life from the count, and then moves us to the Oops state, through a coroutine.<\/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 PlayerDestroyed()\n{\n   \/\/ remove a life\n   livesLeft--;\n\n   \/\/ go to oops state\n   StartCoroutine(OopsState());\n}\n\npublic IEnumerator OopsState()\n{\n    \/\/ put the game in oops state\n    gameState = GameState.oops;\n\n    \/\/ tell the enemy to stop their attack\n    currentMotherShip.GetComponent&lt;MotherShip>().StopTheAttack();\n\n    \/\/ pause\n    yield return new WaitForSeconds(2.0f);\n\n    \/\/ decide if we restart or game over\n    if (livesLeft > 0)\n    {\n        \/\/ restart our round\n        ResetRound();\n    } else\n    {\n        \/\/ go to game over state\n        gameState = GameState.gameOver;\n    }\n}<\/pre>\n\n\n\n<p>We start the OopsState coroutine by putting the game into the &#8220;oops&#8221; gamestate, and we the Mothership to stop moving and dropping bombs.  Then we yield activity for a few seconds to pause the action.  During this time, each object&#8217;s Update( ) command will be called on each frame.   This is why we move into a different game-state, and have objects test against that state &#8211; because the game engine keeps running even when our game flow is taking a rest.  We use these variables to help the other objects exhibit the proper behavior.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 2: Better Enemies (optional!)<\/h3>\n\n\n\n<p class=\"has-background\" style=\"background-color:#f7c78d\">Since we had a little extra time at the end of class, I demonstrated my favorite method for making cool voxel enemies that appear to animate.  The process is relatively simple, and when they explode it adds a great juicy quality to the action.   Please note, enemies like this are NOT required for your assignment, but if you would like to create some of your own, you should definitely try it out! <\/p>\n\n\n\n<p>Our previous alien attackers were rather primitive &#8211; Unity primitives, to be exact.  (Thank you, I&#8217;ll be here all week!)  Now that we have a fancy setting, our enemies should be a little more&#8230; dramatic.  For this section, we are going to look to the source material and draw some inspiration from the classic pixel-y goodness of the original, and build our enemies out of cubes.<\/p>\n\n\n\n<p>Why cubes?  First off, they are super easy to create and work with, and require surprisingly little overhead in terms of rendering and physics calculation.  Second, I want to show you how you can create a game using just the tools provided &#8211; no need for special 3D modeling\/animation software.  Third, we are going to use Physics on those blocks to create a satisfying explosion effect on the alien ship that will add some fun to our game.<\/p>\n\n\n\n<p>First, we create a single cube, using&nbsp;<strong>GameObject &gt; 3D Object &gt; Cube<\/strong>&nbsp;or&nbsp;<strong>Create\u2026 &gt; 3D Object &gt; Cube<\/strong>. I&#8217;m going to want my bottom center cube to represent the &#8220;position&#8221; of the object so I will set its position to the origin (0, 0, 0).  If yours is not at that location, you can select the settings dropdown from the&nbsp;<strong>Transform<\/strong>&nbsp;component and select&nbsp;<strong>Reset Transform<\/strong>&nbsp;to do so.<\/p>\n\n\n\n<p>Next we make copies of the cube and offset them by 1 unit increments so that you have a row of 5 cubes with their edges touching. Now copy those rows and move them up by 1 unit increments, until you have a 5\u00d75 cube grid.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/f2019\/wp-content\/uploads\/2019\/10\/image.png\" alt=\"\" class=\"wp-image-146\"\/><figcaption>In case you were wondering what 25 cubes look like<\/figcaption><\/figure><\/div>\n\n\n<p>Then, we create an empty game object (<strong>GameObject &gt; Create Empty<\/strong>&nbsp;or&nbsp;<strong>Create\u2026 &gt; Create Empty<\/strong>) also located at (0, 0, 0) and make all of the cubes children of this empty object. Notice that when you add the cubes as children, they now indent under the parent object in the Hierarchy, and the parent object now has a small button that lets you roll-up the list.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/f2019\/wp-content\/uploads\/2019\/10\/image-2.png\" alt=\"\" class=\"wp-image-148\"\/><\/figure><\/div>\n\n\n<p>I expect to create a number of aliens using this same block grid, and so I will create a <strong>prefab <\/strong>object from it that I can use as a template for future iterations.  I name the parent object &#8220;EnemyBase&#8221; and drag it into my Asset window.  I can now use this prefab to override the cubes that are on and off in order to make shapes.  Once I have edited this to make an alien shape, I will save this as a different prefab.  (This is <em>my<\/em> path to create these objects, but this is by no means the <em>only<\/em> path.  Do what works for you!)   <\/p>\n\n\n\n<p>Once I have unpacked the object, I edit it by de-activating some of the blocks. You can delete these, or simply turn the cube objects off using the check box next to the GameObject name in the Inspector. This way, the block is preserved, in case you want to go back later and edit quickly. Remove some blocks and make an alien shape like so:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/f2019\/wp-content\/uploads\/2019\/10\/image-3.png\" alt=\"\" class=\"wp-image-149\"\/><\/figure><\/div>\n\n\n<p>Time to give the parent object a name. I called mine\u00a0<strong>EnemyFrameA<\/strong>, since I will have multiple versions of this, each one representing a frame of animation.<\/p>\n\n\n\n<p>Now I turn off <strong><strong>EnemyFrameA<\/strong><\/strong>, and place a new instance of the <strong>EnemyBase<\/strong>template in the scene at the same position.  I unpack, edit it to look as though the legs have moved, and name the parent object\u00a0<strong><strong>EnemyFrameB<\/strong><\/strong>. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/f2019\/wp-content\/uploads\/2019\/10\/image-4.png\" alt=\"\" class=\"wp-image-150\"\/><\/figure><\/div>\n\n\n<p>Now I am going to create a duplicate of <strong>EnemyA_Frame1<\/strong>, but I will call this one&nbsp;<strong>EnemyA_FrameExplode<\/strong>. This object will be out \u201cstunt double\u201d, swapping places with the other frames when it is time for the ship to blow up. To get that great explosive force, add a Rigidbody component to each of the cubes in EnemyA_FrameExplode. (You can simply select all of the cubes at once and go to&nbsp;<strong>Add Component<\/strong>).  <\/p>\n\n\n\n<p>It is also worth noting that in my objects for frames A &amp; B, I have turned their colliders off.  This is because I want to have to worry about managing collisions on the individual blocks &#8211; I want these to simply be a part of a cohesive whole.  <\/p>\n\n\n\n<p>I\u2019m going to make each one of our alien parent objects a prefab by dragging it down into the Assets folder. Next, I am going to create a new empty game object and parent it to these prefabs, so that it serves as a container to the individual frames. Name this object&nbsp;<strong>EnemyTypeA1 <\/strong>and make this object into a prefab as well.  I add my <strong>Enemy<\/strong> class to this, as well as a <strong>Box Collider<\/strong> component which I will resize to fit around my enemy object.  (Don&#8217;t forget to change your layer designation to &#8220;Enemy&#8221;!)<\/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\/f2020\/wp-content\/uploads\/2020\/10\/image-1.png\" alt=\"\" class=\"wp-image-91\"\/><\/figure><\/div>\n\n\n<p>Now, let\u2019s add a little bit of scripting to bring our little attacker to life.  We want to open our <strong>Enemy<\/strong> script and add variables to hold the three child objects of this particular enemy.     <\/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 GameObject FrameA;\npublic GameObject FrameB;\npublic GameObject FrameExplode;<\/pre>\n\n\n\n<p>We define three objects (which we must populate with the Inspector), and then on\u00a0<strong>Start( )<\/strong>\u00a0we set their conditions so that only the modelOne model is active. <\/p>\n\n\n\n<p class=\"has-background\" style=\"background-color:#f7c78d\">NOTE: I should probably use Awake so that this is set instantly, just in case something else is going to access our models when the\u00a0<strong>Start( )<\/strong>\u00a0event runs, but for today, Start will do.<\/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 Start()\n{\n    FrameA.SetActive(true);\n    FrameB.SetActive(false);\n    FrameExplode.SetActive(false);\n\n    \/\/ start the swapping\n    StartCoroutine(NextStage());\n}<\/pre>\n\n\n\n<p>When&nbsp;<strong>Start( )<\/strong>&nbsp;does arrive, we use that to launch a&nbsp;<strong>Coroutine&nbsp;<\/strong>that I have created called&nbsp;<strong>NextStage( )<\/strong>&nbsp;that will call my&nbsp;<strong>SwapFrames( )&nbsp;<\/strong>command once per second. It accomplishes this by nesting the call and the 1.0 second yield inside of a \u201cwhile\u201d loop that will run continuously for the duration of the object because the condition is set to \u201ctrue\u201d.<\/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 IEnumerator NextStage()\n{\n    while (true)\n    {\n        SwapFrames();\n        yield return new WaitForSeconds(1.0f);\n    }\n}<\/pre>\n\n\n\n<p>The <strong>SwapFrames( ) <\/strong>command simply set the active state of both frames to the opposite of its current value.  (I get that current value with the GameObject property <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject-activeSelf.html\"><strong>activeSelf<\/strong><\/a>) <\/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 SwapFrames()\n{\n    FrameA.SetActive(!FrameA.activeSelf);\n    FrameB.SetActive(!FrameB.activeSelf);\n}<\/pre>\n\n\n\n<p class=\"has-black-color has-text-color has-background has-small-font-size\" style=\"background-color:#faeae1\"><strong>NOTE<\/strong><br>In previous versions of this assignment, I would tend to use the GameObject&#8217;s \u201cactive\u201d property, getting and setting that directly.  But over time, Unity started to become mad at this, because this particular interaction is being phased out.  So instead the proper method is used, utilizing the&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject.SetActive.html\">GameObject.SetActive( )<\/a>&nbsp;command, and reading the value using the &#8220;activeSelf&#8221; property.<\/p>\n\n\n\n<p>Once this is done, I am free to go back into my main scene and replace the MotherShip prefab enemies with our new advanced enemy prefabs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 3: Bigger Explosions!<\/h3>\n\n\n\n<p>For this next part, we want to test out our explosion effect \u2013 where we swap out our model frames with our \u201cstunt double\u201d and then apply an explosive force to the individual boxes. We do this by creating an\u00a0<strong>Explode( )\u00a0<\/strong>command in our Enemy, 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=\"\">private void Explode()\n{\n    \/\/ make an explosion\n    Instantiate(enemyExplosionPrefab, transform.position, Quaternion.identity);\n\n    \/\/ activate explosion frame\n    FrameA.SetActive(false);\n    FrameB.SetActive(false);\n    FrameExplode.SetActive(true);\n\n    Rigidbody[] cubes; \/\/ variable to hold rigidbodies\n\n    \/\/ get every rigidbody inside the explosion object.\n    cubes = FrameExplode.GetComponentsInChildren&lt;Rigidbody>();\n\n    \/\/ apply an explosive force\n    foreach (Rigidbody rb in cubes)\n    {\n        rb.AddExplosionForce(250f, (FrameExplode.transform.position + Vector3.forward), 10f);\n    }\n\n    FrameExplode.transform.parent = null;\n}<\/pre>\n\n\n\n<p>Once we have turned off our models and turned on our \u201cstunt double\u201d object, our goal is to apply the&nbsp;<strong><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Rigidbody.AddExplosionForce.html\">Rigidbody.AddExplosionForce( )<\/a><\/strong>&nbsp;to each cube in the object. This method takes three arguments \u2013 a force to exert, an origination point for the explosion, and a radius within which the explosion will have an effect. We create public float objects for \u201cexplosionForce\u201d and \u201cexplosionRadius\u201d in the class declaration, and then set those values.<\/p>\n\n\n\n<p>In our explode command, you see that we declared a variable as \u201cRigidbody[ ] cubes\u201d. These brackets mean that we expect to receive more than one result \u2013 an array of Rigidbody components. We then generate this list using the&nbsp;<strong>GetComponentsInChildren&lt;&gt;( )<\/strong>&nbsp;method which returns the array of all child objects for \u201cmodelExplosion\u201d that have a Rigidbody component in their backpack. Finally we use a \u201cforeach\u201d loop that lets us cycle through each result in our array. In each pass of the loop (that is, for each Rigidbody object in cubes[ ]) we set that object to the variable \u201crb\u201d, and then apply our explosive force to that Rigidbody. By cycling through all of these we ensure that every object is affected by the force.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/courses.ideate.cmu.edu\/53-353\/f2019\/wp-content\/uploads\/2019\/10\/image-9.png\" alt=\"\" class=\"wp-image-155\"\/><figcaption>Kind of, but not quite\u2026<\/figcaption><\/figure><\/div>\n\n\n<p>Now, this creates a problem, because we previously set the Enemy object to self-destruct as soon as it collided with the bullet.  If we destroy the object, the children will be destroyed as well, along with our &#8220;stunt double&#8221;.  Everything will just blink out of existence.  But what if our stunt double was not a child of the enemy object anymore?   To accomplish this, we are going to emancipate our stunt double from it&#8217;s parent object, making it an orphan (or as I like to call it, a &#8220;child of the world&#8221;).  We do this by setting the parent of that object to &#8220;null&#8221;.<\/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=\"\">FrameExplode.transform.parent = null;<\/pre>\n\n\n\n<p>Now our enemy object destroys itself, but not before throwing the stunt double out into the world and applying the explosion just before it disappears.   Because our <strong>Explode( ) <\/strong>command runs prior to the destruction of the Enemy object, our stunt double makes it out alive (but not for long). <\/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\/f2020\/wp-content\/uploads\/2020\/10\/image-3.png\" alt=\"\" class=\"wp-image-93\"\/><figcaption>BOOM-shaka-laka!<\/figcaption><\/figure><\/div>\n\n\n<p>Tomorrow we will look at improved UI fonts using TextMeshPro, and at the &#8220;build&#8221; process, where we compile our game that only runs in an editor into it&#8217;s own standalone executable.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-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<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 enum GameState { menu, getReady, playing, oops, gameOver};\n\npublic class GameManager : MonoBehaviour\n{\n    public static GameManager S; \/\/ set up the singleton\n\n    public GameState gameState;\n\n    \/\/ game variables\n    private int score;\n    private int livesLeft;\n    private int LIVES_AT_START = 3;\n\n    \/\/ mothership variable\n    private GameObject currentMotherShip;\n    public GameObject motherShipPrefab;\n\n    private void Awake()\n    {\n        \/\/ Singleton Definition\n        if (GameManager.S)\n        {\n            \/\/ singleton already exists, destroy this instance\n            Destroy(this.gameObject);\n        } else\n        {\n            \/\/ define the singleton\n            S = this;\n\n        }\n\n    }\n\n\n    \/\/ Start is called before the first frame update\n    void Start()\n    {\n        \/\/ set the gamestate\n        gameState = GameState.menu;\n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        if (gameState == GameState.menu)\n        {\n            \/\/ game is in menu state, press S to start will advance\n            if (Input.GetKeyDown(KeyCode.S)) { StartANewGame(); }\n        } else if (gameState == GameState.gameOver)\n        {\n            \/\/ press r to restart the game\n\n        }\n\n    }\n\n    void StartANewGame()\n    {\n        \/\/ reset our lives\n        livesLeft = LIVES_AT_START;\n\n        \/\/ reset our score\n        score = 0;\n\n        \/\/ go to the first round\n        ResetRound();\n    }\n\n    private void ResetRound()\n    {\n        \/\/ spawn a new set of enemies\n        if (currentMotherShip) { Destroy(currentMotherShip); }\n        currentMotherShip = Instantiate(motherShipPrefab);\n\n\n        \/\/ put us in the get ready state\n        gameState = GameState.getReady;\n\n        \/\/ run the GetReadyState coroutine\n        StartCoroutine(GetReadyState());\n    }\n\n    private void StartRound()\n    {\n        \/\/ start the attack\n        currentMotherShip.GetComponent&lt;MotherShip>().StartTheAttack();\n\n        \/\/ put this into the playing state\n        gameState = GameState.playing;\n    }\n\n    public IEnumerator GetReadyState()\n    {\n        \/\/ turn on the get ready message\n\n        \/\/ wait for a few seconds\n        yield return new WaitForSeconds(3.0f);\n\n        \/\/ turn off the get ready message\n\n        \/\/ start the round\n        StartRound();\n    }\n\n    public void PlayerDestroyed()\n    {\n        \/\/ remove a life\n        livesLeft--;\n\n        \/\/ go to oops state\n        StartCoroutine(OopsState());\n    }\n\n    public IEnumerator OopsState()\n    {\n        \/\/ put the game in oops state\n        gameState = GameState.oops;\n\n        \/\/ tell the enemy to stop their attack\n        currentMotherShip.GetComponent&lt;MotherShip>().StopTheAttack();\n\n        \/\/ put up destroyed message\n\n        \/\/ pause\n        yield return new WaitForSeconds(2.0f);\n\n        \/\/ decide if we restart or game over\n        if (livesLeft > 0)\n        {\n            \/\/ restart our round\n            ResetRound();\n        } else\n        {\n            \/\/ go to game over state\n            gameState = GameState.gameOver;\n\n            \/\/ game over lose message\n\n        }\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\">Enemy.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 Enemy : MonoBehaviour\n{\n    public GameObject enemyBombPrefab;\n    public GameObject enemyExplosionPrefab;\n\n    public GameObject FrameA;\n    public GameObject FrameB;\n    public GameObject FrameExplode;\n\n    private void Start()\n    {\n        FrameA.SetActive(true);\n        FrameB.SetActive(false);\n        FrameExplode.SetActive(false);\n\n        \/\/ start the swapping\n        StartCoroutine(NextStage());\n    }\n\n    public void SwapFrames()\n    {\n        FrameA.SetActive(!FrameA.activeSelf);\n        FrameB.SetActive(!FrameB.activeSelf);\n    }\n\n    public IEnumerator NextStage()\n    {\n        while (true)\n        {\n            SwapFrames();\n            yield return new WaitForSeconds(1.0f);\n        }\n    }\n\n    public void DropABomb()\n    {\n        \/\/ make a bomb\n        Instantiate(enemyBombPrefab, (transform.position + Vector3.down), Quaternion.identity);\n    }\n\n    private void Explode()\n    {\n        \/\/ make an explosion\n        Instantiate(enemyExplosionPrefab, transform.position, Quaternion.identity);\n\n        \/\/ activate explosion frame\n        FrameA.SetActive(false);\n        FrameB.SetActive(false);\n        FrameExplode.SetActive(true);\n\n        Rigidbody[] cubes; \/\/ variable to hold rigidbodies\n\n        \/\/ get every rigidbody inside the explosion object.\n        cubes = FrameExplode.GetComponentsInChildren&lt;Rigidbody>();\n\n        \/\/ apply an explosive force\n        foreach (Rigidbody rb in cubes)\n        {\n            rb.AddExplosionForce(250f, (FrameExplode.transform.position + Vector3.forward), 10f);\n\n        }\n\n        FrameExplode.transform.parent = null;\n    }\n\n    private void Update()\n    {\n        if (Input.GetKeyDown(KeyCode.B)) { DropABomb(); }\n    }\n\n    private void OnCollisionEnter(Collision collision)\n    {\n        if (collision.transform.tag == \"PlayerBullet\")\n        {\n            \/\/ make the explosion effect\n            Explode();\n\n            \/\/ tell the bullet to destroy itself\n            Destroy(collision.gameObject);\n\n            \/\/ destroy this object\n            Destroy(this.gameObject);\n\n            \/\/ make the explosion noise\n            SoundManager.S.MakeEnemyExplosionSound();\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\">Player.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 Player : MonoBehaviour\n{\n    public float speed;\n    public float MAX_OFFSET;\n\n    public GameObject bulletPrefab;\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        \/\/ move our player object\n        Vector3 currentPosition = transform.position;\n        currentPosition.x = currentPosition.x + (Input.GetAxis(\"Horizontal\") * speed * Time.deltaTime);\n\n        \/\/ restrict motion to range\n        currentPosition.x = Mathf.Clamp(currentPosition.x, -MAX_OFFSET, MAX_OFFSET);\n\n        \/\/ update the position\n        transform.position = currentPosition;\n\n        if (GameManager.S.gameState == GameState.playing)\n        {\n            if (Input.GetKeyDown(KeyCode.Space))\n            {\n                FireBullet();\n            }\n        }\n\n    }\n\n    void FireBullet()\n    {\n        \/\/ instantiate a bullet\n        Instantiate(bulletPrefab, (transform.position + Vector3.up), Quaternion.identity);\n    }\n\n    private void OnCollisionEnter(Collision collision)\n    {\n        if (collision.transform.tag == \"EnemyBomb\")\n        {\n            \/\/ make the player explode\n            Destroy(this.gameObject);\n\n            \/\/ make the boom boom noise\n            SoundManager.S.MakePlayerExplosion();\n\n            \/\/ inform the manager that the player exploded\n            GameManager.S.PlayerDestroyed();\n        }\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\">MotherShip.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 MotherShip : MonoBehaviour\n{\n\n    public int stepsToSide;\n    public float sideStepUnits;\n    public float downStepUnits;\n    public float timeBetweenSteps;\n\n    \/\/ Start is called before the first frame update\n    void Start()\n    {\n       \/\/  StartCoroutine(MoveMother());\n       \/\/  StartCoroutine(SendABomb());\n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        \n    }\n\n    public void StartTheAttack()\n    {\n        StartCoroutine(MoveMother());\n        StartCoroutine(SendABomb());\n    }\n\n    public void StopTheAttack()\n    {\n        StopAllCoroutines();\n    }\n\n    public IEnumerator MoveMother()\n    {\n        \/\/ define our side step vector\n        Vector3 moveVector = Vector3.right * sideStepUnits;\n\n        \/\/ repeat this\n        while (transform.childCount > 0) {\n\n            \/\/ side move sequence\n            for(int i = 0; i &lt; stepsToSide; i++)\n            {\n                \/\/ move to the side\n                transform.position = transform.position + moveVector;\n\n                \/\/ wait for the next move\n                yield return new WaitForSeconds(timeBetweenSteps);\n            }\n\n            \/\/ move down\n            transform.position += (Vector3.down * downStepUnits);\n            yield return new WaitForSeconds(timeBetweenSteps);\n\n            \/\/ flip the direction\n            moveVector *= -1;\n        }\n    }\n\n    public IEnumerator SendABomb()\n    {\n        float timeBetweenBombs = 8.7f;\n\n        bool isRunning = true;\n\n        while (isRunning)\n        {\n            \/\/ see how many child objects there are\n            int enemyCount = transform.childCount;\n\n            \/\/ if there are children...\n            if (enemyCount > 0)\n            {\n                \/\/ pick one at random\n                int enemyIndex = Random.Range(0, enemyCount);\n\n                \/\/ get the object\n                Transform thisEnemy = transform.GetChild(enemyIndex);\n\n                \/\/ make sure it has the Enemy component\n                Enemy enemyScript = thisEnemy.GetComponent&lt;Enemy>();\n\n                if (enemyScript)\n                {\n                    \/\/ drop the bomb\n                    enemyScript.DropABomb();\n                }\n            } else\n            {\n                \/\/ .. and if no children, stop running\n                isRunning = false;\n            }\n\n            yield return new WaitForSeconds(timeBetweenBombs);\n        }\n\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=80\"><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\/80"}],"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=80"}],"version-history":[{"count":1,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions"}],"predecessor-version":[{"id":82,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions\/82"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=80"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=80"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/53-353\/m2022\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=80"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}