{"id":69,"date":"2026-09-23T01:30:57","date_gmt":"2026-09-23T01:30:57","guid":{"rendered":"https:\/\/courses.etc.cmu.edu\/53-353\/?p=69"},"modified":"2026-09-23T01:30:57","modified_gmt":"2026-09-23T01:30:57","slug":"lesson-5-astral-attackers-part-2","status":"publish","type":"post","link":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/2026\/09\/23\/lesson-5-astral-attackers-part-2\/","title":{"rendered":"Lesson 5: Astral Attackers (part 2)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In this week&#8217;s class, we discussed strategies for audio, and covered the Singleton pattern &#8211; a very powerful tool (but also a potentially tricky\/dangerous one) for organizing our game and exposing our &#8220;manager&#8221; scripts to the objects in our scene.  The videos this week will cover updates to our Astral Attackers game to build a Sound Manager, create the Game Manager which will handle our game state and flow, and finally we will make some visual improvements to our environment and to our enemies to spice things up.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Part 1-A: Making Better Enemies&#8230;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 class=\"wp-block-paragraph\">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 class=\"wp-block-paragraph\">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 class=\"wp-block-paragraph\">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\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"493\" height=\"414\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-8.png\" alt=\"\" class=\"wp-image-356\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-8.png 493w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-8-300x252.png 300w\" sizes=\"auto, (max-width: 493px) 100vw, 493px\" \/><figcaption class=\"wp-element-caption\">in case you wondered what 25 cubes looked like..<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">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\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"336\" height=\"278\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-9.png\" alt=\"\" class=\"wp-image-357\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-9.png 336w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-9-300x248.png 300w\" sizes=\"auto, (max-width: 336px) 100vw, 336px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">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;Base Grid&#8221; and drag it into my Asset window.   Now that I have saved a copy, I want to unpack this prefab and convert everything back into a scene object so that I can make local edits.  I do this by selecting <strong>Prefab &gt; Unpack Completely<\/strong>.  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 class=\"wp-block-paragraph\">Once I have unpacked the object, I edit it by removing 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\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"556\" height=\"456\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-10.png\" alt=\"\" class=\"wp-image-358\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-10.png 556w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-10-300x246.png 300w\" sizes=\"auto, (max-width: 556px) 100vw, 556px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Time to give the parent object a name. I called mine&nbsp;<strong>AlienFrame1<\/strong>, since I will have multiple versions of this, each one representing a frame of animation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now I turn off <strong>AlienFrame1<\/strong>, and place a new instance of the <strong>AlienGrid <\/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&nbsp;<strong>AlienFrame2<\/strong>. <\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"530\" height=\"436\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-11.png\" alt=\"\" class=\"wp-image-359\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-11.png 530w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-11-300x247.png 300w\" sizes=\"auto, (max-width: 530px) 100vw, 530px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now I am going to create a duplicate of <strong>AlienFrame1<\/strong>, but I will call this one&nbsp;<strong>AlienExplode<\/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 <strong>AlienExplode<\/strong>. (You can simply select all of the cubes at once and go to&nbsp;<strong>Add Component<\/strong>).  <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is also worth noting that in my objects for frames 1 &amp; 2, 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 class=\"wp-block-paragraph\">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>EnemyTypeA <\/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\n<p class=\"wp-block-paragraph\">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<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n    \/\/ enemy voxel frame prefabs\n    public GameObject enemyFrameOne, enemyFrameTwo, enemyExplode;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">We define three objects (which we must populate with the Inspector), and then on&nbsp;<strong>Start( )<\/strong>&nbsp;we set their conditions so that only the modelOne model is active. I use Awake so that this is set instantly, just in case something else is going to access our models when the&nbsp;<strong>Start( )<\/strong>&nbsp;event runs.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nprivate void Start()\n{\n    enemyFrameOne.SetActive(true);\n    enemyFrameTwo.SetActive(false);\n    enemyExplode.SetActive(false);\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Next, we create a functional called&nbsp;<strong>SwapFrames( )&nbsp;<\/strong>that will toggle the visibility of Enemy Frames 1 &amp; 2.  cond. 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<p class=\"wp-block-paragraph\">The <strong>SwapFrames( ) <\/strong>command simply set the active state of both frames to the opposite value.  For now, this function will fire when we press the &#8220;S&#8221; button.  <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic void SwapFrames()\n{\n    enemyFrameOne.SetActive(!enemyFrameOne.activeSelf);\n    enemyFrameTwo.SetActive(!enemyFrameTwo.activeSelf);\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Now we need something to call this frame swapping function.  Our game design calls for a highly coordinated attack orchestrated by a single entity &#8211; the MotherShip (or in this case the MotherShipScript) &#8211; and so it is there that we will issue the <strong>SwapFrames( )<\/strong> call.   In the previous lesson we examined how to access a child object through the <strong>transform<\/strong> of this object, so one strategy would be to count our children, then loop through them one-by-one, accessing the <strong>EnemyScript<\/strong> component and issuing the command, but today we will look at a different method.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of individually accessing each child object, we can simply <strong>broadcast <\/strong>a command to every child object, much like a parent would yell to all of their children at once when it is time for dinner.   We do this through the <strong>BroadcastMessage( )<\/strong> function, which is similar to the <strong>SendMessage( ) <\/strong>function, but where as SendMessage( ) only calls this function on the components of a single target object, BroadcastMessage( ) will issue the message to every child object, and their children as well. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When our MotherShipScript&#8217;s coroutine causes the attackers to move, we also issue a <strong>SwapFrames( )<\/strong> call to every object like so:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; highlight: [6,7]; title: ; quick-code: false; notranslate\" title=\"\">\n\/\/ move to the side\nfor (int = 0; i &lt; stepsToSide; i++) {\n    \/\/ move by the offset\n    transform.position = transform.position + sideStepVector;\n\n    \/\/ run swap frames\n    BroadcastMessage(&quot;SwapFrames&quot;);\n\n    \/\/ wait for the interval\n    yield return new WaitForSeconds(timeBetweenSteps);\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The benefit of this method of calling a function is that we can have multiple scripts that share the same method name and it will be called on all of them. (You already use something similar to this &#8211; <strong>Update( )<\/strong>, <strong>Start( )<\/strong>, and<strong> Awake( ) <\/strong>are examples of functions that respond to a <strong>message<\/strong>.)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Part 1-B: &#8230; and cool Explosions!<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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&nbsp;<strong>EnemyExplode( )&nbsp;<\/strong>command in our <strong>EnemyScript<\/strong>, like so:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n    private void EnemyExplode()\n    {\n        \/\/ turn off the other objects and turn on the explosion frame\n        enemyFrameOne.SetActive(false);\n        enemyFrameTwo.SetActive(false);\n        enemyExplode.SetActive(true);\n\n        \/\/ Make the enemy explosion noise\n        SoundManager.S.MakeEnemyExplosionSound();\n        \n\n        \/\/ get all of the rigidbodies\n        Rigidbody&#x5B;] enemyBlocks = GetComponentsInChildren&lt;Rigidbody&gt;();\n\n        foreach (Rigidbody block in enemyBlocks)\n        {\n            block.AddExplosionForce(explosionForce, (transform.position + Vector3.back * 2f), explosionRadius, 0f, ForceMode.Impulse);\n\n        }\n        \/\/ orphan the explosion object\n        enemyExplode.transform.parent = null;\n    }\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">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 class=\"wp-block-paragraph\">In our explode command, you see that we declared a variable as \u201cRigidbody[ ] enemyBlocks\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 enemyBlocks[ ]) we set that object to the variable \u201cblock\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\n<p class=\"wp-block-paragraph\">One thing that you will notice is that our other frames keep popping back into view. This is because we set our Coroutine to run continuously. We can try to prevent this by issuing a&nbsp;<strong>StopCoroutine( )&nbsp;<\/strong>command, but there is a better solution &#8211; let the Enemy object be destroyed on impact, like it was.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, this creates a conundrum.  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?   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<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nenemyExplode.transform.parent = null;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">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>EnemyExplode( ) <\/strong>command runs prior to the command that destroys the Enemy object, our stunt double makes it out alive (but not for long).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, we are going to insert some special effects, using an explosion prefab built with Unity&#8217;s particle system.  (More on these later&#8230;).   You can download this prefab in the package posted on the course Box site.   By adding this prefab as a child of our <strong>AlienExplode<\/strong> prefab, it will appear and play once when the stunt double wakes up.  It make a fiery explosion with a bright orange light, sends out a &#8220;shockwave&#8221;, and shoots some smoking embers off into the distance. <\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"210\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-13-300x210.png\" alt=\"\" class=\"wp-image-361\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-13-300x210.png 300w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-13-1024x717.png 1024w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-13-768x538.png 768w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-13.png 1083w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Part 2: Improving the Layout<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This still looks a lot like a Unity demo, and less like a real game.  In this workshop section, we make some simple cosmetic changes to the game to create an alien landscape.  Through the addition of some primitive shapes (a ground plane and some &#8220;mountains&#8221;), the addition of some color (via some new materials), and the removal of the skybox (by setting the camera background to solid color), we have added a lot of personality to our game.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"657\" height=\"369\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-12.png\" alt=\"\" class=\"wp-image-360\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-12.png 657w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-12-300x168.png 300w\" sizes=\"auto, (max-width: 657px) 100vw, 657px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Part 3-A: Audio Strategy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When we are making our game, there are a number of decisions that will center around how we want to treat the sounds in our game.  When should they be playing in our game?  What should be emitting the sound?  Do I need to have control over the sound, such as starting and stopping playback, or adjusting the speed or pitch?   Are my audio clips short or long?  How often can I expect to hear each sound?  These questions will inform our decisions as to which objects should emit what sounds, and how much control we may need to exert over them through code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For most of my games, my sounds tend to fall into one of four categories:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Omnipresent Sounds<\/strong> &#8211; these are sounds that will generally play throughout an experience, such as background music, or ambient noise.  The sound is continuous, long, and probably loops.  It tends to remain at a steady volume, regardless of position.  For these types, I prefer to use an <strong>empty object<\/strong> that contains only an <strong>AudioSource<\/strong> component dedicated to playing the one sound, with the <strong>loop<\/strong> property selected.  These may be set to play on <strong>awake<\/strong> or respond to a <strong>Play( )<\/strong> command, depending on the start\/stop conditions.<\/li>\n\n\n\n<li><strong>Object Sounds<\/strong> &#8211; these sounds have an obvious point of origin.  We perceive them as coming from some object in our scene &#8211; perhaps a character, perhaps an inanimate object.   These may be lines of dialog, footsteps, music coming from a radio, a cat meowing, or a twinkling sound to indicate a point of interest.   We expect these sounds to come from &#8220;somewhere&#8221; as opposed to &#8220;everywhere&#8221; so often we will want them to have a spatial quality or directionality.   These are the most likely to be controlled by some behavior or react to some condition.  In this case, I expect these to be generated from an AudioSource component that is attached to a <strong>GameObject<\/strong> or the child of an object in the scene, and managed some script, likely on the object itself.  These sources will often have the <strong>3D<\/strong> sound at least partially implemented, and will likely rely on the <strong>Play( )<\/strong> command.<\/li>\n\n\n\n<li><strong>Prefab Sounds<\/strong> &#8211; I see these as &#8220;object sounds on autopilot&#8221;.  There is no logic expected to control the sound, they simply play when instantiated, and may continue for the duration of the instance object.  These are best used for environmental sound effects that are a little longer or loop (such as a motor running, or a crackling fire), or a larger sound effect (such as a door opening, or longer explosion).   Generally this involves a prefab object with an <strong>AudioSource<\/strong> with a preset <strong>AudioClip<\/strong> that will <strong>Play On Awake<\/strong> and may or may not<strong> Loop<\/strong>.  This can be 2D or 3D sound depending on if your noise needs a point of origin.  These will play through until the object is destroyed, or the clip is finished (if not looping).<\/li>\n\n\n\n<li><strong>One-Shot Sounds<\/strong> &#8211; these are useful for quick sounds, especially repeating sounds, or sound that may overlap. <strong>PlayOneShot( )<\/strong> is a play-and-forget function&#8230; once it begins you cannot stop it until it has played through.  Also, this does not loop.  It must be applied to an audio source, so it will respect the settings of that objects component, such as the spatial quality (and thus position of the object).  This type is best for quick sound effects, especially those that may overlap from the same source.  Footsteps, gunshots, UI noises, beeps, scoring noises,  quick audio effects that don&#8217;t need to be controlled or shut off.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For our game, we chose audio clips for bullet and bomb sounds and attach those to their respective prefabs.  These will play automatically when the prefab is spawned, and will stop playing immediately if their host object is destroyed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For our explosion sounds, we will want to spawn those separately into the scene, so that we can de-activate them if our Player object is destroyed.  (That is a solemn moment, we want to make sure to isolate that noise for maximum impact).  As long as we are spawning explosion sounds, we should also <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, we set up a separate object that plays on awake and will host our background music \/ ambient sound, and play on a loop.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 3-B: Sound Manager (Singleton)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Back when we discussed our \u201ctrain station\u201d metaphor, I mentioned a special method for assigning and calling a single instance of an object, known as the <strong>\u201cSingleton\u201d<\/strong> pattern. This is a way of declaring a class so that there is only ever one single instance of it.  In other situations, the Singleton instantiates itself.  Our code, however, is living inside the confines of our game engine, so we will still need to attach it to an object that exists in our scene, rather than relying on self-instantiation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Think of the Singleton as being similar to the Presidency. At any given time, there is only one President. A new President may come along and replace the old, and when they do the former ceases to be the President. The responsibilities of the office only ever point to one person at a time\u2026 the current President.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So the Singleton pattern is a programming method by which we can define a particular instance of a class within the class declaration itself, so that we never have to worry about \u201cfinding\u201d or establishing a relationship or link\u2026 we simply call the only instance by calling a property in the class that holds the instance itself.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sounds confusing, and it kind of is, but just roll with it and you\u2019ll see how this works.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, we need to create something to generate the sounds.  I first create an empty game object by going to <strong>Create &gt; Create Empty,<\/strong> and naming it &#8220;SoundManager&#8221;. I then add a script to this, which I also name &#8220;SoundManager&#8221;. (These don&#8217;t have to share the same name, this is just a personal preference as I only expect to have one of these).  I also add an <strong>AudioSource<\/strong> component to the SoundManager object&#8230; this is what will emit our <strong>PlayOneShot( )<\/strong> sounds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In our Sound Manager script, I create a variable &#8220;audio&#8221; to hold the reference to the <strong>AudioSource<\/strong> component, and I assign it in the <strong>Start( ) <\/strong>command with <strong>GetComponent&lt;AudioSource&gt;( )<\/strong>.  <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now we need to make our SoundManager into a singleton.  This way we can adjust sounds or modify them or turn them off as needed.  If we instantiate our sounds as children of the Sound Manager, we only need to look internally to make adjustments, rather than performing costly &#8220;find&#8221; processes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first step to declare a singleton is to define a public static version of the class within itself, like so:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic class SoundManager: MonoBehaviour\n{\n\n    public static SoundManager S;\n\n...\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Weird, right? We just set up a&nbsp;<strong>SoundManager <\/strong>type variable inside of our&nbsp;<strong>SoundManager <\/strong>type script?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Weirder still, watch what we do next:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nprivate void Awake()\n{\n        S = this; \/\/ Singleton Definition\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">As our object wakes up, it declares itself to be this value \u201cS\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What is \u201cpublic static\u201d anyways? The \u201cpublic\u201d designation is pretty easy \u2013 it is a value that can be accessed from outside of the class. But the \u201cstatic\u201d designation means that&nbsp;<em>all instances of the class share the same value for that variable.&nbsp;<\/em>So in theory, you can create as many SoundManagers as you want, but if you access the \u201cS\u201d variable, you will always get a reference to the same object, the last one that woke up and set itself to \u201cthis\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So why is this useful? Because now we no longer have to find our SoundManager. We can simply get directly to the active instance of our SoundManager script by writing&nbsp;<strong>SoundManager.S.<\/strong><em>{{whatever public variable or method}}<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As a demonstration of this, we create a public function named <strong>MakeEnemySound( )<\/strong> which will play my enemy advancement sound.  The declaration is simple:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic void MakeTheEnemyAdvanceNoise()\n{\n    \/\/ make our enemy advance sounds\n    managerAudio.PlayOneShot(enemyAdvanceSoundClip);\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Now we can add a command in our MotherShipScript.cs script that calls this particular function to generate the sound by writing:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nSoundManager.S.MakeTheEnemyAdvanceNoise();\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">It all simply works.<\/p>\n\n\n\n<p class=\"has-very-light-gray-background-color has-background has-small-font-size wp-block-paragraph\"><strong>NOTE<\/strong> \u2013 Often times, the variable name \u201cS\u201d is used as shorthand for Singleton, but you can choose whatever name you want. In the video workshop, I decided to name the SoundManager &#8220;Steve&#8221;, so our script made calls to&nbsp;<strong>SoundManager.Steve.StopAllSounds( )<\/strong>&nbsp;instead. &#8220;S&#8221; is just a commonly used shorthand because Singleton starts with S.  Another popular singleton designator is &#8220;instance&#8221;.   But I like giving them names&#8230; and personalities. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next we want to make the explosion sound for our Enemy objects.  These could be one shot sounds, however they do last for a little bit, and I want to make sure I cut off all sounds when our player object is destroyed.  Because of this, I am going to make these sounds prefab objects and I will instantiate them into the scene, so that I can destroy them if I need to cut them off early.  <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic void MakeEnemyExplosionSound()\n{\n    GameObject thisExplosion = Instantiate(enemyExplosionSoundPrefab, transform);\n    Destroy(thisExplosion, 5.0f);\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Here, we generate an instance of the explosion prefab.  We also set a self-destruct timer of 5 seconds to clean up the scene.  (We could make this one second, but this better illustrates the point.)   We also use the version of <strong>Instantiate( )<\/strong> that takes a Game Object and Transform argument.  By passing the current object&#8217;s <strong>transform<\/strong>, we make the Sound Manager the parent object.  This will come in handy later when we want to stop all sounds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Although we did not do this as part of the workshop, were we to set up a Player explosion sound I would likely set this up as a PlayOneShot( ) so that it emits from our manager&#8217;s Audio Source, and call that from the collision script on our player object.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we set up a function to stop all of the sounds, named <strong>StopAllSounds(<\/strong> <strong>)<\/strong>.  We set up a public AudioSource named &#8220;backgroundMusic&#8221;, associate our background track,and create two functions, <strong>StartTheMusic( )<\/strong> and <strong>StopTheMusic( )<\/strong> that run the Play and Stop commands on the associated AudioSource when this function is called. of the associate our ambient sound object.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally we set up a function to handle the player explosion and turn off all playing sounds.  We get an array of all child objects that have the <strong>AudioSource<\/strong> component (this includes the Background Music object, which we made a child of the Sound Manager object).  Next, we move through each AudioSource component in the array and issue the &#8220;Stop&#8221; command.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n    public void PlayerExplosionSequence()\n    {\n        \/\/ turn off all currently playing sounds\n        AudioSource&#x5B;] childAudioSources = GetComponentsInChildren&lt;AudioSource&gt;();\n        foreach (AudioSource thisChild in childAudioSources)\n        {\n            thisChild.Stop();\n        }\n\n        \/\/ play the player explosion sound\n\n    }\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Part 4: Sharper Text (with TextMeshPro)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One thing that you may have noticed when working with the text objects is that they do not scale well.  When scaled up to a larger size, the characters in the text often appear to be blurry, with fuzzy edges.  This is because Unity uses a &#8220;bitmap&#8221; texture &#8211; an image of the font &#8211; to build your text.  It chops up the texture into small rectangles and displays those on your screen.  Depending on the original size of your font, this jagged effect &#8211; an artifact of upscaling the anti-aliased font image &#8211; may be quite pronounced.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"457\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-14-1024x457.png\" alt=\"\" class=\"wp-image-362\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-14-1024x457.png 1024w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-14-300x134.png 300w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-14-768x343.png 768w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-14.png 1295w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Thankfully, Unity now offers us an alternative solution that can render crisp text at any resolution. TextMeshPro was once a third-party asset, but was so popular that Unity acquired it and now includes it with the engine.  It takes a few more steps to set up, and is implemented in a slightly different manner, but the end results are spectacular.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"502\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-15-1024x502.png\" alt=\"\" class=\"wp-image-363\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-15-1024x502.png 1024w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-15-300x147.png 300w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-15-768x377.png 768w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-15.png 1397w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Left: Unity&#8217;s legacy text | Right: TextMeshPro<br>Both set to a font size of 18<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">TextMeshPro differs from the legacy Unity text system in that it does not display bitmap textures \u2013 instead it uses a technique called a Signed Distance Field (SDF). In simple terms, it uses a low resolution image that looks like a blurry version of the font texture and applies a bunch of crazy graphics math on those images to generate a crisp looking render at any distance. If you would like to read more about the process, check out&nbsp;<a href=\"https:\/\/steamcdn-a.akamaihd.net\/apps\/valve\/2007\/SIGGRAPH2007_AlphaTestedMagnification.pdf\">this SIGGRAPH paper<\/a>&nbsp;from the folks at Valve who invented the technique.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"702\" height=\"705\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-16.png\" alt=\"\" class=\"wp-image-364\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-16.png 702w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-16-300x300.png 300w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-16-150x150.png 150w\" sizes=\"auto, (max-width: 702px) 100vw, 702px\" \/><figcaption class=\"wp-element-caption\">A close-up of the <strong>Font Atlas<\/strong><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">What this means for us is that we cannot simply load and assign a font as we can with a&nbsp;<strong>Text<\/strong>&nbsp;object. Instead, we need to generate a&nbsp;<strong>Font Atlas<\/strong>&nbsp;by importing a font and letting TextMeshPro process it into one of these fancy blurry texture maps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To begin, launch the font creator by going to&nbsp;<strong>Window &gt; TextMeshPro &gt; Font Asset Creator<\/strong>. If this is the first time you have launched TextMeshPro in this project, you will get a menu asking you to&nbsp;<strong>Import TMP Essentials<\/strong>. Click this and install. You do not need to load the Examples &amp; Extras.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Importing Fonts<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">In the Font Asset Creator, assign a&nbsp;<strong>Source Font File<\/strong>&nbsp;\u2013 this is a font file that you have already loaded into your Assets folder. As a reminder, Unity supports both True-Type Font (.ttf) and Open-Type Font (.otf) formats. Click the&nbsp;<strong>Generate Font Atlas<\/strong>&nbsp;button and the creator will generate the font image. Select&nbsp;<strong>Save As\u2026<\/strong>&nbsp;to save the font atlas to your asset folder.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">TextMeshPro will also create a folder in your Asset directory that includes some instructions and documentation if you want to dig deeper into the settings.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">TextMeshPro UI<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">To create a TextMeshPro UI object, select the option from the UI menu, such as&nbsp;<strong>Create &gt; UI &gt; TextMeshPro \u2013 Text<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a TextMeshPro object, the Text component is replaced with a&nbsp;<strong>TextMeshPro UGUI<\/strong>&nbsp;component with more settings than before. One of the first things you will notice is that you will load a&nbsp;<strong>Font Asset<\/strong>&nbsp;instead of a&nbsp;<strong>Font<\/strong>. Directly below that is the&nbsp;<strong>Material Preset<\/strong>. This will refer to the \u201cMaterial\u201d settings at the bottom of the panel \u2013 the options that allow you to modify the appearance of your TMP object with effects like Outline, Underlay, or Bevel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What you will notice is that by default, as you create more items they will also share the same Material settings. To create a new instance of the material, right-click the Material panel and select&nbsp;<strong>Create Material Preset<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>NOTE:&nbsp;<\/strong>Some features like Bevel are only available when the material shader is set to TextMeshPro\/Distance Field (Surface Shader). In general this is slightly more computationally expensive, but for our project will not make much of a difference.<\/p>\n\n\n\n<h4 id=\"tmpinworld\" class=\"wp-block-heading\">TextMeshPro: In-World<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">If you want to place TextMeshPro Objects in the 3D world, you will use a different type of TMP object. You can make these by selecting&nbsp;<strong>Create &gt; 3D Object &gt; Text \u2013 TextMeshPro<\/strong>. This will place a TMP objects into the worldspace. Although it has a&nbsp;<strong>Rect Transform<\/strong>, it will work the same way as a regular transform in terms of worldspace position, orientation, and scale.<\/p>\n\n\n\n<h4 id=\"tmpvalue\" class=\"wp-block-heading\">Changing TextMeshPro Text Values<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">In the past, we used&nbsp;<strong>GetComponent&lt;Text&gt;.text<\/strong>&nbsp;to set the value of a text string. TextMeshPro objects have their own component types \u2013 one for UI, and one for In-World objects. To access these, you must also call the&nbsp;<strong>TMPro<\/strong>&nbsp;library.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nusing UnityEngine.UI;\nusing TMPro;\n \n...\n \nGetComponent&lt;Text&gt;().text = &quot;This is how you set a Unity UI Text value&quot;;\n \nGetComponent&lt;TextMeshPro&gt;().text = &quot;This is how you set a TMPro In-World text value&quot;;\n \nGetComponent&lt;TextMeshProUGUI&gt;().text = &quot;This is how youet a TMPro UI value&quot;;\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><strong>Note:&nbsp;<\/strong>There is a big difference between TextMeshProUGUI and TextMeshPro components. One is for UI, the other for in-world. They are not interchangeable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Part 5: The Game Manager (also a Singleton)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">During class, 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As discussed our game flow looks like this:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"827\" src=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-2-1024x827.png\" alt=\"\" class=\"wp-image-76\" srcset=\"https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-2-1024x827.png 1024w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-2-300x242.png 300w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-2-768x621.png 768w, https:\/\/courses.etc.cmu.edu\/53-353\/wp-content\/uploads\/2023\/10\/image-2.png 1193w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<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>\n\n\n\n<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>\n\n\n\n<li>Upon completion of the <strong>GetReady <\/strong>state, we move into <strong>Playing<\/strong>, <\/li>\n\n\n\n<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>\n\n\n\n<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>\n\n\n\n<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>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">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 class=\"wp-block-paragraph\">We declare our variable to hold the Singleton&#8230;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic static GameManager S; \/\/ set up the singleton\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">&#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<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nprivate void Awake()\n{\n    \/\/ Singleton Definition\n    if (GameManager.S) {\n        \/\/ game manager exists, destroy this object\n        Destroy(this.gameObject);\n    }  else  {\n        S = this;\n    }\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Next we define a number of enumerated values for our various gamestates:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic enum GameState { Menu, PreRound, Playing, PostRound, GameOver};\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">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.   We can do a similar thing for the GameOver state with the R key:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n    void Update()\n    {\n        if (currentState == GameState.Menu)\n        {\n            if (Input.GetKeyDown(KeyCode.S))\n            {\n                \/\/ start a new game\n                StartANewGame();\n            }\n        } else if (currentState == GameState.GameOver)\n        {\n            if (Input.GetKeyDown(KeyCode.R))\n            {\n                \/\/ restart the game\n                ResetGame();\n            }\n        }\n    }\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">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 class=\"wp-block-paragraph\">We also create ResetRound( ) and StartRound( ) 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.  ResetRound 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<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n    private void ResetRound()\n    {\n        \/\/ does current mothership exist?\n        if (currentMotherShip)\n        {\n            \/\/ if so, get rid of it\n            Destroy(currentMotherShip);\n        }\n\n        \/\/ spawn the mother ship\n        currentMotherShip = Instantiate(motherShipPrefab);\n\n        \/\/ run the Get Ready coroutine\n        StartCoroutine(GetReady());\n    }\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>StartRound( )<\/strong> moves us to the <strong>Playing<\/strong> state, and we remain here until it is time to end a round.  For our class, 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<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n    private void OnCollisionEnter(Collision collision)\n    {\n        if (collision.transform.tag == &quot;EnemyBomb&quot;)\n        {\n            \/\/ player object is destroyed\n            GameManager.S.PlayerObjectDestroyed();\n        }\n    }\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">So the enemy bomb hits the player, and player calls out to the GameManager and tells it to run the public method PlayerObjectDestroyed( ).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n    public void PlayerObjectDestroyed()\n    {\n        currentState = GameState.PostRound;\n\n        \/\/ this is a tragedy!  stop the enemies\n        currentMotherShip.GetComponent&lt;MotherShipScript&gt;().StopTheAttack();\n\n\n        \/\/ stop all sounds and play the player explosion sound\n        SoundManager.S.PlayerExplosionSequence();\n\n        \/\/ process my lives remaining\n        livesRemaining--;\n\n        if (livesRemaining &gt; 0)\n        {\n            StartCoroutine(OopsState());\n        } \n        \/\/ else the game is over and you lost\n\n    }\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">PlayerDestroyed( ) removes a life from our <strong>livesRemaining <\/strong>integer.  If there are still lives remaining, we move to the <strong>Oops <\/strong>state via a coroutine that operates similar to our <strong>GetReady <\/strong>state.   Otherwise we move to the <strong>GameOver<\/strong> state.<\/p>\n\n\n\n<p class=\"has-very-light-gray-background-color has-background has-small-font-size wp-block-paragraph\">NOTE &#8211; In this version, I only destroyed the player.  For the homework, you will need to make sure to delete the player object and respawn it.  This behavior will be similar to the Mothership object.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our game is moving through the stages relatively well, but some things are still not behaving quite the way we would prefer.   It&#8217;s time to dig into some states to add some detail.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first thing that I don&#8217;t like is that the player can always move and shoot, so 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 during the <strong>playing<\/strong> state.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nif (GameManager.S.currentState == GameState.Playing) { \n   ...\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Again, note that there is no need to associate the game manager with any object in our Player script.  No reference to be made.  GameManager.S is available to be called globally.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next, I don&#8217;t like 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 class=\"wp-block-paragraph\">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> and<strong> StopTheAttack( )<\/strong> in the MotherShipScript, and then call those from GameManager using:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\ncurrentMotherShip.GetComponent&lt;MotherShipScript&gt;().StopTheAttack();\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">In our StopTheAttack( ) function, we call the StopAllCoroutines( ) function.  This will cease any coroutines currently running from the component.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic void StopTheAttack()\n{\n    StopAllCoroutines();\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Finally, we add some messaging so that we understand our current state. <\/p>\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<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; quick-code: false; notranslate\" title=\"\">\nusing UnityEngine;\nusing TMPro;\nusing System.Collections;\n\npublic enum GameState {Menu, PreRound, Playing, PostRound, GameOver}\n\npublic class GameManager : MonoBehaviour\n{\n    \/\/ game variables\n    private int score;\n    private int livesRemaining;\n    private int LIVES_AT_START = 3;\n\n    \/\/ Game Manager Singleton\n    public static GameManager Gary;\n\n    public GameState state = GameState.Menu;\n\n    \/\/ UI Variables\n    public TextMeshProUGUI messageOverlay;\n\n    \/\/ game objects\n    public GameObject motherShipPrefab;\n    private GameObject currentMotherShip;\n\n    void Awake()\n    {\n        \/\/ check for an existing singleton\n        if (Gary)\n        {\n            \/\/ a singleton already exists\n            Destroy(this.gameObject);\n        }\n        else\n        {\n            \/\/ no singleton exists\n            Gary = this; \/\/ singleton definition\n        }\n    }\n\n    \/\/ Start is called once before the first execution of Update after the MonoBehaviour is created\n    void Start()\n    {\n        \/\/ set up the initial menu screen\n        GoToMenu();\n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        if (state == GameState.Menu &amp;&amp; (Input.GetKeyDown(KeyCode.S)))\n        {\n            StartANewGame();\n        }\n    }\n\n    private void GoToMenu()\n    {\n        \/\/ set the state\n        state = GameState.Menu;\n\n        \/\/ set the message overlay to the start message\n        messageOverlay.enabled = true;\n        messageOverlay.text = &quot;Press \\&quot;S\\&quot; to Start&quot;;\n    }\n\n    private void StartANewGame()\n    {\n        \/\/ reset our score\n        score = 0;\n\n        \/\/ reset our lives\n        livesRemaining = LIVES_AT_START;\n\n        \/\/ Reset the Round\n        ResetRound();\n    }\n\n    private void ResetRound()\n    {\n        if (currentMotherShip)\n        {\n            Destroy(currentMotherShip);\n        }\n        \n        \/\/ spawn a mothership\n        currentMotherShip = Instantiate(motherShipPrefab);\n\n        \/\/ go to the get ready state\n        StartCoroutine(GetReady());\n    }\n\n    private IEnumerator GetReady()\n    {\n        \/\/ enter preround state\n        state = GameState.PreRound;\n\n        \/\/ set the message\n        messageOverlay.enabled = true;\n        messageOverlay.text = &quot;Get Ready!!!&quot;;\n\n        \/\/ start the music\n        SoundManager.Steve.StartTheMusic();\n\n        \/\/ wait for 3 seconds\n        yield return new WaitForSeconds(3.0f);\n\n        \/\/ turn off the messages\n        messageOverlay.enabled = false;\n\n        StartRound();\n    }\n\n    private void StartRound()\n    {\n        \/\/ move to the playing state\n        state = GameState.Playing;\n\n        \/\/ tell the mothership to start the attack\n        currentMotherShip.GetComponent&lt;MotherShipScript&gt;().StartTheAttack();\n    }\n\n    public void PlayerWasDestroyed()\n    {\n        \/\/ sound manager to stop all sounds and play the explosion\n        SoundManager.Steve.PlayerExplosionSequence();\n\n        \/\/ stop the attack\n        currentMotherShip.GetComponent&lt;MotherShipScript&gt;().StopTheAttack();\n\n        \/\/ post round state\n        StartCoroutine(OopsState());\n    }\n\n    private IEnumerator OopsState()\n    {\n        \/\/ move to the post round state\n        state = GameState.PostRound;\n\n        \/\/ lose a life\n        livesRemaining--;\n\n        \/\/ put up a message\n        messageOverlay.enabled = true;\n        messageOverlay.text = &quot;You Failed.&quot;;\n\n        \/\/ wait for 2s\n        yield return new WaitForSeconds(2f);\n\n        \/\/ do we have any lives left? \n        if (livesRemaining &gt; 0)\n        {\n            \/\/ reset the round\n            ResetRound();\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">SoundManager.cs<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; quick-code: false; notranslate\" title=\"\">\nusing UnityEngine;\n\npublic class SoundManager : MonoBehaviour\n{\n    public static SoundManager Steve;\n\n    &#x5B;Header(&quot;Sound Resources&quot;)]\n    public AudioSource backgroundMusic;\n    public AudioClip enemyAdvanceSound;\n    public GameObject enemyExplosionSoundPrefab;\n    public AudioClip playerExplosion;\n\n    private AudioSource thisAudio;\n\n    private void Awake()\n    {\n        Steve = this;\n    }\n\n    \/\/ Start is called once before the first execution of Update after the MonoBehaviour is created\n    void Start()\n    {\n        thisAudio = GetComponent&lt;AudioSource&gt;();\n        backgroundMusic.Stop(); \/\/ temporary stop\n    }\n\n    public void MakeEnemyAdvanceSound()\n    {\n        thisAudio.PlayOneShot(enemyAdvanceSound);\n    }\n\n    public void MakeEnemyExplosionSound()\n    {\n        \/\/ instantiate the prefab\n        GameObject thisSound = Instantiate(enemyExplosionSoundPrefab, transform);\n        Destroy(thisSound, 1.5f);\n    }\n\n    public void StartTheMusic()\n    {\n        backgroundMusic.Play();\n    }\n\n    public void StopTheMusic()\n    {\n        backgroundMusic.Stop();\n    }\n\n    public void PlayerExplosionSequence()\n    {\n        \/\/ stop the music\n        StopTheMusic();\n\n        \/\/ if any spawned sounds are running, turn them off\n        AudioSource&#x5B;] audioSources = GetComponentsInChildren&lt;AudioSource&gt;();\n        foreach (AudioSource childSource in audioSources)\n        {\n            \/\/ stop playback\n            childSource.Stop();\n        }\n\n        \/\/ make the enemy explosion sound\n        thisAudio.PlayOneShot(playerExplosion);\n    }\n}\n\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">MotherShipScript.cs<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; quick-code: false; notranslate\" title=\"\">\nusing UnityEngine;\nusing System.Collections;\n\npublic class MotherShipScript : MonoBehaviour\n{\n    public int stepsToSide;\n    public float sideStepUnits, downStepUnits;\n    public float timeBetweenSteps, timeBetweenBombs;\n\n    public void StartTheAttack()\n    {\n        StartCoroutine(MoveMother());\n        StartCoroutine(DropOneBomb());\n    }\n\n    public void StopTheAttack()\n    {\n        StopAllCoroutines();\n    }\n\n    public IEnumerator MoveMother()\n    {\n        \/\/ build the offset\n        Vector3 sideStepVector = Vector3.right * sideStepUnits;\n        Vector3 downStepVector = Vector3.down * downStepUnits;\n\n        \/\/ only run while there are children  (this will keep it looping)\n        while (transform.childCount &gt; 0)\n        {\n            \/\/ move to the side\n            for (int i = 0; i &lt; stepsToSide; i++)\n            {\n                \/\/ move to the side\n                transform.position = transform.position + sideStepVector;\n\n                \/\/ swap the frames\n                BroadcastMessage(&quot;SwapFrames&quot;);\n\n                \/\/ make an advance noise\n                SoundManager.Steve.MakeEnemyAdvanceSound();\n\n                \/\/ wait\n                yield return new WaitForSeconds(timeBetweenSteps);\n            }\n\n            \/\/ move down\n            transform.position += downStepVector;\n\n            \/\/ swap the frames\n            BroadcastMessage(&quot;SwapFrames&quot;);\n\n            \/\/ make an advance noise\n            SoundManager.Steve.MakeEnemyAdvanceSound();\n\n            yield return new WaitForSeconds(timeBetweenSteps);\n\n            \/\/ switch directions   \n            sideStepVector.x *= -1f;\n        }\n    }\n\n    private IEnumerator DropOneBomb()\n    {\n        \/\/ some way of stopping this\n        bool _isRunning = true;\n\n        while (_isRunning)\n        {\n            \/\/ wait for the duration\n            yield return new WaitForSeconds(timeBetweenBombs);\n\n            \/\/ decide who drops a bomb\n\n            \/\/ see how many children we have...\n            int enemyCount = transform.childCount;\n\n            \/\/ if we have children, proceed\n            if (enemyCount &gt; 0)\n            {\n                \/\/ randomly select one child\n                int enemyIndex = Random.Range(0, enemyCount);\n\n                \/\/ get the enemy script from that child\n                EnemyScript thisEnemyScript = transform.GetChild(enemyIndex).GetComponent&lt;EnemyScript&gt;();\n\n                if (thisEnemyScript)\n                {\n                    \/\/ tell the enemy script to drop a bomb\n                    thisEnemyScript.DropABomb();\n                }\n            }\n            else\n            {\n                \/\/ else, no children, stop the script\n                _isRunning = false;\n            }\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">EnemyScript.cs<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; quick-code: false; notranslate\" title=\"\">\nusing UnityEngine;\n\npublic class EnemyScript : MonoBehaviour\n{\n    public GameObject bombPrefab;\n\n    &#x5B;Header(&quot;Enemy Swap Frames&quot;)]\n    public GameObject enemyFrame1;\n    public GameObject enemyFrame2;\n    public GameObject enemyFrameExplode;\n\n    &#x5B;Header(&quot;Explosion Parameters&quot;)]\n    public float explosionForce;\n    public float explosionRadius;\n\n    \/\/ Start is called once before the first execution of Update after the MonoBehaviour is created\n    void Start()\n    {\n        \/\/ turning on the first frame, turning off the others\n        enemyFrame1.SetActive(true);\n        enemyFrame2.SetActive(false);\n        enemyFrameExplode.SetActive(false);\n    }\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        \/\/ test code to drop a bomb\n        if (Input.GetKeyDown(KeyCode.B))\n        {\n            DropABomb();\n        }\n\n        if (Input.GetKeyDown(KeyCode.P))\n        {\n            SwapFrames();\n        }\n\n        if (Input.GetKeyDown(KeyCode.E))\n        {\n            MakeExplosion();\n        }\n    }\n\n    public void SwapFrames()\n    {\n        \/\/ reverse the active values of frames 1 &amp; 2\n        enemyFrame1.SetActive(!enemyFrame1.activeSelf);\n        enemyFrame2.SetActive(!enemyFrame2.activeSelf);\n    }\n\n    public void DropABomb()\n    {\n        \/\/ instantiate a bomb at this position (but slightly below)\n        Instantiate(bombPrefab, (transform.position + Vector3.down), Quaternion.identity);\n    }\n\n    void OnCollisionEnter(Collision collision)\n    {\n        \/\/ was it a bullet that hit us?\n        if (collision.transform.tag == &quot;PlayerBullet&quot;)\n        {\n            \/\/ destroy the bullet\n            Destroy(collision.gameObject);\n\n            \/\/ destroy this object\n            Destroy(this.gameObject);\n\n            \/\/ make the explosion\n            MakeExplosion();\n\n            \/\/ make the explosion sound\n            SoundManager.Steve.MakeEnemyExplosionSound();\n        }\n    }\n\n    private void MakeExplosion()\n    {\n        \/\/ turn off the animation frames\n        \/\/ turn on the explosion frame\n\n        enemyFrame1.SetActive(false);\n        enemyFrame2.SetActive(false);\n        enemyFrameExplode.SetActive(true);\n\n        \/\/ get all of the rigidbodies\n        Rigidbody&#x5B;] enemyBlocks = GetComponentsInChildren&lt;Rigidbody&gt;();\n\n        \/\/ for each one\n        foreach (Rigidbody block in enemyBlocks)\n        {\n            \/\/ add the explosive force\n            block.AddExplosionForce(explosionForce, (transform.position + Vector3.back), explosionRadius, 0f, ForceMode.Impulse);\n        }\n\n         \/\/ make the explode frame an orphan\n        enemyFrameExplode.transform.parent = null;       \n    }\n}\n\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-preformatted\">PlayerScript.cs<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; quick-code: false; notranslate\" title=\"\">\nusing UnityEngine;\n\npublic class PlayerScript : MonoBehaviour\n{\n    public float speed; \/\/ speed for the player object, expressed in units per second\n    public float offset; \/\/ how far can the player object move\n\n    public GameObject bulletPrefab;\n\n    \/\/ Update is called once per frame\n    void Update()\n    {\n        if (GameManager.Gary.state == GameState.Playing)\n        {\n            \/\/ check for player movement\n            MovePlayer();\n\n            \/\/ check for bullet firing\n            if (Input.GetKeyDown(KeyCode.Space))\n            {\n                FireBullet();\n            }\n        }\n    }\n\n    private void MovePlayer()\n    {\n        \/\/ move by the horizontal speed\n        Vector3 currentPosition = transform.position;  \/\/ where are we\n\n        \/\/ offset by speed * input * deltatime\n        currentPosition.x = currentPosition.x + (Input.GetAxisRaw(&quot;Horizontal&quot;) * speed * Time.deltaTime);\n\n        \/\/ clamp the value\n        currentPosition.x = Mathf.Clamp(currentPosition.x, -offset, offset);\n\n        \/\/ put the value back\n        transform.position = currentPosition;\n    }\n\n    private void FireBullet()\n    {\n        \/\/ make a bullet\n        GameObject bulletObject = Instantiate(bulletPrefab, (transform.position + Vector3.up), Quaternion.identity);\n\n        \/\/ set the self destruct timer\n        Destroy(bulletObject, 3f);\n    }\n\n    void OnCollisionEnter(Collision collision)\n    {\n        if ((collision.gameObject.tag == &quot;EnemyBomb&quot;) &amp;&amp; GameManager.Gary.state == GameState.Playing)\n        {\n            \/\/ let the game manager know the player was destroyed\n            GameManager.Gary.PlayerWasDestroyed();\n        }\n    }\n}\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this week&#8217;s class, we discussed strategies for audio, and covered the Singleton pattern &#8211; a very powerful tool (but also a potentially tricky\/dangerous one) for organizing our game and exposing our &#8220;manager&#8221; scripts to the objects in our scene. The videos this week will cover updates to our Astral Attackers game to build a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-69","post","type-post","status-publish","format-standard","hentry","category-recap"],"blocksy_meta":"","featured_image_urls":{"full":"","thumbnail":"","medium":"","medium_large":"","large":"","1536x1536":"","2048x2048":""},"post_excerpt_stackable":"<p>In this week&#8217;s class, we discussed strategies for audio, and covered the Singleton pattern &#8211; a very powerful tool (but also a potentially tricky\/dangerous one) for organizing our game and exposing our &#8220;manager&#8221; scripts to the objects in our scene. The videos this week will cover updates to our Astral Attackers game to build a Sound Manager, create the Game Manager which will handle our game state and flow, and finally we will make some visual improvements to our environment and to our enemies to spice things up. Part 1-A: Making Better Enemies&#8230; Our previous alien attackers were rather primitive&hellip;<\/p>\n","category_list":"<a href=\"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/category\/recap\/\" rel=\"category tag\">Course Recap<\/a>","author_info":{"name":"Tom Corbett","url":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/author\/tcorbett\/"},"comments_num":"0 comments","_links":{"self":[{"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/posts\/69","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/comments?post=69"}],"version-history":[{"count":9,"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/posts\/69\/revisions"}],"predecessor-version":[{"id":564,"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/posts\/69\/revisions\/564"}],"wp:attachment":[{"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/media?parent=69"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/categories?post=69"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.etc.cmu.edu\/53-353\/index.php\/wp-json\/wp\/v2\/tags?post=69"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}