Session 6

Side-scrolling Platformer

For this 2D sidescrolling platformer, we’re going to use Unity’s 2D physics and related functions. These are similar to the 3D physics functions, but everything has “2D” at the end, such as Rigidbody2D vs. Rigidbody, and BoxCollider2D or OnTriggerEnter2D. The 2D and 3D physics in Unity do not interact.

The regular 3D physics in Unity can be used to create a 2D game, as we’ve done earlier in the course, but 2D physics in Unity ignores the Z dimension entirely when calculating collisions, which saves some headaches with dealing with depth.

For this tutorial, we’ll create a new project, setting the desired defaults to “2D”.

Next, we’ll need some ground for testing. Create a Cube object, then make it really wide on the X axis using the Scale tool or the Scale parameters in the Inspector. Use the Gear icon in the upper right to Remove the Box Collider that’s on there. Then go to AddComponent > Physics 2D > BoxCollider2D to add a box collider that will work with the 2D physics.

Creating a platformer character controller

Under Create in the Hierarchy window, click 2D Object and then Sprite. This will create a game object with an empty SpriteRenderer component. For the purposes of coding this demo, we can use any image, either a ship or character from the vertical shooter lessons, or another image you have that’s small.

Our character will need a Rigidbody2D component, found under Add Component > Physics 2D > Rigidbody2D. In this component, go to Constraints, open it and check Freeze Z Rotation. We also need a round collider, which makes it easier for our character to move over any bumps. Add Component > Physics 2D > BoxCollider2D.

Next, we’ll work on the character controller by adding a script file, which we can call Player.cs. First, we need to set up a few variables for our basics: movement speeds and the basic components we need to access over time..

 using UnityEngine;
 using System.Collections;
 
 public class Player: MonoBehaviour {
 
     public float moveSpeed;
 
     private Rigidbody2D rb2d;
     private SpriteRenderer sprite;
 
     // Use this for initialization
     void Start () {
         rb2d = GetComponent<Rigidbody2D> ();
         sprite = GetComponent<SpriteRenderer> ();
     }
     
     // Update is called once per frame
     void FixedUpdate () {
     
     }
 
 }

We’re going to use FixedUpdate, because it works better and more reliably with the physics engine in Unity. Update occurs as fast as the computer can calculate frames. FixedUpdate only checks the state of the game every 60/th of a second, which is more consistently for calculating interactions and motion.

Inside of FixedUpdate, we’ll add the basic left-right movement:

     void FixedUpdate () {
         float move = Input.GetAxis("Horizontal");
 
         rb2d.velocity = new Vector2(move * moveSpeed, rb2d.velocity.y);
     }

Adjusting the raw velocity of an object can mess with the physics and is sometimes frowned upon, but for a side-scrolling platformer video game, we want to be able to stop and start moving quickly, and it works really well.

Now we have a player that can move left and right, kind of like our shooter, but will behave with gravity.

Turn the Sprite Around

If we have a non-symmetrical sprite, it’s easy to notice that the character doesn’t turn around when we switch directions. Here’s how to fix this, by adding a check for the direction of motion and adjusting along the way:

 using UnityEngine;
 using System.Collections;
 
 public class Player: MonoBehaviour {
 
     public float moveSpeed;
     public float jumpSpeed;
 
     private Rigidbody2D rb2d;
     private SpriteRenderer sprite;
 
     private bool facingRight;
 
     // Use this for initialization
     void Start () {
         rb2d = GetComponent<Rigidbody2D> ();
         sprite = GetComponent<SpriteRenderer> ();
     }
     
     // Update is called once per frame
     void FixedUpdate () {
         
         grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
 
         float move = Input.GetAxis("Horizontal");
 
         rb2d.velocity = new Vector2(move * moveSpeed, rb2d.velocity.y);
 
         if (move > 0 && !facingRight) {
             Flip ();
         } else if (move < 0 && facingRight) {
             Flip ();
         }
 
     } // End FixedUpdate
 
     void Flip() {
         facingRight = !facingRight;
         Vector3 theScale = transform.localScale;
         theScale.x *= -1;
         transform.localScale = theScale;
     }
 } // End Script Class

the ! in this case means “not”, so we’re assigning the variable with whatever the opposite of the boolean is. Flipping the scale by -1 reverses the images on that axis — in this case the x — this works in many graphics programs like Illustrator and the like!

Everybody Jump Around

We have a character that will respond to gravity, but for a platformer, we want to fight gravity. At first, jumping seems straightforward: move an object up. But making sure you can jump only when it makes sense requires a little more work. For one, we want to make sure we can only jump when the player is on ground.

We need to create an empty game object, which we will name groundCheck. Parent this object to the Player by dragging the new groundCheck gameobject in the Hierarchy on top of the Player, and then adjusting its location to be near the bottom of the feet of the player. We’re going to later check when the bottom of the feet are close to the ground, and only jump when that’s the case.

We also need to create a layer to distinguish the ground from other objects. Layers are similar to tags, in that they help distinguish items quickly from each other in scripting, but they work on a broader range than just tags.

Like tags, go to the top of the inspector window, and use the Layer drop down menu. Near the bottom of that list, click Add New Layer, an choose an empty slot to add “ground.” afterwards you’ll change the layer of your ground object to “ground”.

Let’s add some more code to our character controller:

 using UnityEngine;
 using System.Collections;
 
 public class Player: MonoBehaviour {
 
     public float moveSpeed;
     public float jumpSpeed;
 
     private Rigidbody2D rb2d;
     private SpriteRenderer sprite;
 
     private bool facingRight;
 
     public bool grounded = false;
     public Transform groundCheck;
     float groundRadius = 0.3f;
     public LayerMask whatIsGround;
 
     // Use this for initialization
     void Start () {
         rb2d = GetComponent<Rigidbody2D> ();
         sprite = GetComponent<SpriteRenderer> ();
     }
     
     // Update is called once per frame
     void FixedUpdate () {
         
         grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
 
         float move = Input.GetAxis("Horizontal");
 
         rb2d.velocity = new Vector2(move * moveSpeed, rb2d.velocity.y);
 
 
 
     }
 
     void Update () {
         if (grounded && Input.GetButtonDown("Jump")) {
             rb2d.AddForce(new Vector2(0, jumpSpeed));
             Debug.Log("Jump");
         }    
     }
 
     void Flip() {
         facingRight = !facingRight;
         Vector3 theScale = transform.localScale;
         theScale.x *= -1;
         transform.localScale = theScale;
     }
 }

Now we have a few things to edit in the Inspector for this script component.  Click  the groundCheck object and drag it to to the GroundCheck (Transform) slot. The What Is Ground will have a drop down, where you can select the ground layer that you just made.

In the conditional for calling the Jump button (Space bar by default in most computers), we also check for the grounded status at the same time, and use “&&” to make sure that both need to be true before we can jump.

In the inspector, you’ll have to play with the Jump Speed variable to get the effect you want, but start with a number like 200 and adjust it from there.

Duplicate the original ground platform you made to make a basic level. If you want to get advanced, try combining what we’ve done in the vertical space shooter with the platformer to create obstacles that can kill the player or ways for the player to attack and destroy obstacles.

For additional visual training, Unity Live Training: 2D Platformer Character Controller