<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Class Recap &#8211; Understanding Game Engines</title>
	<atom:link href="https://courses.ideate.cmu.edu/53-353/m2023/?feed=rss2&#038;cat=6" rel="self" type="application/rss+xml" />
	<link>https://courses.ideate.cmu.edu/53-353/m2023</link>
	<description>53-353 &#124; Summer 2023 &#124; Entertainment Technology Center &#124; Carnegie Mellon University</description>
	<lastBuildDate>Wed, 21 Jun 2023 16:21:26 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2.5</generator>
	<item>
		<title>Day 26: FPS Game</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=121</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Wed, 21 Jun 2023 15:54:33 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=121</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=121"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Today, we are looking at some advanced Unity features that we can use to create our own &#8220;first person shooter&#8221; (FPS) style game.  We will take a look at tools for quick level generation, dive into Unity&#8217;s Character Controller, take a deeper examination of the offerings of the Physics library, and finally dip our toes into some Enemy AI.  </p>



<p>All lessons this week are strictly educational, so there is no deliverable based on these, but I strongly recommend trying to follow along as these systems are very useful for developing your own games.</p>



<span id="more-121"></span>



<h2 class="wp-block-heading">Part 1: Level Prototyping (with ProBuilder)</h2>



<p>ProBuilder is a very useful plugin for Unity, extending the editor by offering simple (but significantly improved) modeling and alignment tools. &nbsp;Once its own standalone product in the Asset Store, Unity has purchased it and now offers it free in the package manager.</p>



<p>ProBuilder is helpful if you need to quickly prototype some objects, rough out a level design, or generate a quick mesh for collisions. &nbsp;This provides enhanced mesh generation, and access down to the vertex level, as well as a quick way to create and adjust UV mapping for objects. &nbsp;&nbsp;</p>



<p>Another plugin from the same creators, PolyBrush, lets you sculpt, shape, and paint your meshes. &nbsp;It is also available for free in the Package Manager.</p>



<h3 class="wp-block-heading">Installing ProBuilder</h3>



<ul>
<li>Open the “Package Manager”, accessed by clicking on&nbsp;<strong>Window &gt; Package Manager</strong></li>



<li>Select the&nbsp;<strong>“Unity Registry”</strong>&nbsp;option to see all available packages</li>



<li>Scroll down until you see the “ProBuilder” entry, and then click&nbsp;<strong>“Install”</strong>&nbsp;&nbsp;</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-25.png" alt="" class="wp-image-152"/></figure></div>


<h3 class="wp-block-heading">ProBuilder Interface</h3>



<p>To start ProBuilder, navigate the top menu to&nbsp;<strong>Tools &gt; ProBuilder &gt; ProBuilder Window.</strong>&nbsp;</p>



<p>This will launch a window with either Text or Icon buttons. &nbsp;You can switch between modes by right-clicking a blank area in the window and selecting the other menu type.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-26.png" alt="" class="wp-image-153"/><figcaption class="wp-element-caption">ProBuilder Text Menu</figcaption></figure></div>


<p>Notice that when you launch ProBuilder, you will also see a small icon menu in your Scene window. &nbsp;This indicates the selection type when interacting with a ProBuilder object.</p>



<p>These are, from left to right:</p>



<ul>
<li><strong>OBJECT Selection</strong></li>



<li><strong>VERTEX Selection</strong></li>



<li><strong>EDGE Selection</strong></li>



<li><strong>FACE Selection</strong></li>
</ul>



<p>The ProBuilder Button menu will change depending on the selection type that is active.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2020/06/image-56.png" alt="" class="wp-image-203"/></figure></div>


<p>By using these selectors, you can grab just a part of an object and use the various transform tools to deform them into the shapes that you want.  The ProBuilder menu contains a number of tools that can split, offset, subdivide, and transform these.   Additionally, you can use the &#8220;shift&#8221; key to generate new geometry, including Shift-Move (face) to Extrude and  Shift-Scale (face) to create an inset poly.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-1.png" alt="" class="wp-image-265"/><figcaption class="wp-element-caption">Shift-Move <strong>(Extrude)</strong> will generate new geometry along the offset</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-2.png" alt="" class="wp-image-266"/><figcaption class="wp-element-caption">Shift-Scale <strong>(Inset)</strong> will shrink the face and generate border polys</figcaption></figure></div>


<p>Additionally, you can use Vertex Color to set the color tint of surfaces.  You can also set materials, and make adjustments to the UVW mapping (the way that texture images are applied to the surfaces.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full is-resized"><img decoding="async" loading="lazy" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-3.png" alt="" class="wp-image-267" width="514" height="333"/></figure></div>


<p>ProBuilder can also <strong>Export</strong> a model into common 3D formats, including OBJ and STL.  (I recommend using .obj as that tends to be the most common amongst other modeling applications).    </p>



<p class="has-black-color has-luminous-vivid-amber-background-color has-text-color has-background"><strong>ADVICE:</strong>&nbsp;ProBuilder is great for quickly generating or editing assets, but is not recommended to be used throughout the entire process. The procedural generation of the mesh at runtime makes it susceptible to small errors, at which point your work may be lost without an option for recovery. Once you are happy with an asset, it is highly recommended that you run the&nbsp;<strong>EXPORT&nbsp;</strong>process to convert it into a mesh file in your Asset folder.</p>



<h4 class="wp-block-heading">Resources</h4>



<ul>
<li><a href="https://unity3d.com/unity/features/worldbuilding/probuilder">ProBuilder Resources (Unity)</a></li>



<li><a href="https://docs.unity3d.com/Packages/com.unity.probuilder@5.0/manual/index.html">ProBuilder Documentation (Unity)</a></li>
</ul>



<h2 class="wp-block-heading">Part 2: The Character Controller</h2>



<p>In this section, we cover the how to implement Unity&#8217;s character controller as a FPS controller.  Unity does have a premade FPS-style player object available in their Standard Assets, however it requires that you upgrade your project to the newer Input system.  Since our lessons have focused on the legacy input system, we go about implementing our own version using the <a href="https://docs.unity3d.com/ScriptReference/CharacterController.html" data-type="URL" data-id="https://docs.unity3d.com/ScriptReference/CharacterController.html" target="_blank" rel="noreferrer noopener"><strong>CharacterController</strong> </a>component.</p>



<p>The first step is to create an object that will be our Player, and for this I like to use a <strong>Capsule</strong> primitive, as it roughly translates to a human-sized object of 2 units tall and 1 unit in diameter.  I disable the <strong>Capsule Collider</strong>, and instead add the <strong>CharacterController </strong>component from the list of <strong>Physics</strong> components.  </p>



<p>The CharacterController also uses a capsule-shaped collider, however this particular collider behaves differently than others.  First, it does not rotate, so we don&#8217;t have to worry about tipping over.  We will not be using a Rigidbody component to move this, rather we will write a different script that will move (or attempt to move) the collider, much in the same way that we commanded our 2D collider to &#8220;move&#8221; in each update.   Next, this controller does not return Collision events, instead it has its own collision method that is thrown when it contacts a collider.</p>



<p>You may notice that the controller has some unique settings, such as <strong>Step Offset</strong> and <strong>Slope Limit</strong>.  These are settings that give the controller information about what is considered &#8220;traversable&#8221; (meaning that we can travel on it).   A slope climb above this range (expressed in degrees) or a vertical step above the offset value (in units) will prevent the character from making that movement. </p>



<p>We move the CharacterController with one of two commands &#8211; <a rel="noreferrer noopener" href="https://docs.unity3d.com/ScriptReference/CharacterController.Move.html" data-type="URL" data-id="https://docs.unity3d.com/ScriptReference/CharacterController.Move.html" target="_blank"><strong>Move( )</strong> </a>and <strong><a rel="noreferrer noopener" href="https://docs.unity3d.com/ScriptReference/CharacterController.SimpleMove.html" data-type="URL" data-id="https://docs.unity3d.com/ScriptReference/CharacterController.SimpleMove.html" target="_blank">SimpleMove( )</a></strong>.     SimpleMove allows us to move only in the X &amp; Z planes (and ignores Y data from the Vector3) and it automatically applies gravity, so this method is best for games where jumping is not an option.   Since we want the ability to jump, we are going to use the Move( ) command instead.  In either case, you should only make one call to Move or SimpleMove per frame, due to the way that Unity processes these.</p>



<p>To make our character move, we are going to create a new script that I call <strong>PlayerFPS</strong>, and use the sample code from the <a href="https://docs.unity3d.com/ScriptReference/CharacterController.Move.html" data-type="URL" data-id="https://docs.unity3d.com/ScriptReference/CharacterController.Move.html">CharacterController.Move( )</a> page.   By running this, we see that our player is movement is not quite what we might expect.  Our character movement is restricted to the two axis, and there is a &#8220;turning&#8221; that does not affect what way we move, even though we turn partway.  We are going to have to make some edits.</p>



<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 class Example : MonoBehaviour
{
    private CharacterController controller;
    private Vector3 playerVelocity;
    private bool groundedPlayer;
    private float playerSpeed = 2.0f;
    private float jumpHeight = 1.0f;
    private float gravityValue = -9.81f;

    private void Start()
    {
        controller = gameObject.AddComponent&lt;CharacterController>();
    }

    void Update()
    {
        groundedPlayer = controller.isGrounded;
        if (groundedPlayer &amp;&amp; playerVelocity.y &lt; 0)
        {
            playerVelocity.y = 0f;
        }

        Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
        controller.Move(move * Time.deltaTime * playerSpeed);

        if (move != Vector3.zero)
        {
            gameObject.transform.forward = move;
        }

        // Changes the height position of the player..
        if (Input.GetButtonDown("Jump") &amp;&amp; groundedPlayer)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
        }

        playerVelocity.y += gravityValue * Time.deltaTime;
        controller.Move(playerVelocity * Time.deltaTime);
    }
}</pre>



<p>In this example version, the Update( ) function does the following:</p>



<ol>
<li>Gets the value of controller.<strong>isGrounded</strong>.  This returns a boolean indicating whether or not the most recent move caused the controller to intersect with a collider underneath it.</li>



<li>If isGrounded is true, it sets the y-value of the &#8220;playerVelocity&#8221; variable to 0.  This is because the controller is considered &#8220;on the ground&#8221; and so is &#8220;resetting&#8221; the effect of gravity.  PlayerVelocity is the vector representation of the speed with which a player is moving downward, to replicate gravity.</li>



<li>Next a &#8220;move&#8221; directional vector is created using the horizontal and vertical axis, and the <strong>Move( )</strong> command is called with that vector multiplied by speed and Time.deltaTime to indicate how far it should move in this frame.</li>



<li>The next part rotates the game object to face the movement direction.  This is not necessary for our purposes and we will comment this out of our version.</li>



<li>Now the script checks the &#8220;jump&#8221; input button.  If true, and if the player is grounded, it will apply a positive y force to the playervelocity variable.</li>



<li>Finally, the player velocity adds the gravityvelocity (-9.81) multiplied by Time.deltaTime to create the effects of gravity, and again issues the <strong>Move( )</strong> command.  Yes, this goes against Unity&#8217;s own advice about not calling Move( ) twice in one frame, but it works here because one call handles X-Z movement, and one handles Y movement only. </li>
</ol>



<p>In the accompanying video, I outline these changes:  </p>



<ul>
<li>First, I remove the &#8220;turning&#8221; function from step 4.  </li>



<li>I set a camera as the child of the player object (and make this our primary camera for game view)</li>



<li>Next I create a MouseLook function to control our player object.  Using <strong>Input.GetAxis(&#8220;Mouse X&#8221;)</strong>  I can translate horizontal mouse movement into player rotation around the y-axis (or up-axis). </li>



<li>Now that our player body is being rotated,  I redefine our PlayseFPS movement command to use the Horizontal and Vertical input axes as scalar values to apply towards the &#8220;forward&#8221; and &#8220;right&#8221; vectors of my player transform.  This will return the world vectors of our local directions.  I pass that value into the Move command and this allows us to move and strafe in a classic FPS style. </li>



<li>Next I update MouseLook to rotate the camera up and down in response to the <strong>Mouse Y</strong> movement, implementing a <strong>Clamp</strong> to make sure we don&#8217;t rotate too far.</li>
</ul>



<p> One key difference in using the CharacterController is that we are not using a standard collider.   If we attempt to have our object run into objects with Rigidbodies, we will simply stop as though they are a solid wall.  Instead, we must use<strong> <a rel="noreferrer noopener" href="https://docs.unity3d.com/ScriptReference/CharacterController.OnControllerColliderHit.html" data-type="URL" data-id="https://docs.unity3d.com/ScriptReference/CharacterController.OnControllerColliderHit.html" target="_blank">OnCharacterColliderHit( )</a></strong> that will inform us that a collision has occurred.  Using the sample code from Unity&#8217;s site, we can see that we find the rigidbodies that we have hit, and use that to push the object.  </p>



<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 class Example : MonoBehaviour
{
    // this script pushes all rigidbodies that the character touches
    float pushPower = 2.0f;
    void OnControllerColliderHit(ControllerColliderHit hit)
    {
        Rigidbody body = hit.collider.attachedRigidbody;

        // no rigidbody
        if (body == null || body.isKinematic)
        {
            return;
        }

        // We dont want to push objects below us
        if (hit.moveDirection.y &lt; -0.3)
        {
            return;
        }

        // Calculate push direction from move direction,
        // we only push objects to the sides never up and down
        Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);

        // If you know how fast your character is trying to move,
        // then you can also multiply the push velocity by that.

        // Apply the push
        body.velocity = pushDir * pushPower;
    }
}</pre>



<p> </p>



<h2 class="wp-block-heading">Part 3: Physics</h2>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-27-1024x624.png" alt="" class="wp-image-154"/></figure></div>


<h3 class="wp-block-heading">ForceMode Types</h3>



<p>When we first started using Rigidbody Physics way back in the days of Pong (or BingoBangoBongo, as we called it), we set our ball in motion with a push that we created using&nbsp;<strong>AddForce( )</strong>. We passed a rather large force vector to make such a small and light object move at the speed we anticipated. This is due to the methods which Unity uses to apply forces to an object, known as the ForceMode.</p>



<p>There are four types of ForceMode:</p>



<ul>
<li>Force</li>



<li>Acceleration</li>



<li>Impulse</li>



<li>VelocityChange</li>
</ul>



<h4 class="wp-block-heading">ForceMode.Force (default)</h4>



<p>The default mode, ForceMode.Force is designed to apply a force over time to an object. It takes the<strong>&nbsp;mass</strong>&nbsp;of the object into consideration, so it will take more force to move heavier objects. It is designed to be used in&nbsp;<strong>FixedUpdate( )</strong>, as it will calculate the force to apply across that fixed time. The unit of force is calculated as:</p>



<p><em>(mass * distance) / (time^2)</em></p>



<p>This explains why our force needed to be so high to push a 1 kg object around the field of play. Our game provided one super quick burst, while this method is designed to be called repeatedly over time. A good use case would be a “thruster” that is called during every fixed update command.</p>



<h4 class="wp-block-heading">ForceMode.Acceleration</h4>



<p>Similar to ForceMode.Force, a force calculated over the time of FixedUpdate( ), except the force is applied equally to objects without regard to mass. Useful for purposes such as gravity effects, or driving games where the mass of the vehicle does not matter. The unit of force is applied as:</p>



<p><em>distance / (time^2)</em></p>



<h4 class="wp-block-heading">ForceMode.Impulse</h4>



<p>This mode takes the application of time out of the equation and delivers the force in one single burst, while taking mass into consideration. This mode is best for explosions, with an instant release of energy, but larger objects will affected less by it. The unit of force here is expressed as:</p>



<p><em>(mass * distance) / time</em></p>



<h4 class="wp-block-heading">ForceMode.VelocityChange</h4>



<p>Similar to impulse, but mass no longer has an effect. This mode instantly alters the velocity of the affected object by the value applied. (Remember, velocity is expressed as units per 1s). The “force” unit here is expressed as:</p>



<p><em>distance / time</em></p>



<p>For our experiment, we created a ball prefab with the following script. We set up two balls side by side, one with a weight of “1” and the other a weight of “50”. We created a similar pairing for each ForceMode, and set the applied force value to be equal across all eight balls.</p>



<p>ForceMode.Force required the most effort, rapidly clicking the mouse to barely move the heavy ball, while making some progress getting the light ball up the hill. ForceMode.Acceleration made things a little easier, as the heavy and light ball responded equally to each click. ForceMode.Impulse had a much greater effect, moving the heavy ball a greater distance than Acceleration had, and launching the light ball off of the board. ForceMode.VelocityChange launched both light and heavy balls far into the distance.</p>



<h3 class="wp-block-heading">Raycasting</h3>



<p>Detail Here</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-4.png" alt="" class="wp-image-270"/></figure>



<h3 class="wp-block-heading">Overlay Sphere</h3>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-5-1024x610.png" alt="" class="wp-image-271"/></figure></div>


<h2 class="wp-block-heading">Part 4: The Navmesh</h2>



<p>While it is fun to wander around blowing things up, I created this level for something more. I want to create enemy objects that have some intelligence and can pursue me through the level. In order to do this, I created a prefab variant of the Barrel that I named “AngryBarrel”. The Angry Barrel is recognizable by it’s greenish hue, it’s white cube on top to help indicate forward direction, and it’s ever present bloodlust.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-7.png" alt="" class="wp-image-273"/></figure></div>


<p>In order to make this object “intelligent”, or at least smart enough to figure out how to navigate my level, I need to define the areas that it is capable of travelling. This involves generating a surface shape that is comprised of all of the valid positions that one of the intelligent agents could occupy, which we call a&nbsp;<strong>Navmesh</strong>.</p>



<p>To create a NavMesh, you must first open the Navigation window, which can be reached by going to&nbsp;<strong>Window &gt; AI &gt; Navigation</strong>. This will open up another panel usually in the same area as the Inspector.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-15.png" alt="" class="wp-image-184"/></figure></div>


<p>In order to create a Navmesh, you must tell the system which objects to include, and what the rules are about what is and is not accessible terrain. With the&nbsp;<strong>Object&nbsp;</strong>tab active, I selected all of the mesh objects that I wished to be part of my navigable layer, and checked the&nbsp;<strong>Navigation Static</strong>&nbsp;property, which indicates that this is an element to be included in the baking process. (You can include any object with either a Mesh Renderer or Terrain component)</p>



<p>Next, I set the conditions for the Agent Size that the bake should consider. This will analyze your navigation objects, and generate a simplified mesh that outlines the valid locations where an agent with these dimensions would fit. Finally I hit the&nbsp;<strong>Bake</strong>&nbsp;button, and soon I have my navmesh.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-16.png" alt="" class="wp-image-185"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-17.png" alt="" class="wp-image-186"/><figcaption class="wp-element-caption">The blue area indicates that this is somewhere the agent can travel. Note that there is a buffer around walls and under low overhangs.</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-18.png" alt="" class="wp-image-187"/></figure></div>


<p>The “slope” property defines the maximum sloped angle to be considered traversable. The “step height” indicates how many units high the agent can “step up”. Thanks to the step settings, I was able to create a somewhat safe zone where the enemy cannot follow me up a slope, but must instead go around to use the stairs.</p>



<p>Next we add a&nbsp;<strong>Navmesh Agent</strong>&nbsp;component to our enemy objects, and then a small script to tell that Agent component where to go. If we feed the agent a position, it will automatically move at the designated speed to to that point considering the shortest path and moving around obstacles and other agents.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-19.png" alt="" class="wp-image-188"/></figure></div>


<figure class="wp-block-image size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2021/wp-content/uploads/2021/11/image-6-1024x477.png" alt="" class="wp-image-272"/></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">PlayerFPS.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class PlayerFPS : MonoBehaviour
{
    private CharacterController controller;
    private Vector3 playerVelocity;
    public bool groundedPlayer;
    public float playerSpeed = 2.0f;
    private float jumpHeight = 1.0f;
    private float gravityValue = -9.81f;

    public float pushPower = 2.0f;

    private void Start()
    {
        // controller = gameObject.AddComponent&lt;CharacterController>();
        controller = gameObject.GetComponent&lt;CharacterController>();
    }

    void Update()
    {
        // checking for the ground, and stopping our fall if grounded
        groundedPlayer = controller.isGrounded;
        if (groundedPlayer &amp;&amp; playerVelocity.y &lt; 0)
        {
            playerVelocity.y = 0f;
        }

        // moving the character on XZ
        // Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));

        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");

        Vector3 move = (transform.right * x) + (transform.forward * z);
        
        
        controller.Move(move * Time.deltaTime * playerSpeed);

        /* unnecessary
        if (move != Vector3.zero)
        {
            gameObject.transform.forward = move;
        }
        */

        // application of jumping
        if (Input.GetButtonDown("Jump") &amp;&amp; groundedPlayer)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
        }
        // application of gravity
        playerVelocity.y += gravityValue * Time.deltaTime;
        controller.Move(playerVelocity * Time.deltaTime);
    }

    // this script pushes all rigidbodies that the character touches
    
    void OnControllerColliderHit(ControllerColliderHit hit)
    {
        Rigidbody body = hit.collider.attachedRigidbody;

        // no rigidbody
        if (body == null || body.isKinematic)
        {
            return;
        }

        // We dont want to push objects below us
        if (hit.moveDirection.y &lt; -0.3)
        {
            return;
        }

        // Calculate push direction from move direction,
        // we only push objects to the sides never up and down
        Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);

        // If you know how fast your character is trying to move,
        // then you can also multiply the push velocity by that.

        // Apply the push
        body.velocity = pushDir * pushPower;
    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">MouseLook.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class mouseLook : MonoBehaviour
{
    public Transform playerBody;
    public float mouseSpeed = 100.0f;

    public float tiltRotation = 0.0f;

    // Start is called before the first frame update
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
    }

    // Update is called once per frame
    void Update()
    {
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");

        playerBody.Rotate(Vector3.up * (mouseX * mouseSpeed * Time.deltaTime));

        // tilt rotation
        tiltRotation -= mouseY * mouseSpeed * Time.deltaTime;
        // clamp
        tiltRotation = Mathf.Clamp(tiltRotation, -90.0f, 90.0f);
        // rotate the camera
        transform.localRotation = Quaternion.Euler(tiltRotation, 0, 0);

        // visualize the gaze direction
        Debug.DrawRay(transform.position, (transform.forward * 5.0f), Color.cyan);
    }
}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">BarrelScript.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class BarrelScript : MonoBehaviour
{
    public GameObject smokePrefab;
    public GameObject explosionPrefab;
    
    
    public float forceRadius;
    public float forceApplied;
    public ForceMode forceMode;

    private bool _isLive = true;

    public void BarrelImpact()
    {
        // barrel has been hit, make it explode
        if (_isLive) { StartCoroutine(BarrelBurning());}
    }

    private void BarrelExplode()
    {
        _isLive = false;

        // make the barrel blow up
        Instantiate(explosionPrefab, transform.position, Quaternion.identity);

        Collider[] hitColliders = Physics.OverlapSphere(transform.position, forceRadius);

        foreach(Collider collider in hitColliders)
        {
           
            BarrelScript thisBarrel = collider.gameObject.GetComponent&lt;BarrelScript>();
            if (thisBarrel) { thisBarrel.BarrelImpact(); }

            Rigidbody rb = collider.gameObject.GetComponent&lt;Rigidbody>();
            if (rb)
            {
                rb.AddExplosionForce(forceApplied, transform.position, forceRadius, 10.0f, forceMode);
            }


        }


        Destroy(this.gameObject);
    }

    private IEnumerator BarrelBurning()
    {
        // start the smoke
        Instantiate(smokePrefab, transform.position, Quaternion.Euler(new Vector3(-90, 0,0)), transform);

        // wait for the fuse
        yield return new WaitForSeconds(2.0f);

        // time to die
        BarrelExplode();
    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">RaycastScript.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class RaycastScript : MonoBehaviour
{
    private AudioSource audio;
    public AudioClip gunshotSound;

    private void Start()
    {
        audio = GetComponent&lt;AudioSource>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // make the gun noise
            audio.PlayOneShot(gunshotSound);

            // check for a valid shot
            Transform cameraTransform = Camera.main.transform; // kind of a singleton

            // build my ray
            Ray ray = new Ray(cameraTransform.position, cameraTransform.forward); // return pos-z axis ray
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.tag == "Barrel")
                {
                    // tell the barrel to explode
                    BarrelScript barrelScript = hit.transform.GetComponent&lt;BarrelScript>();

                    // verify that I have a component
                    if (barrelScript)
                    {
                        barrelScript.BarrelImpact();
                    }
                }
            }

        }

        
    }


}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">EnemyScript.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class EnemyScript : MonoBehaviour
{
    private NavMeshAgent agent;

    public Transform player;

    // Start is called before the first frame update
    void Start()
    {
        agent = GetComponent&lt;NavMeshAgent>();
        StartCoroutine(UpdateTarget());
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private IEnumerator UpdateTarget()
    {
        while(true) { 
            bool foundRoute = agent.SetDestination(player.position);
            Debug.Log("Destination = " + foundRoute);

            yield return new WaitForSeconds(2.0f);
        }
    }

}
</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 25: 2D Attacks</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=228</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Tue, 20 Jun 2023 20:34:28 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=228</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=228"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s not always &#8220;one jump, one kill&#8221; in the land of side-scrolling platformers.  Today we looked at some alternate approaches to handling player/enemy interactions.  (These are not required for your final project&#8230; you already have everything you need there.  This week is all about how to do other cool things with Unity Engine now that you know the basics! )</p>



<span id="more-228"></span>



<h2 class="wp-block-heading">Part 1: Simplifying our Controller</h2>



<p>One aspect of our initial controller continues to bother me, and that is the Ground Transform object that we ended up using.  On the one hand, it is convenient because when we flip our character, that child object also inherits that flip.   On the other hand, we have to select the object that is NOT our controller in order to adjust the transform position. </p>



<p>In the first part of today&#8217;s lesson, we rewrite aspects of the SideScrollerController that handle the ground-check position.  Along the way we encountered some issues, because <strong>Vector3</strong> math can be selective at times.   Our process was to replace reference to that GroundCheck child object with a simple offset value (recorded as a <strong>Vector2</strong>) that we would add to the transform of our character object.    </p>



<p>This created issues when we attempted to add a <strong>Vector2</strong> to a <strong>Vector3</strong>, but this was resolved by simply converting the Vector2 into a Vector3 by declaration.  Then we added that offset to the object position.  Also, to account for conditions where we face opposite directions, we multiply the Vector2 value by transform.localScale (which is how we are flipping our character)</p>



<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 FixedUpdate()
{
    Vector3 groundCheckOffset = groundCheckPosition * transform.localScale;

    // Check if player is grounded
    Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position + groundCheckOffset, colliderRadius, groundLayer);

    // check for a grounding collider
    isGrounded = colliders.Length > 0 ? true : false;

}</pre>



<p class="has-luminous-vivid-amber-background-color has-background has-small-font-size">There was also an unfortunate bug introduced by the &#8220;conveyer&#8221; aspects that we added last time.  This prevented us from initiating movement from a standstill.  Removing the &#8220;conveyer&#8221; components of our move commands resolved this issue.</p>



<h2 class="wp-block-heading">Part 2: Other Enemy Death Options</h2>



<p>Next we adjusted our enemy object to die differently.   We created a simple cylindrical object that would serve as our enemy.  We adjusted our <strong>EnemyScript</strong> to get these objects out of the way by placing them on a different layer (one that would interact with the &#8220;Ground&#8221; layer, but nothing else).  This prevents the player from colliding with them once the death trigger has been hit.  We also used the SpriteRenderer&#8217;s &#8220;Color&#8221; property to change the sprite to gray, and turned off the &#8220;freeze rotation&#8221; option on the Rigidbody2D.   Finally, a slight velocity assured that our character would &#8220;fall over&#8221;.</p>



<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 EnemyHasDied()
{
    // no longer alive
    _isAlive = false;

    // destroy the instance
    Destroy(this.gameObject, 3.0f);

    // change the color
    sprite.color = Color.gray;

    // roll over
    rb.freezeRotation = false;

    // put this on the dead layer 
    this.gameObject.layer = 13;
    rb.velocity = Vector3.right;

}</pre>



<h2 class="wp-block-heading">Part 3: Pushback</h2>



<p>Up to this point, our enemy has been capable of one-shotting our Player character, but many times these games feature a &#8220;health&#8221; system, which allows for multiple hits before player death.  One issue commonly encountered when first coding these health-based mechanics is accounting for multiple successive hits.   Our enemy characters keep pushing ahead, often times this can cause multiple hits before your player has time to react.  To address this, we introduce two mechanics &#8211; The first is a &#8220;stunned&#8221; state, where the player is incapable of controlling movement, but is also invulnerable for a period of time.   </p>



<p>We accomplish this by introducing a &#8220;stunned&#8221; boolean to the character controller, and testing that when we use our input or process collisions with enemies.  We only process the collision if <strong>stunned == false</strong>, otherwise we ignore it.  If we DO get hit, then we set &#8220;stunned&#8221; to <strong>true</strong>, and run an animation (here we keyframe the color value of the sprite to give a red tint that fades out over one second).  We use Animation Events to call another function that ends our stunned status, setting the value back to false.</p>



<p> The second mechanic is a &#8220;pushback&#8221; force&#8230; if we are hurt by an enemy, we should be propelled back in the opposite direction.  here we accomplish this by drawing a line between the two characters and applying a force in the opposite direction.</p>



<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 OnCollisionEnter2D(Collision2D collision)
{
    if (collision.transform.tag == "Enemy")
    {
        if (controller.stunned == false)
        {
            // take damage
            controller.stunned = true;

            // run the hurt animation
            animator.SetTrigger("Stunned");

            Vector3 pushback = collision.transform.position - transform.position;
            GetComponent&lt;Rigidbody2D>().velocity = pushback * -pushbackForce;


        }
    }
}</pre>



<h2 class="wp-block-heading">Part 4: Melee Attacks (Hitbox and Hurtbox)</h2>



<p>Often times our characters have attack animations, such as the one in the Ninja pack that we used for today&#8217;s class.  These attack areas often fall outside of the original sprite dimensions, meaning that we must define a volume that will be the area of the attack&#8217;s effect.  This is know as a <strong>hitbox</strong> and conversely, the area that is vulnerable to damage is the <strong>hurtbox</strong>.  In the case of our Enemy object, the Box Collider trigger is our hurtbox.  (Our player, on the other hand, is one giant hurtbox.)</p>



<p>To define this hitbox, we created a similar system to our &#8220;Ground Check&#8221; &#8211; defining an point relative to the player object, and a radius of effect.   Once we attached this to an &#8220;attack&#8221; animation, we noticed that the death was instant, before the sword even reached the enemy objects.  To counteract this, we created another Animation Event that calls the hitbox check midway through the swing, which improves the timing to match the movement. </p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">Player.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public int score = 0;

    public bool isAlive = true;

    private SideScrollerController controller;
    private Animator animator;

    public float pushbackForce;

    [Header("Attack Check Values")]
    public float attackXOffset = 0;
    public float attackYOffset = 0;
    public float attackRadius = 0.5f;
    public LayerMask attackLayer;

    private void Start()
    {
        controller = GetComponent&lt;SideScrollerController>();
        animator = GetComponent&lt;Animator>();
    }

    private void Update()
    {
        if (Input.GetButtonDown("Fire1"))
        {
            
            if (!controller.stunned)
            {
                // call the attack
                PlayerAttack();

            }
            
        }
    }

    private void PlayerAttack()
    {
        // queue the animation
        animator.SetTrigger("Attack");

        // make the attack happen



    }

    public void ProcessAttack()
    {
        // offset vector
        Vector3 offset = new Vector3(attackXOffset, attackYOffset);

        offset.x *= transform.localScale.x;

        // run physics
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position + offset, attackRadius, attackLayer);

        foreach (Collider2D thisCollider in colliders)
        {
            // yell "hit" at the object
            thisCollider.gameObject.SendMessage("SuccessfulHit");
        }

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.transform.tag == "EndOfLevel")
        {
            LevelManager.S.NextLevel();
        }
        
        if (collision.transform.tag == "EndOfGame")
        {
            LevelManager.S.GoToStartMenu();
        }

    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.transform.tag == "Enemy")
        {
            if (controller.stunned == false)
            {
                // take damage
                controller.stunned = true;

                // run the hurt animation
                animator.SetTrigger("Stunned");

                Vector3 pushback = collision.transform.position - transform.position;
                GetComponent&lt;Rigidbody2D>().velocity = pushback * -pushbackForce;


            }
        }
    }

    public void StunExpires()
    {
        controller.stunned = false;
    }

    public void PlayerHasDied()
    {
        LevelManager.S.RestartLevel();
    }


    private void OnDrawGizmos()
    {

        // offset vector
        Vector3 offset = new Vector3(attackXOffset, attackYOffset);

        offset.x *= transform.localScale.x;

        // Draw a yellow sphere at the ground check position
        Gizmos.color = Color.cyan;
        Gizmos.DrawWireSphere(transform.position + offset, attackRadius);


    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">EnemyScript.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class EnemyScript : MonoBehaviour
{
    public float speed;
    public bool faceLeft = true;

    public bool _isAlive = true;

    private Rigidbody2D rb;
    private SpriteRenderer sprite;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent&lt;Rigidbody2D>();
        sprite = GetComponent&lt;SpriteRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        /*
        if (faceLeft &amp;&amp; sprite.flipX)
        {
            sprite.flipX = false;
        }
        else if (!faceLeft &amp;&amp; !sprite.flipX)
        {
            sprite.flipX = true;
        }
        */

        if (faceLeft == sprite.flipX) { sprite.flipX = !sprite.flipX; }
    }

    private void FixedUpdate()
    {
        if (_isAlive)
        {
            float direction = faceLeft ? -1 : 1;
            rb.velocity = new Vector2(direction * speed, rb.velocity.y);
        }

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "TurnAround")
        {
            faceLeft = !faceLeft; // invert faceleft
        }

        if (collision.gameObject.tag == "Player")
        {
            EnemyHasDied();
        }

    }

    public void SuccessfulHit()
    {
        if(_isAlive)
        {
            EnemyHasDied();
        }
    }

    private void EnemyHasDied()
    {
        // no longer alive
        _isAlive = false;

        // cue the death animation 
        // GetComponent&lt;Animator>().SetTrigger("EnemyDied");

        // increase the score
        // GameManager.S.IncreaseScore(50);

        // destroy the instance
        Destroy(this.gameObject, 3.0f);

        // change the color
        sprite.color = Color.gray;

        // roll over
        rb.freezeRotation = false;

        // put this on the dead layer 
        this.gameObject.layer = 13;
        rb.velocity = Vector3.right;

    }



}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">SideScrollerController.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]

/*
 *  SideScrollerController - created for UGE Summer 23
 *  Based on Sharp Coder 2D Controller (https://sharpcoderblog.com/blog/2d-platformer-character-controller)
 */

public class SideScrollerController : MonoBehaviour
{
    // Player Movement Variables
    public float maxSpeed = 3.4f;
    public float jumpHeight = 6.5f;
    public float gravityScale = 1.5f;
    float moveDirection = 0;
    public bool isGrounded = true;
    public bool isConveyer = false;
    public bool facingRight = true;

    public LayerMask conveyerLayer;

    // public Transform groundCheckObject;
    [Header("Ground Check Values")]
    public Vector2 groundCheckPosition;
    public float colliderRadius = 0.5f;
    public LayerMask groundLayer;

    private Animator animator;

    public bool stunned = false;



    // components
    Rigidbody2D r2d;
    CapsuleCollider2D mainCollider;
    // Transform t;

    // Start is called before the first frame update
    void Start()
    {
        // set the component variables
        r2d = GetComponent&lt;Rigidbody2D>();
        mainCollider = GetComponent&lt;CapsuleCollider2D>();

        // setting some component properties
        r2d.freezeRotation = true;
        r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
        r2d.gravityScale = gravityScale;

        // set the initial facing right value
        facingRight = transform.localScale.x > 0;

        animator = GetComponent&lt;Animator>();
        
    }

    // Update is called once per frame
    void Update()
    {

        // Movement controls
        moveDirection = Input.GetAxis("Horizontal");


        // set animation controls
        // animator.SetFloat("speed", Mathf.Abs(moveDirection));
        // animator.SetBool("isOnGround", isGrounded);

        // TO-DO:  Implement Change Facing Direction

        if (moveDirection > 0 &amp;&amp; !facingRight)
        {
            facingRight = true;
            transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }
        if (moveDirection &lt; 0 &amp;&amp; facingRight)
        {
            facingRight = false;
            transform.localScale = new Vector3(-Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }




        // Jumping
        if (Input.GetButtonDown("Jump") &amp;&amp; isGrounded){
            r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
        }


    }

    void FixedUpdate()
    {
        if (!stunned) { 
            r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);
        }

        Vector3 groundCheckOffset = groundCheckPosition * transform.localScale;

  
        // Check if player is grounded
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position + groundCheckOffset, colliderRadius, groundLayer);

        // check for a grounding collider
        isGrounded = colliders.Length > 0 ? true : false;


    }

    private void OnDrawGizmos()
    {

        Vector3 groundCheckOffset = groundCheckPosition * transform.localScale;

        // Draw a yellow sphere at the ground check position
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(transform.position + groundCheckOffset, colliderRadius);


    }

    /*
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.transform.tag == "Enemy")
        {
            // Death happens here

            // queue the death animation
            animator.SetTrigger("Death");
        }
    }
    */

}
</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 24: Animation Events / 2D Advanced Topics</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=115</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Fri, 16 Jun 2023 18:08:08 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=115</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=115"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>For this class, we looked at a few more advanced techniques to enhance our platform games.  The first is Animation Effects, which allow us to call a scripted event from inside an animation clip, which we used to make a &#8220;coin box&#8221;.  Next we looked at 2D Effectors, physics components that let us add behaviors to our collider objects.  Let&#8217;s go!</p>



<span id="more-115"></span>



<h2 class="wp-block-heading">Part 1: Animation Events</h2>



<p>Today we created a classic element from the old side-scrollers, the coin box.  Super Mario Brothers featured these boxes throughout their levels, and they were a wonderful devices.  A player would jump from underneath, the box would shift up slightly to indicate that your actions caused a reaction, and a spinning coin would pop out, increasing your coin count and playing a happy coin sound to reinforce that a good thing happened, and your brain would release just a little more dopamine.  Ahhh&#8230;.</p>



<p>To construct our coin box, I first created a prefab element that contains a box sprite from our environment sprite sheet, and that sprite has a box collider to receive the impact event, and the animator component so that we can make it move.  (Remember, if you make the moving object a child of the prefab parent object, then the transform animation will be in relationship to its steady parent object.  This way the box will work the same wherever the prefab is placed and we do not need to use Apply Root Motion to keep it in place)</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-28.png" alt="" class="wp-image-160"/><figcaption class="wp-element-caption">Not much to look at yet&#8230;</figcaption></figure></div>


<p>I generated two animation clips, a quick upward bump animation (at a sample rate of 12 frames per second) and a single frame idle animation that will be our default state.  I generated a Trigger parameter that we can fire off when the player touches our Box Collider 2D.  This will move us to the Bounce animation.  Rather than set an exit condition to this clip, we select &#8220;Has Exit Time&#8221;.  This way, once the clip has run, the exit transition will automatically be processed and we will return to the idle state.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-29-1024x510.png" alt="" class="wp-image-161"/></figure></div>


<p>We wrote a quick script (BoxScript.cs) to attach to the box object that uses <strong>OnCollisionEnter2D( )</strong> to detect a collision with the player, and the <strong>Animator</strong> component to set the trigger.  First step completed!</p>



<p>Next, we created a jumping coin.  We built this with our spinning coin sequence, running at a higher sample rate of 24fps, and animated the position so it made a quick jump up and back down.  We made this a prefab object as well, so that we could call it into existence whenever a box is bounced.</p>



<p>Now it is time to connect the two objects.  In our box script, we added a public function <strong>LaunchCoin( )</strong> that would instantiate our coin prefab at the box position when called (and then destroy it momentarily afterwards because it&#8217;s always good to clean up after ourselves).  Because this BoxScript class is attached to the same object as our Animation, and because the function was made public, it is now available to us as an animation event.   </p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-30-1024x555.png" alt="" class="wp-image-162"/></figure></div>


<p>At the height of our boxes bounce clip, I added an Animation Event to the timeline.  You can see it up there just under the number 3 &#8211; it&#8217;s the little white marker.   To create an event, you hit the &#8220;Add Event&#8221; button which is located just next to the &#8220;Add Keyframe&#8221; marker in the Animation window.  (Just under the Sample Rate field).</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" loading="lazy" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-31.png" alt="" class="wp-image-163" width="340" height="144"/><figcaption class="wp-element-caption">This little guy right here&#8230;</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" loading="lazy" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-34.png" alt="" class="wp-image-166" width="200" height="180"/><figcaption class="wp-element-caption">The &#8220;Add Event&#8221; button</figcaption></figure></div>


<p>The Event itself can be configured in the Inspector.  Select the event marker in the timeline and then use the dropdown in the Inspector to associate it with a public available function attached to that object.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-35.png" alt="" class="wp-image-167"/></figure></div>


<p>Here our animation event takes no arguments, but you can configure one parameter to be passed when the event is called.  This can be either a float, int, string, or object reference.   Now we have a coin that pops out of our box.  Our coin sticks around a little longer than we would like, so we disable looping on the Animation Clip, and also add the Sprite Renderer&#8217;s &#8220;enabled&#8221; property to the clip so that we can turn off the visual once it has landed.</p>



<p>Finally we created a separate CoinScript that registers the scoring event and commands an attached AudioSource to play a coin sound.  Using Animation Events again, we can fine tune the timing of that sound in relation to our animation.   Timing sounds to events is one of the more common use case that I have found for these type of event calls, as it allows you to synchronize a sound with a particular frame or feature of animation, such as making a &#8220;footfall&#8221; sound in a walking or running animation.</p>



<h2 class="wp-block-heading">Part 2: 2D Effectors</h2>



<p>Sometimes our standard platforms don&#8217;t quite do what we want them to.  Or there are effects we would like to create such as wind, or a conveyor belt, or floating on water.  Thankfully, Unity includes a number of &#8220;effector&#8221; components that modify a standard 2D collider and turn it into a specialty object or zone.</p>



<p>The important thing to keep in mind is that in order for an effector to work, it must be a component on the same object as a 2D collider, and that collider must have the <strong>Used by Effector</strong> checkbox ticked.  Some effectors (like the Platform Effector and Surface Effector) require that the collider be a solid object.  Others like the Area Effector and Buoyancy Effector require that the collider be set up as a Trigger.  I have a short description of each of these types below, but for more information on how to use them I recommend watching the recording of today&#8217;s class.</p>



<h3 class="wp-block-heading">Platform Effector</h3>



<p>The <strong>Platform Effector</strong> allow us to create platforms that can be accessed from one direction but solid from another.  The most common use of this is a platform that you can jump to from directly underneath, such as in the image below.   While these platforms were created in a <strong>Tilemap</strong>, using the <strong>TilemapCollider</strong> would result in an impervious surface that my player object cannot jump through. </p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image.png" alt="" class="wp-image-168"/></figure></div>


<p>Instead, I have created a series of empty objects with <strong>Box Colliders</strong> that fit over these platform tiles, and then added the <strong>Platform Effector </strong>component to each of these objects.  I have set the colliders to <strong>Used by Effector</strong>, and now I have a one-way platform that  will let me pass through as long as I am moving in an upward direction.   This is the default setting of 180 degrees, and is represented by the arc  in the scene window.  This arc and the rotation offset of that arc can be set in the component.  You can also mask the physics layer of this effect.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-1.png" alt="" class="wp-image-169"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-2.png" alt="" class="wp-image-170"/></figure></div>


<h3 class="wp-block-heading">Area Effector</h3>



<p>The <strong>Area Effector</strong> take a Collider that has the <strong>Is Trigger</strong> property checked, and converts it into a force applying zone of influence.   This creates a force applied in a single direction, and you can alter the magnitude of the force, and introduce variations to that magnitude that will apply some randomness into the effect.  The <strong>Force Target</strong> determines whether the force is applied directly to the Rigidbody, or to the Collider itself.  If it is the collider, this can generate torque (rotation) if not coinciding the the center of mass.  Applying directly to the Rigidbody is the same as applying to the center of mass and so no torque will be generated.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-3.png" alt="" class="wp-image-171"/><figcaption class="wp-element-caption">My favorite use of Area Effectors is to create &#8220;updrafts&#8221;</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-4.png" alt="" class="wp-image-172"/></figure></div>


<h3 class="wp-block-heading">Buoyancy Effector</h3>



<p>The <strong>Buoyancy Effector</strong> is useful for creating water-like settings, where you have a character or objects that have to swim or float within a substance.  Here, there is a defined <strong>Surface Level</strong> and a <strong>Density</strong> that define how objects should float.  The more mass an object has across it&#8217;s collider, the less buoyant it is considered to be, and will sink further into the area.  You can also define a <strong>Flow</strong> force to be applied as well, if you want your body of water to have a current.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-5.png" alt="" class="wp-image-173"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-6.png" alt="" class="wp-image-174"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-7.png" alt="" class="wp-image-175"/></figure></div>


<h3 class="wp-block-heading">Surface Effector</h3>



<p>Finally, the <strong>Surface Effector </strong>creates a conveyor like system, where a tangent force is applied along the surface of the collider, at a set speed.  The <strong>Force Scale</strong> is used to adjust how the effector attempts to get the colliding object to arrive at the set speed.  A value of 1 will override all other movement such as walking, so a lesser value is recommended.  The <strong> Use Contact Force</strong> option is similar to the Area Effector&#8217;s Force Target.  If checked, the force is applied at the contact point of the collider, if unchecked the force is applied to the collider as a whole.   (Point of Contact can cause rotational forces to be applied, which made the box in the bottom image spin)</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-8.png" alt="" class="wp-image-176"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-9.png" alt="" class="wp-image-177"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/07/image-10.png" alt="" class="wp-image-178"/><figcaption class="wp-element-caption">Our character does not spin, as rotation been constrained in the Rigidbody component.</figcaption></figure></div>


<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 23: UI Reference</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=112</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Thu, 15 Jun 2023 16:19:35 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=112</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=112"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>In this section we look deeper into Unity&#8217;s UI system, including the various configurations for the Canvas, creating and navigating our own Buttons.</p>



<span id="more-112"></span>



<h2 class="wp-block-heading" id="canvas">UI: Canvas</h2>



<p>When you create your first UI element, Unity will automatically create a Canvas object inside of your scene, along with the “EventSystem” object that you can ignore for now.</p>



<p>The&nbsp;<strong>Canvas&nbsp;</strong>is the two-dimensional object that holds your user interface objects. Text, buttons, images, sliders, dropdowns… all of these UI type objects need to be contained inside of a Canvas object. All objects on the canvas are placed using the&nbsp;<strong>Rect Transform</strong>, a method of rectangular transformation for 2D objects. A Rect Transform defines a rectangle of pixel dimensions W (width) and H (height), and the offsets X and Y measuring the distance the pivot of the object should be placed from the “anchor”. The anchor is a method of defining how it should be offset from it’s parent object, and can be placed at a normalized value horizontally or vertically. The Anchor presets give you options for placement from the Top, Middle, or Bottom vertically, or Left, Center, or Right horizontally. These presets work similarly to text alignment in a word document or spreadsheet cell.</p>



<p>There are two settings inside the Canvas object which are particularly important for setting up your UI. The first is the&nbsp;<strong>Render Mode</strong>&nbsp;inside the Canvas component. There are three modes available:</p>



<ul>
<li><strong>“Screen Space – Overlay”</strong>&nbsp;is the default setting and will be your most common use case. This setting causes the canvas objects to be drawn over top of your content on the screen. The canvas will cover the entire area of your screen, and resize if your window resizes. This is similar to the concept of “compositing” video by adding graphics overtop of a video feed. The two do not interact visually at all.</li>



<li><strong>“ScreenSpace – Camera”&nbsp;</strong>works similar to the Overlay method, however it sets the screen out at a distance from the camera, and the camera’s perspective will apply, as opposed to the overlay’s planar projection style.</li>



<li><strong>“World Space”</strong>&nbsp;creates a canvas object in the scene, as opposed to an overlay. This object is a rectangle but has a transform (and thus a position and orientation) within the environment itself. This is what is commonly referred to as a “diagetic” interface, in that it exists within the world itself, rather than just from the perspective of the player/observer.</li>
</ul>



<p>The Unity Manual has&nbsp;<a href="https://docs.unity3d.com/Manual/UICanvas.html">more descriptions and examples</a>&nbsp;of these modes.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/10/image-16.png" alt="" class="wp-image-117"/></figure></div>


<p>The&nbsp;<strong>Canvas Scaler</strong>&nbsp;controls the way in which objects will respond to different window sizes. You may have noticed that often that the size and placement of UI objects in your game window do not match their size and placement when you run the game at full screen. This is because the&nbsp;<strong>Canvas Scaler</strong>&nbsp;is set by default to&nbsp;<strong>Constant Pixel Size.&nbsp;</strong>In this mode, objects retain their width and height values, unaffected by the larger screen. I recommend using the second option,&nbsp;<strong>Scale With Screen</strong>&nbsp;which will scale the size and positioning of UI objects up and down with the game screen size, thus preserving your layout as it appears in your editor.</p>



<h2 class="wp-block-heading" id="panels">UI: Panels</h2>



<p>If you want to group your buttons into a more formal menu, you can use the&nbsp;<strong>Panel</strong>&nbsp;object. This simply creates a UI Image object, but one that has a background that mimics the button background, only more transparent. By default, the Panel will fill the screen, but by changing the Rect Transform you can resize it and make objects children of this object.</p>



<p>Panels are an easy way to collect buttons and other UI elements into one place, and can be activated and deactivated. This can be useful for making menus that slide up or over, or populating a tabbed menu. Panels are also useful for gathering UI elements into one location, and for providing contrast for the UI elements against the game view itself.</p>



<p>Unity&#8217;s default panel uses the rounded rectangle UI Sprite to soften it&#8217;s edges.  If you would prefer to use a solid color, just set the <strong>Source Image</strong> setting to &#8220;None&#8221;.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/10/image-17.png" alt="" class="wp-image-118"/></figure></div>


<h2 class="wp-block-heading" id="buttons">UI: Buttons</h2>



<h4 class="wp-block-heading">Creating Buttons</h4>



<p>To create a button, select&nbsp;<strong>Create &gt; UI &gt; Button</strong>. This will generate a simple grey button with black text on your screen, and place it in your&nbsp;<strong>Canvas</strong>. (If you have not created a Canvas yet, Unity will generate one for you.)</p>



<p>You can use the&nbsp;<strong>Rect Transform</strong>&nbsp;to position the object. Clicking the Anchor diagram will call up the Anchor Presets option. Note the modifiers listed at the top for the Shift / Alt keys. Pressing “Shift-Alt” and clicking the center object will move your button to the middle of the screen.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2019/wp-content/uploads/2019/11/image-2.png" alt="" class="wp-image-218"/></figure></div>


<p>The text of your button is actually a separate text object – to change the text, find the button object in your hierarchy, and expand it to see the child objects (in this case a “Text” object) and edit the value in the Text field.</p>



<h4 class="wp-block-heading" id="buttonaction">Button Actions</h4>



<p>To assign a function to a button, you must first have an object in your scene that has a script with a&nbsp;<strong>public</strong>&nbsp;function. In this case, we are accessing the “UIScript” class that has been attached to the “UIManager” object and lives in the scene.</p>



<p>With the button selected, look at the “Button” component and find the&nbsp;<strong>On Click ( )&nbsp;</strong>panel a the bottom.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2019/wp-content/uploads/2019/11/image-4.png" alt="" class="wp-image-220"/></figure></div>


<p>Click the&nbsp;<strong>“+”</strong>&nbsp;symbol to add a new On Click action. You’ll see an empty Object field.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2019/wp-content/uploads/2019/11/image-5.png" alt="" class="wp-image-221"/></figure></div>


<p>Drag the object that with the script or component that contains the method you want to trigger with this button into the Object field.</p>



<p>Next, click the dropdown (currently showing “No Function”) and select the script or component, and then the function you want to run.  Now the button press will call that function when clicked.  Button Actions can also have up to one parameter.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2020/06/image-43.png" alt="" class="wp-image-181"/></figure></div>


<h4 class="wp-block-heading" id="buttonsound">Button Sounds</h4>



<p>We can use the same On Click ( ) panel to emit a sound when a button is pressed. We created an empty object and added an audio clip to it, which generated an <strong>AudioSource</strong> component.</p>



<p>In the <strong>OnClick( ) </strong> section of our button we add another action, this time adding our sound object, then setting the function dropdown to <strong>AudioSource &gt; Play( )</strong></p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2020/06/image-44.png" alt="" class="wp-image-182"/></figure></div>


<p>AudioSource is exposed because it is a component of the object, and the Play( ) command is available publicly.   Another handy tool is the <strong>AudioSource &gt; PlayOneShot( )</strong> method, which lets us play a single pass of an audioClip.   Selecting this opens another field of the AudioClip type, and we can drag that directly from our Assets folder into the field, and now it will make that noise when clicked. </p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2020/06/image-45.png" alt="" class="wp-image-183"/></figure></div>


<h4 class="wp-block-heading" id="buttoncustom">Button Sprites (Color &amp; Image)</h4>



<p>One way to change the visual appearance of your game is to edit the color of the buttons to move away from the default light gray button that Unity provides. In the Button’s&nbsp;<strong>Image</strong>&nbsp;component, you can set the&nbsp;<strong>Color</strong>&nbsp;value to customize the appearance. </p>



<p>The <strong>Button</strong> component gives you access to the states of the button so that you can provide feedback for selections, presses, and active vs. inactive states.   The default Button object uses <strong>Color Tint</strong> as the method for changing these states, which provides a subtle adjustment to the overall brightness of the button image (but not the text object)</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/10/image-18.png" alt="" class="wp-image-119"/></figure></div>


<p>Another way to customize this is to create your own custom button states using <strong>sprites</strong>.  If you ever created your own buttons to build a website, this process will feel familiar &#8211; you generate similar images for each and then swap their placement when in the various states.  We will look more at this in the next project.</p>



<h4 class="wp-block-heading">Button Navigation</h4>



<p>You may have noticed that when we have a button selected, we can use the arrow keys to cycle through the selected option.   This is thanks to the <strong>Navigation</strong> system.  By default, Navigation is set to &#8220;Automatic&#8221;, which means that Unity will try to figure out the logic for itself.   Sometimes this logic does not make a great deal of sense, so if you want to see what is going on, click the Visualize button and you will see a flow diagram appear between your UI elements.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2020/06/image-49.png" alt="" class="wp-image-187"/></figure></div>


<p>The yellow arrows indicate where the selected state will transition when a horizontal or vertical button is pressed.  If you want to set these yourself rather than rely on Unity&#8217;s automatic setting, open the dropdown and deactivate the items so that only &#8220;Explicit&#8221; is selected, then you can define the object by dragging it into each directional slot.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 22: Scene Management</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=110</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Wed, 14 Jun 2023 18:38:25 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=110</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=110"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Today we took a look at the Scene Manager, which allows us to make the jump out of our scene and into new ones.  This introduces some challenges when we want to retain information or objects between these scenes, and so we look at how we can use the singleton pattern to help us manage this process.</p>



<span id="more-110"></span>



<h3 class="wp-block-heading">Part 1 &#8211; Scenes</h3>



<p>In the early days of gaming, developers would divide a game up into small chunks called&nbsp;<strong>“levels”</strong>.&nbsp; &nbsp;While they were useful for players to measure their progress, their real purpose was to minimize the amount of data a game had to hold in memory in order to be playable.&nbsp; &nbsp;Even today, as games allow us to explore cities, countries, worlds, galaxies… the “level” is commonly used as a way to divide up content.&nbsp; It would be silly to hold the final boss battle of a game in active memory when a player is hours away from encountering it.&nbsp; &nbsp;Each draw call would have to consider every polygon in the entire game, every object and behavior would have to be set just so.&nbsp; In short, it would be chaos.&nbsp; Computationally expensive chaos.&nbsp; Why spend all of those cycles considering things that are hours away from use?</p>



<p>So instead we break up our game into smaller chunks and these load into memory when they are needed, hence the “loading” screen that so many games have.&nbsp; (You may be thinking “but what about sandbox games?”&nbsp; Well, those are divided into smaller areas as well – and not just areas, even objects inside those areas have various levels of detail that can be called up.&nbsp; The loading of that content happens in the background, and will swap places with a lower quality model when ready, usually fading between the two so as to disguise the effect and not distract the eye.&nbsp; Most often this happens in the distance, and the game is using predictive algorithms to determine which content you are most likely to need yet and start to load it.)</p>



<p>When Unity was first created, they&nbsp;also included a level system, which became known as&nbsp;<strong>Scenes.&nbsp;&nbsp;</strong>You will recognize this today as the format that we save our files in.&nbsp; Scenes have evolved to be much much more than just game levels, and are frequently seen used for other cases such as UI screens/menus, code and asset loading, and animated cutscenes.&nbsp; Sometimes entire games will be saved as scenes.&nbsp; &nbsp;Projects (the things we open when we launch Unity, and where our Assets and such are stored) can contain multiple scenes, and when we build our game we will designate which of the scenes we create will be included in the build.&nbsp; (This is particularly useful when you’re working on branches or variations of your game – you can simply swap one out for another if you need to!)</p>



<p>For our in-class demo, we changed the name of our main game scene from the Unity default “SampleScene” to &#8220;Level01&#8221;.  We also created a new scene (&#8220;StartMenu&#8221;) to house our UI, and a quick small game scene we named &#8220;Tutorial&#8221;.   </p>



<p class="has-very-light-gray-background-color has-background has-small-font-size"><strong>NOTE </strong>– Don’t worry too much today about the buttons, we will take a deeper dive into the UI system in the next class.</p>



<h3 class="wp-block-heading">Part 2 &#8211; Setting up Buttons</h3>



<p>Unity’s UI buttons are easy to generate by going to&nbsp;<strong>Create &gt; UI &gt; Button</strong>&nbsp;or in the top menu&nbsp;<strong>GameObject &gt; UI &gt; Button</strong>.&nbsp; This will create a new button object as a child of the Canvas object (which will also be created if you don’t already have one).&nbsp; Buttons consist of two parts, the&nbsp;<strong>Button</strong>&nbsp;object, and a child&nbsp;<strong>Text</strong>&nbsp;object.&nbsp; &nbsp;The Button object has two Components to note – the “Image (Script)” component where you define the look of your button, and the “Button (Script)” component where you can define the behaviors and transitions of the buttons.&nbsp; The default button transition is a color tint, but If you have pre-made images for your button states, you can use the “Sprite Swap” transition to show them.&nbsp; The child text object is optional.</p>



<p>Now for the complicated part – adding an action to your button.&nbsp; Buttons have an “OnClick()” message that they send when a user clicks them but accessing them is not as simple as accessing collider messages.&nbsp; At the bottom of the “Button (Script)” component you will see an “OnClick()” panel that is empty.&nbsp; If you click the “+” button it will create a new action.&nbsp; This action requires an object to be associated with it in order to access its scripted functions.&nbsp; &nbsp;You can add a script to the Button object, but then you will have to self-associate the button with itself.&nbsp; &nbsp;<strong>NOTE: The object HAS to be an object currently in the hierarchy.&nbsp; Linking to a prefab does not work.</strong></p>



<p>In class, I created an empty game object (&#8220;LevelManager&#8221;) and added a script (also called &#8220;LevelManager”) which contained a single function that we would use to move to the next scene (&#8220;btn_StartTheGame&#8221;).  In the <strong>OnClick( ) </strong>menu I associate the script object and select a function from the dropdown on the right by going to the specific component “LevelManager” and selecting the function I want to use.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="420" height="80" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-4.png" alt="" class="wp-image-223" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-4.png 420w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-4-300x57.png 300w" sizes="(max-width: 420px) 100vw, 420px" /></figure></div>


<p class="has-very-light-gray-background-color has-background has-small-font-size"><strong>NOTE:</strong>&nbsp; Any function that will be accessed by a Button must be PUBLIC, and VOID.&nbsp; It can only take up to one (1) parameter, that parameter can only be a Float, Int, String, or Object.</p>



<h3 class="wp-block-heading">Part 3 &#8211; Scene Manager</h3>



<p>Now that we have a button in place, let’s connect some scenes.  I’m going to make our LevelManager script handle our first scene jump.</p>



<p>First, I have to add a new library to the code, so I start the file by adding this line:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using UnityEngine.SceneManagement;</pre>



<p>Now we can access the scene management commands.&nbsp; &nbsp;I can create a button command, so looking at our button example above to make us jump to the Level01 scene, I create the following in button script.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void btn_StartTheGame()
{
    SceneManager.LoadScene("TutorialLevel");
}</pre>



<p>Again, please note that this must public and void for the editor to see it.&nbsp; Here, ours takes no arguments.&nbsp; &nbsp; And the command itself is rather simple – we run the&nbsp;<strong>LoadScene( )</strong>&nbsp;function and pass in the string name of our level.&nbsp; &nbsp;We can also pass in an integer that corresponds with the scene index.&nbsp; Speaking of…</p>



<h3 class="wp-block-heading">Part 4 &#8211; Adding Scenes to the Build</h3>



<p>In order to load scenes, Unity has to have those scenes associated with the build.&nbsp; You can do this by going to&nbsp;<strong>File &gt; Build Settings</strong>, and dragging the scenes you will be using from the Asset panel into the scene list in Build Settings.&nbsp; Notice that when you add a scene, it also assigns it an indexed location.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="625" height="155" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-5.png" alt="" class="wp-image-224" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-5.png 625w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-5-300x74.png 300w" sizes="(max-width: 625px) 100vw, 625px" /></figure></div>


<p>Once you have associated the scenes, you can close the window.&nbsp; (There is no save button to worry about, your changes are automatically registered.)</p>



<h3 class="wp-block-heading">Part 5 – Staying Alive (DontDestroyMeBro!)</h3>



<p>If we are making a game, chances are we want to do things like keep track of the score, or the number of lives remaining.&nbsp; A GameManager script seems like the best candidate for this. We created an empty object, assigned it a GameManager script which sets up a singleton, and converted it into a prefab.   We made sure to include this prefab in both of our platform game scenes, as we will need it to handle our logic for events like player deaths, inventory, and of course, the score.</p>



<p>We edited our Level Manager to also be a singleton (so that we can automatically access it within a scene) and added a public string to hold the name of the next scene we should visit upon successful completion of the level.</p>



<p>If our player successfully reaches the end of a level, we reward them by sending them to the next one. To support this, we set a trigger at the end of the level, and tagged it with a &#8220;EndOfLevel&#8221; tag.  If the Player object enters the trigger with that tag, it calls the <strong>NextLevel( )</strong> command from the <strong>LevelManager </strong>and that sends the player to the appropriate scene.  </p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "EndOfLevel")
    {
        LevelManager.S.NextLevel();
    } 
}</pre>



<p>In our GameManager script, we define our scene jump like so:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void NextLevel()
{
    SceneManager.LoadScene(nextScene);
}</pre>



<p>But now we have a problem.  GameManager only exists in the scene that I created it in.&nbsp; As soon as I jump to a new scene, it gets destroyed just like everything else in its host scene and THAT scene’s GameManager instance appears.&nbsp;  I spent all that time collecting those coins, and now they&#8217;re gone!</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2021/wp-content/uploads/2021/06/image-9.png" alt="" class="wp-image-126"/><figcaption class="wp-element-caption">I earned those points!</figcaption></figure></div>


<p>If I want to hold onto things like my score, remaining lives, and inventory, I need to make GameManager persist across the transition from one scene to the next.</p>



<p>To accomplish this, we spare GameManager from an untimely deletion by adding the following line to the&nbsp;<strong>Start ( )</strong>&nbsp;function in the GameManager script.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">DontDestroyOnLoad (this);
</pre>



<p><strong>DontDestroyOnLoad&nbsp;( )</strong>&nbsp;preserves a game object across transitions.&nbsp; It actually creates a little side scene in the Hierarchy and moves the object into it during runtime, as seen below:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2021/06/image.png" alt="" class="wp-image-226"/><figcaption class="wp-element-caption">Before: Our GameManager is just another object in the scene</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2021/06/image-1.png" alt="" class="wp-image-227"/><figcaption class="wp-element-caption">After: Our GameManager object has been moved into a side scene</figcaption></figure></div>


<p>Now when we jump scenes, this object remains, and our score persists as we play through.</p>



<p>But… (you knew there would be one, didn’t you?)</p>



<p>For our game to run correctly, we need to have GameManager running in every scene. This means that either we would ALWAYS have to start our game from the very beginning in the scene where we first instantiate it and play all the way through – not fun, especially when we just want to test one tiny thing in level 5 – OR we have to find a way to include it in EVERY scene. This is why we chose to make our Game Manager a prefab, and drop an instance of it into every level.</p>



<p>But… if we are not destroying our Game Manager and we load a scene with another instance of Game Manager, won’t there be two Game Managers?</p>



<p>Yes there will, and that is a problem.&nbsp; Because now not only do we have two game managers, the new one has just declared itself the singleton (S = this) and overwritten the old one, meaning the new object’s score is the one we display and that’s not what we want at all, is it?&nbsp; &nbsp;And if we move scenes again, since both of these now live within the DontDestroyOnLoad section, we will be adding a third Game Manager and overwritting our Singleton yet again and this seems bad.</p>



<p>So to avoid this situation, we create a small workaround.&nbsp; We build in a check in our Game Manager start script that checks to see if any instance of the singleton exists.&nbsp; If none exists, great – we’re first to the party and we set up shop.&nbsp; But if there is already a singleton defined, we kill this instance of the manager as it is not needed.&nbsp; The script for that looks like this:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private void Awake()
{
    if (S)
    {
        Destroy(this.gameObject);  
        // Singleton already exists, remove this instance
    } else { 
        S = this;  // Singleton Definition
    }
}</pre>



<p>Here we check for the existence of another version of GameManager.&nbsp; If none exists, GameManager.S will return “null”.&nbsp; If not null, someone is already here.&nbsp; We destroy this new version, and we submit a “return” command, which exits our script rather than continuing on.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2021/06/image-2.png" alt="" class="wp-image-228"/><figcaption class="wp-element-caption">Way After: We load the next level but our GameManager continues to exist in this pocket scene.  Because a Singleton was already in place, the Level01 GameManager politely removed itself.</figcaption></figure></div>


<p>Once we’ve passed our null-check, the script is still executing so we know we must be the one true game manager. We assign Singleton status and then in the&nbsp;<strong>Start( )&nbsp;</strong>method we declare our intention to live forever.</p>



<h3 class="wp-block-heading">Part 6 &#8211; I&#8217;m Ready to Go Now</h3>



<p>What about when we DO want something to disappear?  In the case of our game flow, how do we reset a game if we reach a win or lose state?  How to do we go from zero lives remaining back to three, to full health, to an empty inventory?    We could write an elaborate script to reset our conditions internally, but the easier method would be to simply let the GameManager die, return to the Title Screen, and let the next GameManager that comes along start the process all over again.</p>



<p>It turns out that the answer to this is quite simple &#8211; we <strong>Destroy( )</strong> the Game Manager from within itself.  <strong>DontDestroyOnLoad </strong>only preserves objects across transitions &#8211; it does not make them immortal.</p>



<p>In this example, we are returning to the title screen (for whatever reason) and we remove the GameManager object.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "EndOfLevel")
    {
        LevelManager.S.NextLevel();
    } else if (collision.gameObject.tag == "EndOfGame")
    {
        Debug.Log("EndOfGame Reached");
        // tell the level manager to go back to the start menu
        LevelManager.S.GoToStartMenu();
    }
}</pre>



<h3 class="wp-block-heading">Part 7 &#8211; Restarting a Level</h3>



<p>My favorite method for managing death and respawning in a platform game is to simply restart the level by reloading it.  This means that all objects in the scene will be destroyed and re-instantiated.  </p>



<p>Here is a sample script of how you could handle this, by checking for lives remaining.  (NOTE: this is not what we implemented in class today)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void PlayerDied()
{
    livesRemaining--;
    Debug.Log("Lives Remaining = " + livesRemaining);

    if (livesRemaining &lt;= 0)
    {
        LevelManager.S.GoToStartMenu();

    } else
    {
        LevelManager.S.RestartLevel();
    }
}</pre>



<p>Here, if my Player dies but I still have lives left, my GameManager script calls <strong>RestartLevel( )</strong> which simply loads the same scene by passing it the name of the current active scene.  It is a simple and clean solution, if you don&#8217;t need to preserve the state of objects or items within the level.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public void RestartLevel()
{
    SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}</pre>



<p>If you DO need to preserve states or items , you can store this information in your undestroyed game manager and when the level restarts, have the objects check for relevant data.  For instance, if you were building checkpoints into your game you could store a &#8220;checkpoint&#8221; value with a unique ID for where to place the player. </p>



<p>Better yet, you could make a Level Manager which would store the states of the objects in the level itself, but which would destroy itself when your script moved on to the next level.   Then you have a GameManager that lives across the game, and a LevelManager that lives across attempts at a level.</p>



<hr class="wp-block-separator has-css-opacity"/>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">GameManager.cs</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="22" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class GameManager : MonoBehaviour
{
    public int score = 0;
    public static GameManager S;

    public TextMeshProUGUI scoreText;

    private void Awake()
    {
        if (S) { 
            Destroy(this.gameObject); 
        } else { 
            S = this;  // singleton definition
        } 
    }

    private void Start()
    {
        // set the game manager to persist across scenes
        DontDestroyOnLoad(this);

        // scoreText = GetComponent&lt;TextMeshProUGUI>();
        scoreText.text = "Score: " + score;


    }

    public void IncreaseScore(int scoreAmount)
    {
        score += scoreAmount;
        scoreText.text = "Score: " + score;
    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">LevelManager.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class LevelManager : MonoBehaviour
{
    public string nextLevel;
    public static LevelManager S;

    private void Awake()
    {
        if (S)
        {
            Destroy(this.gameObject);
        } else
        {
            S = this;
        }
    }

    public void btn_StartTheGame()
    {
        // start the game by going to the first scene
        SceneManager.LoadScene("Level01");
    }

    public void NextLevel()
    {
        SceneManager.LoadScene(nextLevel);
    }

    public void GoToStartMenu()
    {
        SceneManager.LoadScene(0);
        Destroy(GameManager.S.gameObject);
    }

    public void RestartLevel()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">Player.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public int score = 0;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.transform.tag == "EndOfLevel")
        {
            LevelManager.S.NextLevel();
        }
        
        if (collision.transform.tag == "EndOfGame")
        {
            LevelManager.S.GoToStartMenu();
        }

    }

    public void PlayerHasDied()
    {
        LevelManager.S.RestartLevel();
    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 21: Animation (part 2)</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=220</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Tue, 13 Jun 2023 20:05:43 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=220</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=220"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Today we dive deeper into the Animator Controller, which operates much the same way as our state engine, transitioning us between clips based upon parameters that we will set via scripts.  We also look at how to animate properties beyond the Sprites themselves.</p>



<span id="more-220"></span>



<p>Yesterday, we made made animations by selecting sprites that would play in sequence.  We also used the created moving platforms by adjusting the values of the position over time.  Today, we will combine those methods to create new animations clips using Sprites in the Animation window, and to generate behaviors as we transition between those clips.</p>



<h2 class="wp-block-heading">Part 1: Animating Properties</h2>



<p>Our enemies here are relatively simple,  and they only need to have two states &#8211; walking, and dying.  Our first step is to create our walking animation, which we will do from the sprites included in the sprite sheet.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-35.png" alt="" class="wp-image-177"/></figure></div>


<p>Since our enemy object already exists, and the prefab has been placed into multiple places, it would be easier for us to add the sprite animation to the existing object.  We open the prefab, select the enemy object, and open our <strong>Animation Window</strong>.  You should see a message indicating that there is no Animator or Animation Clip associated yet, and a button to let you create those.  Click that button, and give your walking clip a name (and location, if you&#8217;re trying to keep your file structure neat).</p>



<p>Now this will create an empty Animation Clip.  From here, the sprite animation is easy to create.  We can simply grab the sprites from the Asset Folder and drag them directly into the Dopesheet on the Animation Window.  This will create a sprite animation.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-36.png" alt="" class="wp-image-178"/></figure></div>


<p>If you play this, you will notice that it moves very fast.  That is because Unity by default creates animation clips to run at a <strong>sample rate</strong> of 60, meaning it holds 60 timing frames per second.  Since we are dealing with sprites, turn this setting down to 12, which is the default setting for sprite animation.</p>



<p>Next, create a new clip that will be our &#8220;dying&#8221; clip.  Here, we are going to use the Animation Clip to manage a few properties for us, saving us the trouble of having to make these happen through programming.  Our intended behavior is that when an enemy is hit, it will stop moving, blink rapidly, then disappear.  During this time, it&#8217;s colliders should also be turned off so that the player cannot be affected by accident.</p>



<p>Inside of our empty dying clip, I add new properties, specifically the &#8220;enabled&#8221; property for both colliders.  This will start them with a keyframe at the first and last frame, which I will set to be unchecked.  Next I will add the Sprite Renderer &gt; Enabled property, and set keyframes every few frames to cycle this property on and off quickly to create a blinking movement.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-38.png" alt="" class="wp-image-180"/></figure></div>


<p>Next, I set up the clips in my Animator to move from Walking to Dying only when a particular trigger is called (I&#8217;ve called mine &#8220;Dying&#8221;).</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-39.png" alt="" class="wp-image-181"/></figure></div>


<p>Lastly, I make some modifications in my script by setting the rigidbody to <strong>isKinematic</strong> so that it will no longer move, and setting the velocity to 0, calling the <strong>SetTrigger( )</strong> function from the Animator, and putting a 1 second delay on the <strong>Destroy( ) </strong>call so that the death animation has time to run.</p>



<h2 class="wp-block-heading">Part 2:   Player Animation States</h2>



<p>The principles behind our player animation are very similar to those of our Enemy object, except now we have many more clips to move between.   For this section, I highly recommend watching the video walkthrough, as a writeup will not do the process justice.  However, here is a brief summary of the various animation states we attach to the player object.</p>



<p>We set up parameters to assist with the various conditions that we will want to cause a transition to occur.  These include:</p>



<ul>
<li><strong>speed</strong>: a float value that will determine if our character should walk, run, or stand idle.</li>



<li><strong>isOnGround</strong>: a boolean that we will use to determine if our player is mid-air or grounded.</li>



<li><strong>Death</strong>: a trigger that we will call as events to move us between states</li>
</ul>



<figure class="wp-block-image"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/m2020/wp-content/uploads/2020/06/image-33.png" alt="" class="wp-image-164"/></figure>



<p>Above is our finished controller, with transitions in place. Here is how I have them configured:</p>



<ol>
<li><strong>Entry</strong>: This step is automatic. Anytime the animation controller is enabled, or if another animation passes through the “Exit” state we will reset to here. Immediately moves to the default state, “player_idle” which holds our idle animation.</li>



<li><strong>Idle/Walk:</strong>&nbsp;uses the “speed” float as a condition to move between states. If we are Idle and speed is &gt; 0.1 we will move to “player_walk”. If the current state is Walk and speed is &lt; 0.1 we will move back to “player_idle”.</li>



<li><strong>Walk/Run</strong>: Similar to Idle/Walk, transition occurs when the speed crosses a value of 0.9</li>



<li><strong>Jump:&nbsp;</strong>All three of these transitions will use the “grounded” boolean, which we will populate from the “isGrounded” property of our character controller. A value of “false” will trigger this transition.</li>



<li><strong>Exit from Jump</strong>: Rather than creating multiple return paths and testing each path for speed values inside a range, we end our jump by exiting the animation when “isOnGround” equals “true”. This sends us back to the “Entry” state, which moves us to Idle and then evaluates based on our current speed to arrive at the correct position.</li>



<li><strong>Death</strong>: Because we may die at any time, we build the transition to the “Death” animation from the “Any State”, and set the condition to the “Death” trigger.</li>



<li><strong>Exit from Death:&nbsp;</strong>We did not use this path this year, but in past years I have also set a trigger “PlayerRevive” to act as a singular event that can move us out of the “Death” state. By going to Exit, we re-enter the flow at the “Entry” point and our animation starts again.  However, this only makes sense if we are going to have our player object continue to exist.  Since we are likely going to reset by destroying and instantiating, or by simply reloading a scene, this is extraneous and so we did not use it this year. </li>
</ol>



<p>For all sprite transitions,  I recommend turning off the <strong>Has Exit Time</strong> checkbox and setting the <strong>Transition Duration</strong> to 0 to assure instant transition from one clip to the next.</p>



<p>Animations can be looped by selecting the individual animation in the Asset Window and checking the <strong>Loop Time</strong> box in the Inspector. Our “Death” animation does NOT loop, as we do not want to die over and over again.</p>



<h2 class="wp-block-heading">Part 3: Connecting the Parameters</h2>



<p>Now we need to send values into the parameters.  Previously, we have only used the <strong>SetTrigger( )</strong> command, which is similar to calling a function.   And because this was a one-time event, we simply ran the <strong>GetComponent&lt;Animator></strong> method in-line.  </p>



<p>But now we want to submit regular updates to our Animator, indicating our current speed (for the walk cycle) and our grounded state (for the &#8220;jump&#8221; clip).</p>



<p>We first define an <strong>Animator </strong>type variable, and assign our component in the <strong>Start( )</strong> command, so that we have a steady reference to it. </p>



<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 Animator animator;

...

void Start()
{  
    ...
    animator = GetComponent&lt;Animator>();
}</pre>



<p>Next, we pass in our values &#8211; &#8220;moveDirection&#8221; for the speed and &#8220;isGrounded&#8221; for the grounding status. Previously we used <strong>Input.GetAxisRaw</strong> for movement, but because we want to have our character&#8217;s speed ramp up, we changed our input to <strong>Input.GetAxis</strong>.   That still returns values between -1 and 1, but our speed check is only looking for numbers between 0 and 1, so we find the absolute value using <strong>Mathf.Abs( )</strong>.</p>



<p>We place this check inside of the <strong>Update( )</strong> command rather than <strong>FixedUpdate( )</strong>, because we only need to update the values when the frame is preparing to draw.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    void Update()
    {

        // Movement controls
        moveDirection = Input.GetAxis("Horizontal");


        // set animation controls
        animator.SetFloat("speed", Mathf.Abs(moveDirection));
        animator.SetBool("isOnGround", isGrounded);

        ...
     }</pre>



<p>We use the <strong>SetFloat(string, float) </strong>command to say which float parameter, and the value (here an absolute value).  We pass the grounded status with <strong>SetBool(string, bool)</strong>, and this will make our character enter the jump animation whenever he leaves the ground.</p>



<p>Finaly, we set our Death animation to occur upon a collision with the Enemy Object, using the <strong>SetTrigger()</strong> method.   </p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">EnemyScript.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class EnemyScript : MonoBehaviour
{
    public float speed;
    public bool faceLeft = true;

    public bool _isAlive = true;

    private Rigidbody2D rb;
    private SpriteRenderer sprite;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent&lt;Rigidbody2D>();
        sprite = GetComponent&lt;SpriteRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        /*
        if (faceLeft &amp;&amp; sprite.flipX)
        {
            sprite.flipX = false;
        }
        else if (!faceLeft &amp;&amp; !sprite.flipX)
        {
            sprite.flipX = true;
        }
        */

        if (faceLeft == sprite.flipX) { sprite.flipX = !sprite.flipX; }
    }

    private void FixedUpdate()
    {
        if (_isAlive)
        {
            float direction = faceLeft ? -1 : 1;
            rb.velocity = new Vector2(direction * speed, rb.velocity.y);
        }

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "TurnAround")
        {
            faceLeft = !faceLeft; // invert faceleft
        }

        if (collision.gameObject.tag == "Player")
        {
            EnemyHasDied();
        }

    }

    private void EnemyHasDied()
    {
        // no longer alive
        _isAlive = false;

        // cue the death animation 
        GetComponent&lt;Animator>().SetTrigger("EnemyDied");

        // destroy the instance
        Destroy(this.gameObject, 1.0f);

        // disable rigidbody
        rb.velocity = Vector3.zero;
        rb.isKinematic = true;

    }



}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">SideScrollerController.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]

/*
 *  SideScrollerController - created for UGE Summer 23
 *  Based on Sharp Coder 2D Controller (https://sharpcoderblog.com/blog/2d-platformer-character-controller)
 */

public class SideScrollerController : MonoBehaviour
{
    // Player Movement Variables
    public float maxSpeed = 3.4f;
    public float jumpHeight = 6.5f;
    public float gravityScale = 1.5f;
    float moveDirection = 0;
    public bool isGrounded = true;
    public bool facingRight = true;

    public LayerMask groundLayer;

    public Transform groundCheckObject;
    public float colliderRadius = 0.5f;

    private Animator animator;

    // components
    Rigidbody2D r2d;
    CapsuleCollider2D mainCollider;
    // Transform t;

    // Start is called before the first frame update
    void Start()
    {
        // set the component variables
        r2d = GetComponent&lt;Rigidbody2D>();
        mainCollider = GetComponent&lt;CapsuleCollider2D>();

        // setting some component properties
        r2d.freezeRotation = true;
        r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
        r2d.gravityScale = gravityScale;

        // set the initial facing right value
        facingRight = transform.localScale.x > 0;

        animator = GetComponent&lt;Animator>();
        
    }

    // Update is called once per frame
    void Update()
    {

        // Movement controls
        moveDirection = Input.GetAxis("Horizontal");


        // set animation controls
        animator.SetFloat("speed", Mathf.Abs(moveDirection));
        animator.SetBool("isOnGround", isGrounded);

        // TO-DO:  Implement Change Facing Direction

        if (moveDirection > 0 &amp;&amp; !facingRight)
        {
            facingRight = true;
            transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }
        if (moveDirection &lt; 0 &amp;&amp; facingRight)
        {
            facingRight = false;
            transform.localScale = new Vector3(-Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }




        // Jumping
        if (Input.GetButtonDown("Jump") &amp;&amp; isGrounded){
            r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
        }


    }

    void FixedUpdate()
    {
        // Apply movement velocity
        r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);

        // Check if player is grounded
        Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckObject.position, colliderRadius, groundLayer);

        // check for a grounding collider
        isGrounded = colliders.Length > 0 ? true : false;
        
        
    }

    private void OnDrawGizmos()
    {
        // Draw a yellow sphere at the ground check position
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(groundCheckObject.position, colliderRadius);
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.transform.tag == "Enemy")
        {
            // Death happens here

            // queue the death animation
            animator.SetTrigger("Death");
        }
    }

}
</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 19: Camera Control &#038; Triggers</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=102</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Fri, 09 Jun 2023 19:52:53 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=102</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=102"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Today we continued to develop our platform game by making our camera smoothly follow the player throughout the environment, implementing our ruleset for how the player can move through the environment (forward only, please!) and finally we added an enemy controller and gave it a patrolling &#8220;behavior&#8221; through the use of triggers (a special variation of colliders).</p>



<span id="more-102"></span>



<h2 class="wp-block-heading">Part 1: Camera Control</h2>



<p>Now we have created a player object and let him run through our level, but very quickly the player runs out of view.  There are a number of ways to address this.  The simplest way to let the camera follow our player is to make the camera a <em>child</em> of the player object.  This way, any movement of the player was automatically reflected in the camera.  The end result is that the player remains perfectly still as the rest of the world moves around it.</p>



<p>This solution is OK, but does not really fit what we are going for.  This should be a side scroller, meaning we move our camera to the side, and so we need a better solution.</p>



<p>Our first step is to move the camera horizontally with the player, but not vertically.  We accomplish this by placing a script on the Camera object that would mirror the X value of the player object, like so:</p>



<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 player;
void Update () {
    Vector3 playerposition = player.transform.position;
    Vector3 cameraposition = transform.position;
    cameraposition.x = playerposition.x
    transform.position = cameraposition;
}</pre>



<p class="has-luminous-vivid-amber-background-color has-background"><strong>IMPORTANT NOTE:</strong> Although our game is 2D, our transforms are still very much 3D.  This means when working with position, we always need to use Vector3 rather than Vector2 structures.</p>



<p>We set a public game object and assign the Player object to it in the Inspector.&nbsp; Then on each frame, we find the X position and match it.&nbsp; The result was just OK.  The side motion worked but things feel a little jittery.&nbsp; We want to give our player a little room to move away from the center and have the camera catch up, as though it were controlled by some camera person trying to keep up with the action.&nbsp; To facilitate that, we used <strong>Mathf.SmoothDamp( )</strong>, a dampening function to create a gentle curve to the updating X values, giving the camera an eased, elastic feel.</p>



<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 player;
private float xVelocity = 0.0f;
// Update is called once per frame
void Update () {
    Vector3 playerposition = player.transform.position;
    Vector3 cameraposition = transform.position;
 
    cameraposition.x = Mathf.SmoothDamp (cameraposition.x, playerposition.x, ref xVelocity, 0.5f);
    transform.position = cameraposition;
}</pre>



<p>SmoothDamp ( ) is part of the float math libraries, and takes 4 parameters – the start value, the end value, the current velocity (as a reference), and the time the smoothing should occur.&nbsp; Velocity here is tricky, as SmoothDamp ( ) will modify it each time it runs.&nbsp; In order to let that persist, we pass the velocity variable as a “ref”, which is the closest we will get to pointers in this class.&nbsp; Normally when we call a method we say “here is a value” but in this case by declaring “ref”&nbsp; we say “here is access to the variable itself”.&nbsp; SmoothDamp ( ) will update velocity each time it runs.</p>



<p>Playing this again, this is getting better.&nbsp; My player runs away and the camera catches up again.&nbsp; Since I’ve decided to use “Mario” rules, I want to make sure that the player can only advance, not move backwards.&nbsp; I’ll do that by defining two rules.</p>



<blockquote class="wp-block-quote">
<p><strong>Rule #1:&nbsp;</strong>Any time the player moves right of the midpoint on the screen, the camera will follow.<br><strong>Rule #2:&nbsp;</strong>The player can only move as far left as is currently visible on the screen.</p>
</blockquote>



<p>Rule #1 is easy enough to implement.&nbsp; To do this, we set a condition around the SmoothDamp and transform position update that test to see if playerposition.x is greater than cameraposition.x and if so, it will let the camera follow.</p>



<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 player;
// ref value for smoothDamp
private float xVelocity = 0.0f;
// Update is called once per frame
void Update()
{
    // match the player x position
    Vector3 playerposition = player.transform.position;
    Vector3 cameraposition = transform.position;
    if (playerposition.x > cameraposition.x) { 
        // cameraposition.x = playerposition.x;
        cameraposition.x = Mathf.SmoothDamp(cameraposition.x, playerposition.x, ref xVelocity, 0.5f);
        transform.position = cameraposition;
    }
}</pre>



<p>Rule #2 is a little trickier, as we don’t really know where the left edge is.&nbsp; We could do all kinds of math to figure this out, casting rays and such, but we are going to do this the lazy way.</p>



<p><strong>OPTION 1: (the complicated way)&nbsp;</strong>&nbsp;<em>In a previous semester, we included a value called “<strong>leftSideOffset</strong>” that held the distance in x units that corresponded with the left edge of the screen from the center of the camera.&nbsp; Since the camera is always the same z-distance from the player, this number can be a constant.&nbsp; In our update loop, we then check the offset as a bounding x-value for the player, with the following code:&nbsp;&nbsp;</em></p>



<pre class="wp-block-preformatted"> &nbsp;&nbsp;&nbsp;Vector3&nbsp;checkposition&nbsp;=&nbsp;transform.position;
 &nbsp;&nbsp;&nbsp;float&nbsp;leftx&nbsp;=&nbsp;gameCamera.transform.position.x&nbsp;-&nbsp;leftSideOffset;
 
 &nbsp;&nbsp;&nbsp;if&nbsp;(checkposition.x&nbsp;&lt;&nbsp;leftx)&nbsp;{
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkposition.x&nbsp;=&nbsp;leftx;
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transform.position&nbsp;=&nbsp;checkposition;
 &nbsp;&nbsp;&nbsp;}
</pre>



<p><strong>OPTION 2: (the cheap way)&nbsp;&nbsp;</strong>For this class, I have implemented a much more rudimentary system.  I create an object and assign it a <strong>BoxCollider 2D</strong>.  I size this to span beyond the vertical length of my camera view, and move it to the very left of the camera.  Finally, I make this object a child of the Main Camera, meaning that it will follow along wherever our camera goes.   This prevents our player from being able to run beyond the edge of the screen.   (Once we place enemy objects, you will want to adjust your Collision Matrix in the <strong>Physics 2D</strong> panel in your <strong>Project Settings</strong>, just like we did with our 3D collisions in Astral Attackers, so that your non-player objects can pass through unimpeded.)</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-22.png" alt="" class="wp-image-153"/><figcaption class="wp-element-caption">That box on the left means there&#8217;s only one way you can go.</figcaption></figure></div>


<h3 class="wp-block-heading">Part 6 &#8211; Enemies and Triggers</h3>



<p>Now that we’re moving and jumping, let’s create some enemy objects to interact with.&nbsp; We won’t worry about colliding with them right now, but let’s at least get them in the scene and behaving the way we expect them to behave (patrolling their platforms)!</p>



<p>We created an object similar to our player but with our Enemy sprite.  We added a  Circle Collider 2D, and Rigidbody 2D component to it, and created a simplified version of our character controller script to automate its movement.    We use a simple <strong>boolean</strong> in our script called &#8220;faceLeft&#8221; which will indicate if our enemy is facing left or right, used that value to determine the direction (represented by -1 : 1).  We then used the <strong>SpriteRenderer</strong> component to set the <strong>flipX</strong> value (which flips the horizontal orientation of the sprite itself)</p>



<p>To create behaviors, we will use <strong>Triggers</strong> to give our Enemy the illusion of intelligently patrolling its platform.  Triggers are a variation of colliders that don&#8217;t actually collide &#8211; they simply define a volume, and that volume will create a collison-like event when another object&#8217;s collider enters it.</p>



<p>For our purposes, we are going to create a &#8220;turn around&#8221; box &#8211; a cube volume that our enemy will enter, register, and react by reversing direction.   First, create an empty object, add a Box Collider 2D, and check the <strong>Is Trigger</strong> box.  Create a tag called &#8220;TurnAround&#8221; and assign it to this box.   Make this object a prefab so that we can place them throughout the level.  Then we add this to the Enemy script:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "TurnAround")
    {
        faceLeft = !faceLeft;
    }
    
}</pre>



<p>Once the Enemy object enters the trigger object, it checks for the “TurnAround” tag, and if it finds one, it will invert the “faceLeft” value of the Enemy causing him to walk in the other direction.&nbsp; This will only fire when the enemy first enters the volume as&nbsp;<strong>OnTriggerEnter2D</strong>&nbsp;only occurs at the first point of overlap.&nbsp; You can use&nbsp;<strong>OnTriggerExit2D</strong>&nbsp;to register when the overlap has ended, or&nbsp;<strong>OnTriggerStay2D</strong>&nbsp;to confirm that an overlap is ongoing.</p>



<p class="has-pale-cyan-blue-background-color has-background has-small-font-size"><strong>NOTE: </strong>Trigger&#8217;s use the <strong>OnTrigger</strong> commands, as opposed to the <strong>OnCollision</strong> commands.  Unity treats these differently, so they will only apply to the corresponding setting for <strong>isTrigger</strong>.  Also, because this is 2D physics,  note the <strong>2D</strong> designation at the end of each of these.   <strong>OnCollisionEnter</strong> and <strong>OnCollisionEnter2D</strong> use different physics systems and are not interchangeable, so if your collisions are not registering, check that you are using the proper command.</p>



<h2 class="wp-block-heading">Part 7: Pixel Perfect Camera</h2>



<p>When working with pixel art, you may notice some tearing or flickering lines in your game.  This is often due to the slight artifacts created by the camera sampling the artwork at an incorrect distance.  Thankfully, Unity includes a package to help correct for this &#8211; the <strong>2D Pixel Perfect Camera</strong>.  Rather than run through all of the features here, check out the Unity Manual which does an excellent job breaking down exactly how this works.</p>



<p><a href="https://docs.unity3d.com/Packages/com.unity.2d.pixel-perfect@1.0/manual/index.html">Unity Manual: 2D Picture Perfect</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">CameraFollow.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    public GameObject player;
    private float xVelocity;


    // Update is called once per frame
    void Update()
    {
        if (player) { 

            Vector3 playerPosition = player.transform.position;
            Vector3 cameraPosition = transform.position;

            // match the x-value
            // cameraPosition.x = playerPosition.x;

            // if the player is right of the camera midpoint
            if (playerPosition.x > cameraPosition.x)
            {
                // smoothdamp the x-value
                cameraPosition.x = Mathf.SmoothDamp(cameraPosition.x, playerPosition.x, ref xVelocity, 0.5f);

            }


            // update the camera position
            transform.position = cameraPosition;

        } 
    }
}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">EnemyScript.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class EnemyScript : MonoBehaviour
{
    public float speed;
    public bool faceLeft = true;

    private Rigidbody2D rb;
    private SpriteRenderer sprite;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent&lt;Rigidbody2D>();
        sprite = GetComponent&lt;SpriteRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        /*
        if (faceLeft &amp;&amp; sprite.flipX)
        {
            sprite.flipX = false;
        }
        else if (!faceLeft &amp;&amp; !sprite.flipX)
        {
            sprite.flipX = true;
        }
        */

        if (faceLeft == sprite.flipX) { sprite.flipX = !sprite.flipX; }
    }

    private void FixedUpdate()
    {

    float direction = faceLeft ? -1 : 1;
        rb.velocity = new Vector2(direction * speed, rb.velocity.y);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "TurnAround")
        {
            faceLeft = !faceLeft; // invert faceleft
        }
    }

}
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 18: Sprites &#038; Tilemaps</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=206</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Thu, 08 Jun 2023 18:57:34 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=206</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=206"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Today we completed our Character Controller, and then took a deep dive into Sprites.  We examined how to import and properly configure these images, split Sprite Sheets into multiple sprites, how to convert sprite into Tiles, and finally how to make levels using Tilemaps.</p>



<span id="more-206"></span>



<h2 class="wp-block-heading">Part 1: Finishing the Collider</h2>



<p>At the end of yesterday&#8217;s class, we had set up most of our 2D Character Controller, making modifications from a script that we had found.  One aspect that we did not get to complete was to simplify the means of generating the &#8220;ground&#8221; detection.</p>



<p>Rather than remeasuring the collider bounds and recalculating the sizes in each fixed update, we instead created a child object that we will use as our &#8220;Ground Check&#8221; object.  We add public variables to our script to hold the Transform of that child object (in order to grab the position) and a float value to measure the radius.</p>



<p>Next, we added a <a href="https://docs.unity3d.com/ScriptReference/Gizmos.html"><strong>Gizmo</strong> </a>to draw a sphere representing that ground check object so that we can see our area of effect and create the proper amount of protrusion just below the curvature of our player collider.</p>



<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 OnDrawGizmos()
{
    // Draw a yellow sphere at the ground check position
    Gizmos.color = Color.yellow;
    Gizmos.DrawWireSphere(groundCheckObject.position, colliderRadius);
}</pre>



<p> </p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter size-full is-resized"><img decoding="async" loading="lazy" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-3.png" alt="" class="wp-image-207" width="251" height="282"/><figcaption class="wp-element-caption">Gizmos.DrawWireSphere() gives us this yellow circle in the Editor Scene window.</figcaption></figure></div>


<h2 class="wp-block-heading">Part 2: Sprites</h2>



<p>“Sprite” is a term that has been used in the video game industry almost since it’s inception. In the beginning, it referred to an image of a specific size and type that could be drawn directly to the the screen without having to be processed by the CPU, and was used to improve the performance of a game. These days, the term refers to any bitmap (or image) that is used primarily as a 2D object in a game, rather than a texture or mesh object. 2D games still rely heavily on these for their characters and worlds.</p>



<p class="has-pale-cyan-blue-background-color has-background has-small-font-size">Technically, our Sprite objects in Unity are still mesh objects.  Unity generates a simple flat mesh shape, and applies the bitmap to it as a texture, but instead uses an optimized shader to draw this without considering many of the procedure we would include in 3D rendering.  Even our text objects generate a simplified mesh and apply the font as a texture to it.  For our purposes, however, we will refer to &#8220;mesh&#8221; objects as those belonging in the 3D realm, and our &#8220;sprite&#8221; object meshes simply as Sprites.</p>



<p>A “sprite sheet” refers to an image that contains a collection of images to be used as sprites. Sometimes these are collections of common objects or “tiles” that can be used to create a level, as we will do here, but more often you will encounter them as collections of sequential frames for animation. The reason that these sprite sheets are used is that it is far more costly to load a number of small images into memory and swap them in and out as objects in the scene than it is to have a single large image and instead adjust which coordinates of the image will be drawn in a particular frame. </p>



<p>For this demo, we are going to use three files as sprites:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/viking.png" alt="" class="wp-image-132"/><figcaption class="wp-element-caption">Our hero, a little Viking boy&#8230;</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-4.png" alt="" class="wp-image-134"/><figcaption class="wp-element-caption">&#8230; his enemy, a pixel dragon&#8230;</figcaption></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-7.png" alt="" class="wp-image-137"/><figcaption class="wp-element-caption">&#8230;and the pixelated land they inhabit.</figcaption></figure></div>


<p>In this case, we have:</p>



<ul>
<li>Our hero, a little Viking Boy (a high resolution drawing, and part of a larger sprite sheet comprised of frames of animation.  I&#8217;ll be showing these next week)</li>



<li>An enemy (a very small pixelated dragon, also from a sprite sheet comprised of frames of animation)</li>



<li>Environment Tiles (a 128&#215;128 sprite sheet composed of 16&#215;16 pixelated blocks, each one intended as a &#8220;Tile&#8221; for our Tilemap)</li>
</ul>



<h4 class="wp-block-heading">Sprite Layering</h4>



<p>We start by placing an instance of our Viking Boy into the Scene.   You&#8217;ll notice when we do this, that we have a new Component, the Sprite Renderer.  (This replaces the Mesh Renderer and Mesh components from our 3D games.)  You will also notice that the default tool to move the Sprite is the <strong>Rect Tool</strong>, located to the right of the <strong>Scale</strong> button.  (The hotkey for this is: <strong><span style="text-decoration: underline;">Y</span></strong>) You can still use move/scale/rotate, but the Rect tool is easier for 2D workflows. </p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-11.png" alt="" class="wp-image-141"/></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-12.png" alt="" class="wp-image-142"/></figure></div>


<p>Our <strong>Sprite Renderer</strong> component gives us access to only a few controls that we will use.  The &#8220;flip&#8221; property is useful for swapping a sprites direction without resorting to rotation or scaling tricks.  The &#8220;color&#8221; property lets you set a tint for the sprite, as well as the alpha (transparency) value.  This means that any sprite can be faded.  There are no special shaders like what we had to use with our 3D blocks in the last assignment.</p>



<p>Under <strong>Additional Settings</strong> we find perhaps the most important setting, the <strong>Sorting Layer</strong>.  Unity&#8217;s sprite rendering works a little different from what you have experienced up to this point, in that it often doesn&#8217;t matter how close or far a sprite is from the camera &#8211; the order that sprites are drawn in (and therefore the determination of which sprite will be in front of the other) comes down to a series of comparisons, the most important of which is the Sorting Layer.   </p>



<p>Here, we have our Viking Boy on the &#8220;Default&#8221; layer, but if we access the dropdown and click &#8220;Add Sorting Layer&#8230;&#8221; we go to our Tags &amp; Layers panel and can create new layers.  The order of these layers determines which will draw in front of which.  Each layer on the list will draw in front of the layer before it, so the objects set to the bottom layer (here we created &#8220;Foreground&#8221;) will draw in front of everything else.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-13.png" alt="" class="wp-image-143"/></figure></div>


<p>Also in our <strong>Additional Settings</strong>, you can see the <strong>Order within Layer </strong>setting.  This takes a numerical value, and when two objects in the same layer overlap, the object with the higher number will render in front of the lower numbered object.   (If objects are equal after this, distance to camera is finally considered)</p>



<h2 class="wp-block-heading">Importing and Editing Sprite Sheets</h2>



<p>Sprites and sprite sheets come into our game the same way as the rest of our resources – we import them using&nbsp;<strong>Assets&gt; Import New Assets…</strong></p>



<p>Once imported, you will want to select it so that you can make edits to the&nbsp;<strong>Import Settings</strong>. Remember, these assets are external to your editor. Nothing you do here will&nbsp;<em>change</em>&nbsp;the contents of the sprite sheet, only how the Editor interprets it. Also remember that once you have made changes you will need to confirm the changes by clicking the&nbsp;<strong>Apply</strong>&nbsp;button at the bottom of the panel.</p>



<p>In your Import Settings, chance the<strong>&nbsp;Texture Type</strong>&nbsp;to “Sprite (2D and UI”. This tells Unity a bit about how you intend to use the file.&nbsp; If your sprite is a single image, then the default <strong>Sprite Mode</strong> setting of &#8220;Single&#8221; will work for you.  However, if this is a sprite sheet, change this to  “Multiple”, which lets unity know to expect more than one object will come out of this texture atlas.&nbsp; &nbsp;Your&nbsp;<strong>Pixels Per Unit</strong>&nbsp;setting serves as the scale for how big your sprites should be if you drop them into the scene (of course you can always scale them to meet your preference). By default this value is 100 pixels to a unit. This is great for our Viking Boy as this will make him about 2 units high, but our Environment object is only using 16&#215;16 pixels per sprite. We change this value to “16” so that each block will be 1 unit by 1 unit.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-8.png" alt="" class="wp-image-138"/></figure></div>


<p>Next, we want to correct the “fuzzy” look of our smaller objects.  If we post this to our level, we see the edges are blurry.  This is because Unity is &#8220;sampling&#8221; the image to visualize it at a higher resolution.  This is great for things like photorealistic textures, but terrible for our pixel art.  To correct this, we go to the <strong>Advanced Settings </strong>and set the&nbsp;<strong>Filter Mode</strong>&nbsp;to “Point (no filter)”. This setting controls how to handle scaling images. Using “point” tells the editor that we want the images sharp edges to be preserved.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-9.png" alt="" class="wp-image-139"/><figcaption class="wp-element-caption">Advanced Import Settings.  Don&#8217;t forget to click &#8220;Apply&#8221; when you finish making changes!</figcaption></figure></div>


<p>If you are using pixel art, and are still getting images that don&#8217;t look quite right (i.e. the edges are sharp, but the sprites themselves are blocky or the colors somehow.. wrong)  you might be having issues with the &#8220;compression&#8221; settings.  Unity automatically puts compression on all images that it imports, but for pixel art this can often create a degraded look, and the gains are super minimal because the source images are so small to begin with.  To correct this, look at the Default settings at the bottom of Import Settings and set your <strong>Compression</strong> to &#8220;None&#8221;.  This will remove any adjustment and use your source material as-is.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-10.png" alt="" class="wp-image-140"/><figcaption class="wp-element-caption">and again, make sure you hit &#8220;Apply&#8221;</figcaption></figure></div>


<p>Now it is time to split the sprites, which we will do with the <strong>Sprite Editor</strong>, which you access from the sprite&#8217;s Import Settings. </p>



<p class="has-pale-cyan-blue-background-color has-background has-small-font-size">NOTE:  If you want to use the Sprite Editor with the 3D template, you may need to install the <strong>2D Sprite</strong> package, which you can do from the Package Manager.  If you are starting from a 2D template, this package is already activated.</p>



<p>For our Environment sprite sheet, go to the&nbsp;<strong>Slice</strong>&nbsp;dropdown button at the top and select&nbsp;<strong>Grid by Cell Count</strong>. This will let you define the how the image should be split into pieces. For this object, we want to set the count to 8 columns and 8 rows.  Keep the pivot at &#8220;Center&#8221; (you&#8217;ll want this for proper placement in the Tilemap.)  Once you are ready, hit slice.   You will see that small boxes have been drawn for these, but also that any empty square has been removed.   If you update your sprite sheet with more sprites, you may need to run the Sprite Editor again to include the new content.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-14.png" alt="" class="wp-image-144"/></figure></div>


<p>If you click one of these boxes, you will get a Rect controller, and call up information about the sprite itself.  You can adjust values, or change the name of the individual sprite.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-15.png" alt="" class="wp-image-145"/></figure></div>


<p>When you have completed your set-up &amp; edits, click the &#8220;Apply&#8221; button in the Sprite Editor window, and then also the &#8220;Apply&#8221; button in the Import Settings.  Now in the Asset window your sprite sheet will have a small arrow in a circle next to it. Clicking on this arrow will expand the list to show the individual sprites contained within this object.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-16.png" alt="" class="wp-image-146"/></figure></div>


<h2 class="wp-block-heading">Part 3: Tilemaps</h2>



<p>Now that we have set up our sprites, it is time to build a level. While we could place individual sprites and colliders throughout the environment, Unity gives us an excellent tool for designing sprite content – the&nbsp;<strong>Tilemap</strong>. The Tilemap is a system of components which handles&nbsp;<strong>Tile</strong>&nbsp;assets and allows you to place them in your scene.</p>



<p>To create a new Tilemap, go to&nbsp;<strong>Create &gt; 2D Object &gt; Tilemap</strong>. This will also generate a&nbsp;<strong>Grid</strong>&nbsp;object, and your Tilemap will be a child of this object. (You can create more than one Tilemap, each will become a child of this Grid). This parent object has a Grid component within which you can set the size, spacing, and orientation of the Grid.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-17.png" alt="" class="wp-image-148"/></figure></div>


<p>You will see this grid reflected in the Scene window…</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-18.png" alt="" class="wp-image-149"/></figure></div>


<p>The Tilemap object will contain the&nbsp;<strong>Tilemap Component</strong>&nbsp;and the&nbsp;<strong>Tilemap Renderer</strong>&nbsp;component. The most important setting to notice here is the&nbsp;<strong>Sorting Layer</strong>&nbsp;setting in the Tilemap Renderer. For our game, we will set up multiple Tilemaps under the same grid, and set them to different sorting layers to create foreground and background objects.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-19.png" alt="" class="wp-image-150"/></figure></div>


<p>Once your Tilemap is created, you will want to create a&nbsp;<strong>Tile Palette</strong>&nbsp;– a collection of tiled sprites that you can use to populate the grid cells on your Tilemap. Access this by going to&nbsp;<strong>Window &gt; 2D &gt; Tile Palette</strong>. Once here, select “Create New Palette…” and give it a location and a name. Then you can drag sprites into this window which will create&nbsp;<strong>Tiles</strong>&nbsp;for each sprite. (You will see a window asking where you want to save these in your Asset directory. I recommend creating a new folder, as these can get pretty numerous if you are working with even a modestly sized sprite sheet.)</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-20.png" alt="" class="wp-image-151"/></figure></div>


<p>The Tile Palette window features a list of tools at the top. These are:</p>



<ul>
<li><strong>Select&nbsp;</strong>– allows you to select one or more grid cells</li>



<li><strong>Move</strong>&nbsp;– allows you to relocate a tile (you must first select the tile)</li>



<li><strong>Paint</strong>&nbsp;– select a palette tile or tiles and use this to paint that selection onto your scene’s tilemap.</li>



<li><strong>Box Fill</strong>&nbsp;– fill an area of your tilemap with the selected tile(s) from your palette</li>



<li><strong>Pick</strong>&nbsp;– click on a tile to change your active brush selection to that type of tile</li>



<li><strong>Erase</strong>&nbsp;– click on a tile to remove it</li>



<li><strong>Flood Fill</strong>&nbsp;– fill a large area with the active tile(s)</li>
</ul>



<p>Below the tile tools is the&nbsp;<strong>Active Tilemap</strong>&nbsp;setting. If you have multiple tilemaps (which is common with layering) you will want to make sure that you have the correct Tilemap selected before you paint. For this reason, it is recommended to give each of your Tilemaps a unique name that identifies their purpose (such as Foreground, Background, etc)</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">SideScrollerController.cs</pre>



<p>using System.Collections;<br>using System.Collections.Generic;<br>using UnityEngine;</p>



<p>[RequireComponent(typeof(Rigidbody2D))]<br>[RequireComponent(typeof(CapsuleCollider2D))]</p>



<p>/*</p>



<ul>
<li>SideScrollerController &#8211; created for UGE Summer 23</li>



<li>Based on Sharp Coder 2D Controller (https://sharpcoderblog.com/blog/2d-platformer-character-controller)<br>*/</li>
</ul>



<p>public class SideScrollerController : MonoBehaviour<br>{<br>// Player Movement Variables<br>public float maxSpeed = 3.4f;<br>public float jumpHeight = 6.5f;<br>public float gravityScale = 1.5f;<br>float moveDirection = 0;<br>public bool isGrounded = true;<br>public bool facingRight = true;</p>



<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 LayerMask groundLayer;

public Transform groundCheckObject;
public float colliderRadius = 0.5f;

// components
Rigidbody2D r2d;
CapsuleCollider2D mainCollider;
// Transform t;

// Start is called before the first frame update
void Start()
{
    // set the component variables
    r2d = GetComponent&lt;Rigidbody2D>();
    mainCollider = GetComponent&lt;CapsuleCollider2D>();

    // setting some component properties
    r2d.freezeRotation = true;
    r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
    r2d.gravityScale = gravityScale;

    // set the initial facing right value
    facingRight = transform.localScale.x > 0;
}

// Update is called once per frame
void Update()
{

    // Movement controls
    moveDirection = Input.GetAxisRaw("Horizontal");

    // TO-DO:  Implement Change Facing Direction

    if (moveDirection > 0 &amp;&amp; !facingRight)
    {
        facingRight = true;
        transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
    }
    if (moveDirection &lt; 0 &amp;&amp; facingRight)
    {
        facingRight = false;
        transform.localScale = new Vector3(-Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
    }




    // Jumping
    if (Input.GetButtonDown("Jump") &amp;&amp; isGrounded){
        r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
    }


}

void FixedUpdate()
{
    // Apply movement velocity
    r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);

    // Check if player is grounded
    Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckObject.position, colliderRadius, groundLayer);

    // check for a grounding collider
    isGrounded = colliders.Length > 0 ? true : false;

}

private void OnDrawGizmos()
{
    // Draw a yellow sphere at the ground check position
    Gizmos.color = Color.yellow;
    Gizmos.DrawWireSphere(groundCheckObject.position, colliderRadius);
}</pre>



<p>}</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 17: 2D Character Controller</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=200</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Wed, 07 Jun 2023 19:09:00 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=200</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=200"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Unity has a bunch of great components that make game development much easier, but one thing they are still missing is a 2D version of a Character Controller.  So today we found a 2D controller that uses 2D Physics and modified it to make it our own.  Let&#8217;s dive in! </p>



<span id="more-200"></span>



<h3 class="wp-block-heading">Part 1: 2D Character Controller Setup</h3>



<p>At their core, Character Controllers are rather simple, but incredibly important.  They manage the important task of moving our character through our levels based upon our input, running, jumping, and not falling through the floor.  And by providing situational information such as the answer to &#8220;are we on the ground?&#8221; which is a critical question when figuring out whether or not we can &#8220;jump&#8221;.</p>



<p>While Unity provides a <strong>3D Character Controller </strong>component for their games, they have yet to release their own 2D controller   As such, there are a significant number of controllers that others have created and published.  Today, we looked at a lovely little controller script from the Sharp Coder blog that I prefer &#8211; it&#8217;s simple, straightforward, and uses 2D Physics to handle gravity and collisions.   We incorporated much of their code, with some modifications that simplify our process.</p>



<p>You can find their blog post and code here:  <a href="https://sharpcoderblog.com/blog/2d-platformer-character-controller">Sharp Code &#8211; Unity 2D Character Controller </a></p>



<p>The first neat feature about this script is the RequireComponent instruction at the head of the file.  This tells the Unity Editor that this script requires these components to be attached to the same object (in this case, <strong>Rigidbody2D </strong>and <strong>CapsuleCollider2D</strong>).  If you attach this script to an object without these components, they will automatically be added.</p>



<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="">[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]</pre>



<p>The next feature that I appreciate about this particular controller is that they modify the rigidbody properties during the Start( ) function, to ensure that the right settings are in place.  In this case, they make sure that <strong>Rigidbody2D.freezeRotation</strong> is set to true (so our character doesn&#8217;t roll over) and that the collision detection mode is set to &#8220;Continuous&#8221; rather than the default setting of &#8220;Discrete&#8221;.    Continuous detection is going to be the preferred method for your player object and probably your enemy objects &#8211; you want those collisions running all the time.    Finally, it set the rigidbody&#8217;s <strong>gravityScale</strong> to a value defined by this script.  This is important because it will affect the speed of your vertical movement.</p>



<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="">        r2d.freezeRotation = true;
        r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
        r2d.gravityScale = gravityScale;</pre>



<p>We left out all references to camera control from the script, as we will be making our own custom camera controller later in this lesson.  </p>



<h3 class="wp-block-heading">Part 2: Character Controller Inputs</h3>



<p>The Update( ) method is used to fetch our user Inputs, which currently consist of moving horizontally, and jumping (which is a vertical motion).   We modify the ridigbody&#8217;s velocity, which is expressed as a <strong>Vector2</strong>, in order to move the controller in that direction.</p>



<p>Jumping can only occur when we are &#8220;grounded&#8221; (more on that in a minute) and simply replaces the velocity&#8217;s y-value with whatever we set out &#8220;jumpHeight&#8221; value to be.</p>



<p>Horizontal movement is a little more complicated.  Sharp Coder&#8217;s features a lot of logic to determine whether how to set our &#8220;moveDirection&#8221;.</p>



<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="">        // Movement controls
        if ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) &amp;&amp; (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f))
        {
            moveDirection = Input.GetKey(KeyCode.A) ? -1 : 1;
        }
        else
        {
            if (isGrounded || r2d.velocity.magnitude &lt; 0.01f)
            {
                moveDirection = 0;
            }
        }</pre>



<p>This version looks to see if we are pressing the A or D key, AND if we are either grounded, or already moving horizontally (by testing the absolute of the velocity&#8217;s x-value).  This means that we can move while on the ground, or can adjust our movement in air <em>if we were already moving horizontally in air<strong>.  </strong></em>Whether or not you can control your character&#8217;s horizontal movement while in-air makes for a heated debate amongst designers.  Also, what&#8217;s up with this line?</p>



<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="">moveDirection = Input.GetKey(KeyCode.A) ? -1 : 1;</pre>



<p>This is what is known as a &#8220;ternary operator&#8221;. It&#8217;s a conditional that works like a simplified &#8220;if&#8221; conditional.   It&#8217;s structure is <strong>something = statement ? &lt;true value> : &lt;false value></strong> Here, moveDirection can be set to one of two values, based on &#8220;is the A key is pressed&#8221;.  If true, a value of -1 is returned, if false, 1 is returned.</p>



<p>The else on the original statement says if we are not pressing A or D, then our move direction should be 0.   Doesn&#8217;t this sound like <strong>Input.GetAxisRaw</strong>?  It sure does, so that&#8217;s what we use instead. </p>



<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="">// Movement controls
moveDirection = Input.GetAxisRaw("Horizontal");</pre>



<h3 class="wp-block-heading">Part 3: Actual Making things Move</h3>



<p>We used the Update( ) loop to take our settings, but for actually *moving* our object, we want to take advantage of <strong>FixedUpdate( )</strong>.   FixedUpdate is called at a steady, regular interval (usually 50 times per second) and is the preferred method for working with Rigidbodies / Physics.   Update is frame dependent &#8211; and that framerate will fluctuate, which is why we have to include a &#8220;deltaTime&#8221; measurement when creating motion.  With FixedUpdate( ), the interval is the same every time.</p>



<p>Towards the end of Sharp Coder&#8217;s FixedUpdate( ) function, we see the code that sets the velocity value of our rigid body.</p>



<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="">// Apply movement velocity
r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);</pre>



<p>This is super-simple &#8211; take the horizontal movement direction we have designated, multiply it by the max speed and place it in the x-value.  We preserve the y-value of the velocity, as this will be set elsewhere through a &#8220;jump&#8221;, react to the -y forces of gravity, or return to 0 in the event that we are on a surface. </p>



<h3 class="wp-block-heading">Part 4: Are we on the ground?</h3>



<p>The beginning of the original FixedUpdate( ) function contains a considerable amount of logic to identify a point in space that exists inside of our player collider, and the width of the collider itself.  The reason this information is important is that it will be used to create a custom collision check to see if our collider is indeed touching something.  The logic goes something like this:</p>



<ol>
<li>Find a point just about the radius point of the lower arc on the capsule.</li>



<li>Create a circle that will reach just a little lower than that arc, and check within that circle for any colliders.</li>



<li>Take the list of returned colliders and check to see if one of those is NOT the player collider.</li>



<li>If you find a non-player collider, you&#8217;re on the ground!   If not, isGrounded remains false.</li>
</ol>



<p>This method certainly does work, but I have a few issues with it.  First, it just assumes that ANY collider that it detects is automatically the ground &#8211; but later we will use colliders with the &#8220;trigger&#8221; function that our player object will pass through, but this <strong>Physics2D.OverlapCircleAll</strong> will definitely detect.   We saw before how we could use the layer matrix in Physics settings to &#8220;mask&#8221; layers from one another (like objects in Enemy layers ignoring one another).  Since we only care if we hit colliders that represent the ground, why don&#8217;t we just use layers to check specifically for that? </p>



<p>In order to do this, I can use the third parameter of <strong>Physics2D.OverlapCircleAll( )</strong> &#8211; a integer named <strong>layerMask</strong>.  The layer mask is what is known as a &#8220;bitmask&#8221; &#8211; a way of using and manipulating the bits in a byte of information, otherwise known as a bitwise operation.  These bits are set on or off, and used as a way to store information as a number.  If you look at the Layer panel, you&#8217;ll notice that we only have slots in Layer 0-31, or 32 total possible values that can be set to &#8220;on&#8221; or &#8220;off&#8221;.  We have a data type that is made of 32 bits &#8211; the integer.  And so there is a single numeric value that represents the over 4 million possible combinations of on-off in those 32 slots.   Sound complicated?  It is!  </p>



<p>Thankfully we don&#8217;t have to worry about the math for this, because Unity has already built this into our editor, with the <strong>LayerMask</strong> type.  You&#8217;ve seen these variables before, in the Camera Mask settings, which determine whether a camera can view a layer or not.   We declare a variable to set in the editor like so:</p>



<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 LayerMask groundLayer;</pre>



<p>This gives us a dropdown that we can use to set the layers we are interested in looking at.  I&#8217;ve created a layer named &#8220;Ground&#8221;, and put all of our environment objects on that layer.  </p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="447" height="372" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-2.png" alt="" class="wp-image-203" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-2.png 447w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-2-300x250.png 300w" sizes="(max-width: 447px) 100vw, 447px" /></figure></div>


<p>Now in FixedUpdate( ) we modify that custom physics call to include the layerMask so that it only returns colliders on the layers that we defined in the editor, which in this case is &#8220;Ground&#8221; and &#8220;Enemy&#8221;.</p>



<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="">// Check if player is grounded
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckPos, colliderRadius, groundLayer);
        </pre>



<p>Now rather than sort through all of the colliders, I can simply check to see if there ARE any colliders returned!  I set my grounded variable equal to false, then if there is length, I turn it to true.</p>



<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="">//Check if any of the overlapping colliders are not player collider, if so, set isGrounded to true
isGrounded = false;

if (colliders.Length > 0) { isGrounded = true; }</pre>



<p>Of course, if I wanted to be fancy, I could use our ternary operator to express these in one line.</p>



<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="">isGrounded = colliders.Length > 0 ? true : false;</pre>



<p>Tomorrow we will finish our controller by simplifying the physics check, and generating some helpful gizmos to visualize their range of effect.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">SideScrollerController.cs</pre>



<p>using System.Collections;<br>using System.Collections.Generic;<br>using UnityEngine;</p>



<p>[RequireComponent(typeof(Rigidbody2D))]<br>[RequireComponent(typeof(CapsuleCollider2D))]</p>



<p>/*</p>



<ul>
<li>SideScrollerController &#8211; created for UGE Summer 23</li>



<li>Based on Sharp Coder 2D Controller (https://sharpcoderblog.com/blog/2d-platformer-character-controller)<br>*/</li>
</ul>



<p>public class SideScrollerController : MonoBehaviour<br>{<br>// Player Movement Variables<br>public float maxSpeed = 3.4f;<br>public float jumpHeight = 6.5f;<br>public float gravityScale = 1.5f;<br>float moveDirection = 0;<br>public bool isGrounded = true;<br>public bool facingRight = true;</p>



<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;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]

/*
 *  SideScrollerController - created for UGE Summer 23
 *  Based on Sharp Coder 2D Controller (https://sharpcoderblog.com/blog/2d-platformer-character-controller)
 */

public class SideScrollerController : MonoBehaviour
{
    // Player Movement Variables
    public float maxSpeed = 3.4f;
    public float jumpHeight = 6.5f;
    public float gravityScale = 1.5f;
    float moveDirection = 0;
    public bool isGrounded = true;
    public bool facingRight = true;

    public LayerMask groundLayer;


    // components
    Rigidbody2D r2d;
    CapsuleCollider2D mainCollider;
    // Transform t;

    // Start is called before the first frame update
    void Start()
    {
        // set the component variables
        r2d = GetComponent&lt;Rigidbody2D>();
        mainCollider = GetComponent&lt;CapsuleCollider2D>();

        // setting some component properties
        r2d.freezeRotation = true;
        r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
        r2d.gravityScale = gravityScale;

        // set the initial facing right value
        facingRight = transform.localScale.x > 0;
    }

    // Update is called once per frame
    void Update()
    {

        // Movement controls
        moveDirection = Input.GetAxisRaw("Horizontal");

        // TO-DO:  Implement Change Facing Direction

        if (moveDirection > 0 &amp;&amp; !facingRight)
        {
            facingRight = true;
            transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }
        if (moveDirection &lt; 0 &amp;&amp; facingRight)
        {
            facingRight = false;
            transform.localScale = new Vector3(-Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
        }




        // Jumping
        if (Input.GetButtonDown("Jump") &amp;&amp; isGrounded){
            r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
        }


    }

    void FixedUpdate()
    {
        Bounds colliderBounds = mainCollider.bounds;
        float colliderRadius = mainCollider.size.x * 0.4f * Mathf.Abs(transform.localScale.x);
        Vector3 groundCheckPos = colliderBounds.min + new Vector3(colliderBounds.size.x * 0.5f, colliderRadius * 0.9f, 0);
        
        
        
        // Check if player is grounded
        Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckPos, colliderRadius, groundLayer);
        
        
        
        //Check if any of the overlapping colliders are not player collider, if so, set isGrounded to true
        isGrounded = false;

        if (colliders.Length > 0) { isGrounded = true; }

        // Apply movement velocity
        r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);

        // Simple debug
        Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(0, colliderRadius, 0), isGrounded ? Color.green : Color.red);
        Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(colliderRadius, 0, 0), isGrounded ? Color.green : Color.red);
    }

}
</pre>



<p>}</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Day 16: 2D Physics</title>
		<link>https://courses.ideate.cmu.edu/53-353/m2023/?p=195</link>
		
		<dc:creator><![CDATA[Tom Corbett]]></dc:creator>
		<pubDate>Tue, 06 Jun 2023 20:05:05 +0000</pubDate>
				<category><![CDATA[Class Recap]]></category>
		<guid isPermaLink="false">https://courses.ideate.cmu.edu/53-353/m2023/?p=195</guid>

					<description><![CDATA[<a class="more-link"  href="https://courses.ideate.cmu.edu/53-353/m2023/?p=195"><span class="more-text"></span></a>]]></description>
										<content:encoded><![CDATA[
<p>Today we started into our new Side Scroller project with an overview of the 2D configuration of the Unity Editor, as well as an introduction to sorting layers and 2D Physics, creating a simple &#8220;top-down&#8221; character controller.</p>



<span id="more-195"></span>



<h2 class="wp-block-heading">Part 1: 2D Basics</h2>



<p>An important distinction to make about building a 2D game in Unity is that it is all the same Unity game engine.  There are 2D and 3D capabilities built into the engine, but up to now we have dealt exclusively in the 3D realm.  To make development (and the file-size of a project) efficient, many of the packages that are specific to 2D are turned off in the 3D templates.   The best way for us to activate these packages is to launch a new project, but use the built-in <strong>2D Template </strong>instead.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="1013" height="595" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-5.png" alt="" class="wp-image-94" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-5.png 1013w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-5-300x176.png 300w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-5-768x451.png 768w" sizes="(max-width: 1013px) 100vw, 1013px" /><figcaption class="wp-element-caption">f </figcaption></figure></div>


<p>When we launch this, our editor will look mostly the same, with a few small, but significant differences.  First, you will notice that our Scene window is set to &#8220;2D&#8221; mode, with our camera facing the XY plane.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="1022" height="618" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-6.png" alt="" class="wp-image-95" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-6.png 1022w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-6-300x181.png 300w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2022/06/image-6-768x464.png 768w" sizes="(max-width: 1022px) 100vw, 1022px" /></figure></div>


<p>You will also notice that our scene only include a camera &#8211; by default there is no directional light, because we will be using <strong>sprite rendering</strong> in this project instead.   Also, our <strong>Main Camera</strong> is a little different &#8211; our <strong>Projection</strong> is set to <strong>Orthographic</strong>, and there is no <strong>Skybox</strong>.  (There can be a skybox, but none is included by default, so the <strong>Clear Flags</strong> option falls back to the <strong>Solid Color</strong> designation.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://courses.ideate.cmu.edu/53-353/f2020/wp-content/uploads/2020/11/image-3.png" alt="" class="wp-image-131"/></figure></div>


<p>While the 2D / Orthographic camera <em>can</em> be used to render 3D content &#8211; remember, we did this with our Pong game &#8211; the primary method of drawing objects on the screen for 2D content is to use <strong>Sprites</strong>.</p>



<p>“Sprite” is a term that has been used in the video game industry almost since it’s inception. In the beginning, it referred to an image of a specific size and type that could be drawn directly to the the screen without having to be processed by the CPU, and was used to improve the performance of a game. These days, the term refers to any bitmap (or image) that is used primarily as a 2D object in a game.</p>



<p>For now, we will use Unity&#8217;s built-in sprites which are simple shapes that we can use to explore layers and physics. </p>



<h3 class="wp-block-heading">Part 2: Sorting Layers</h3>



<p>Sprite rendering works differently than 3D rendering &#8211; there are no polygons to receive and reflect light, and so the images used for sprites are simply presented (and rendered) as-is.  The <strong>Sprite Renderer</strong> has different properties than the <strong>Mesh Renderer</strong> that we have previously used, including two very important ones &#8211; the <strong>Sorting Layer</strong> and the <strong>Order in Layer</strong>, and Unity&#8217;s rendering engine uses these values to determine which object to draw on top of another.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="439" height="230" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image.png" alt="" class="wp-image-196" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image.png 439w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-300x157.png 300w" sizes="(max-width: 439px) 100vw, 439px" /></figure></div>


<p>The <strong>Sorting Layer</strong> is given top priority in rendering.  The higher the Sorting Layer&#8217;s index (as defined in the <strong>Tags &amp; Layers</strong> panel that we&#8217;ve used before), the closer to the camera this layer will render, regardless of other attributes like distance (unless the sprite is behind the camera, and thus outside of the view frustrum).    These are especially useful as we set up rules for our tilemaps, so that we can generate proper layers without having to worry about actual z-depths.</p>



<p>The next level of sorting priority when drawing is the <strong>Order in Layer</strong> setting.  Overlapping objects that are contained within the same layer will use this numeric value to determine which should draw on top of the other &#8211; the higher number sprite drawing over top of the lower numbered sprite.  (These numbers can also be negative, and these values can be changed programmatically if need be).</p>



<p>In the event that overlapping sprites do not have different layers or layer orders, then there are a few other comparisons such as distance from camera (as determined by the pivot point).  But if you have overlapping sprites, it&#8217;s best to use these systems.</p>



<h3 class="wp-block-heading">Part 3: 2D Physics &amp; a Top-Down Controller</h3>



<p>Unity&#8217;s 2D physics system relies on a different physics library than the 3D system, and so the sprites that we create will use a separate set of components when operating in the 2D physics world.  There is a <strong>Rigidbody2D</strong> type component that performs the same task as the 3D Rigidbody, but only operates on the XY plane with the only possible rotation happening on the Z axis, towards the camera).</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="758" height="408" src="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-1.png" alt="" class="wp-image-197" srcset="https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-1.png 758w, https://courses.ideate.cmu.edu/53-353/m2023/wp-content/uploads/2023/06/image-1-300x161.png 300w" sizes="(max-width: 758px) 100vw, 758px" /></figure></div>


<p>There are also a set of <strong>2D Colliders</strong> which work with this system, including a <strong>BoxCollider2D</strong>, a <strong>CircleCollider2D</strong> and a <strong>CapsuleCollider2D</strong>.  It is important to note that collision detection also relies upon its own method, <strong>OnCollisionEnter2D(Collision2D collision)</strong>.</p>



<p>As a demo today, we built a simple environment using multiple square sprites (stretched to form rectangles), plus a few sprites with the Rigidbody controller attached.  We used the <strong>Rigidbody2D.MovePosition( ) </strong>function to move our sprite towards a location that we defined as the current position + a directional offset (using the <strong>Input.GetAxis( )</strong> for horizontal and vertical dimensions).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<pre class="wp-block-preformatted">TopDownCharacterController.cs</pre>



<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;
using System.Collections.Generic;
using UnityEngine;

public class TopDownCharacterController : MonoBehaviour
{
    public float speed = 1.0f;

    private Rigidbody2D rb;
    private Vector2 direction;


    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent&lt;Rigidbody2D>(); // get the rigidbody for this object
        direction = new Vector2();
    }

    // Update is called once per frame
    void Update()
    {
        // get the movement inputs
        direction.x = Input.GetAxisRaw("Horizontal");
        direction.y = Input.GetAxisRaw("Vertical");

    }

    private void FixedUpdate()
    {
        if (rb)
        {
            // move the controller
            rb.MovePosition(rb.position + (direction.normalized * speed * Time.fixedDeltaTime));
        } else
        {
            Debug.Log("Missing a Rigidbody 2D component");
        }
    }
}
</pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
