Game Design and Development in Unity Editor | Beginner Tutorial | First 2D Platform Game | With C# Scripts

Learn how to build a 2d platformer game in unity and C# from scratch without any code experience, made for beginners in game design and development. A step-by-step tutorial to help you build your very first 2d platform game like the classic ones Mario, Contra, Canabalt, etc. Simple game mechanics and classic pixel art graphics. 



Setting Up Unity Hub & Editor

  1. Install Unity Hub
  2. Install Unity Editor version 2021.3.11f1 or the latest one.
  3. Create New Project and select 2D core. Rename the file and folder location.
  4. We enter the Unity Game Dev environment. One scene is one level of the game.


 

Tile Map and Tile Palette

A tile map is like a puzzle which allows you to build the game environment piece by piece much like a puzzle. Here we will create the background, foreground and the terrain using the tile map and tile palette function in Unity.

  • Grab some free assets from the Unity Asset Store or create your own graphics using Photoshop or other tools which is Pixel Art. Unity Asset Store is full of multiple resources to create a game.
  • Click on ‘My Assets’ to add the assets in your unity editor → Click on ‘Open in Unity’ in the browser once added.
  • Go to Package Manager in unity editor and click on download to download the added assets.
  • Click on ‘Import’ to import the particular assets from the package manager in your project. The assets will be visible in the Project panel at the bottom.
  • One square on the grid display in the centre in “One Unit”. Before adding the asset in the scene you need to change the pixels per unit otherwise it will be very small. Change every asset from 100 to 16.
  • To add terrain, locate the file in the project browser → Change the pixel per unit to 16 from 100 → Change the sprite mode from ‘single’ to ‘multiple’ → Now you need to slice the entire sprite into individual parts so that you can pick them up separately and create your own custom environment → Click on ‘Apply’ to save changes.
  • To slice the sprite into different parts, go to Sprite Editor → Click on ‘Slice’ → Change type from automatic to ‘grid by cell size’ → Change pixel size value x =16 and y=16 → Click on Slice. This should give you a nice grid cutlines on the sprites. Close the window while saving the slice changes.

  • You should see a sliced up preview of the terrain sprite at the bottom right corner. This means that your terrain sprite is spliced into equal parts.
  • Now we create a tilemap which is sort of like a frame for our puzzle pieces. Right click in the hierarchy menu at the right → select 2d object → then tilemap → then select rectangular from there → Change the name as ‘Terrain’.
  • Open the tile palette that will help you to create the tile map. Go to window → 2d → select tile palette. Click on ‘create new palette’ → change the name to terrain → select the location of the folder.
  • Drag and drop the terrain sprite from the project browser into the tile palette window. Choose an appropriate folder location and name it ‘Tiles’.
  • Use the creation tools at the top of tile palette to create your own terrain in the game environment.

  • Once the terrain is created, make sure you are creating in the terrain layer and don’t worry you can change anything anytime.
  • Now we need to create the background in a similar way but we segregate the tile map in a different layer and a different order otherwise it will overlap with the terrain.
  • Create another Tilemap in the hierarchy browser and name it as ‘Background’.
  • While creating the Tilemap from the tile palette select the ‘Background’ from the drop-down menu at the top. Remember to keep the background in the background and so on for the different layers.
  • You need to change the pixel per unit and slice the background in the same way we did for the terrain in previous steps.

  • Now you will need to add a sorting layer to background layer and other layers to define order of tilemaps otherwise all of them will be in the same layer. Click on ‘Background’ → go to ‘Additional Settings’ in the Inspector window panel → go to ‘Sorting Layer’ → add sorting layer → click on + sign → name it accordingly as ‘background’ and make one for terrain as well.
  • Remember to keep sorting layers or change orders whenever you want to place items on top of other. Helps in organisation and categorization.

Creating a Player

  • Go to hierarchy, select 2d object, select sprite, select square, rename as ‘player’.
  • Select your player’s idle character from the project browser → Drag and drop it in the sprite renderer component of the player sprite.
  • Make sure the Filter Mode for every Sprite element is set to ‘Point(no filter) rather than ‘bilinear’ so that you can see sharp pixel art.

  • Sprite renderer component is used to render the blank sprite.
  • Now add physics to the player sprite by adding specific component to the sprite.
  • Make the player a physical body which will give it gravity by adding a ‘rigidbody2d’ component from the Inspector tab.
  • Add ‘boxcollider2d’ to the player sprite which will make it collide with the ground below.
  • Click on ‘edit’ boundary in the boxcollider to change the boundaries. Make them smaller than the character so that the collision doesn’t occur from outer boundary.

  • But applying a collider on the player is not enough. You need to apply it on the other colliding surface too.
  • Apply the ‘Tilemap Collider 2d’ to the terrain layer. This will automatically create a boundary for collision.
  • But the problem is that there are boundaries for each unit which we don’t need. So create another collider named ‘Composite Collider 2D’ → tick on ‘Used by Composite’ under the Tile Map Collider 2D component.
  • Now hit play. Everything falls down because even the terrain has a bodytype as a rigidbody set to dynamic → change it to ‘static’. Now the terrain stays in place and the player falls on the terrain.


Player Movement

  • To make the player move, you need to write a C# script. Go to components in Player inspector. Click on ‘Add Component’ → New Script → Name it as ‘PlayerMovement’ in Pascale Case ( where first letter is in capitals).
  • Create a folder called ‘Scripts’ in the project browser → Drag and Drop the script in it → Double click on the file to open the code in Visual Studio.
  • To switch on autocomplete in Visual Studio, you need to link it with Unity. Go to Edit → Preferences → External Tools → External Script Editor → Choose Microsoft Visual Studio Code form the drop down → Check on Embedded and Local Packages → Click on Regenerate Files → Double click on the script to open in Microsoft Visual Studio.

  • In VS, there are two methods already set for the game.
  • The Start method is used to put code that will run only once at the start of the game.
  • The Update method is used to put code that will run in each frame of the game.

  • First we’ll add code to make the player jump when the space key is pressed.
  • Ctrl K + Ctrl D → Autoformats your code if the authoring is messed up.
  • Make sure that the Player Movement script to the player sprite and not anyone else. If you’ve added it other, simply remove the component and add it to the player sprite.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown("space"))
        {
            GetComponent<Rigidbody2D>().velocity = new Vector3(0, 14, 0);
        }
    }
} // don't use this script, it is incomplete and wrong


  • Instead of GetKeyDown we can specify a name to the jump action in the Edit → Project Settings → Input Manager → Axes and you can see jump has “Jump” as the name which we will use in the VS Script.
  • For movement along the horizontal axis we simply use the name “Horizontal” which already has left and right defined for positive and negative values.
  • Instead of using the if method for horizontal movement, we use the velocity method because otherwise it won’t be supported by a joystick.


using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class PlayerMovement : MonoBehaviour

{

    private Rigidbody2D rb; // defines the rigidbody component a variable name


    // Start is called before the first frame update

    void Start()

    {

        rb = GetComponent<Rigidbody2D>(); // gets the rigidbody component once at the beginning of the game instead of every frame

    }


    // Update is called once per frame

    void Update()

    {

        float dirX = Input.GetAxisRaw("Horizontal"); // for left and right movement of the player. Raw makes the movement abrupt instead of smooth.

        rb.velocity = new Vector2(dirX * 7f, rb.velocity.y); // we don't use if method for joystick purposes. 


        if (Input.GetButtonDown("Jump"))

        {

            rb.velocity = new Vector2(rb.velocity.x, 14f); // for change in y-axis which makes the player jump when space key is pressed

        } 

    }

}


  • Always make sure you check the Z value of every layer so that every thing is on the same plane.
  • To make the camera follow the player, simply nest it within the player layer in hierarchy.
  • To stop the player from rotating, go to player layer → constraints in inspector tab → tick on freeze rotation in Z axis.
  • Always remember to make changes in the edit mode and not in the play mode or else all the changes will be reverted once the play icon is changed to off.


Animation and Animator

Before animating an asset you need to create a sprite sheet with individual frames sliced up and combined in a single file. Need to look at a different tutorial to understand how to do this.

To create a new animation:

Go to Project Browser → Go to the first folder of Assets → Right Click → Create → Folder → Rename to Animations → Double Click and Open this empty folder → Right Click → Create → Animation → Rename to Player_Idle or whatever you wish → Click on the Animation Icon → Drag and Drop it on the Player Layer in Hierarchy Window

The second file created after dragging and dropping is the one that connects the animation to the player.

To have an animation that continues infinitely, just click on the first animation triangular icon → go to inspector window at right → tick the loop condition

Now, to add the animation assets, click on Window → Animation → Animation. This will open the animation panel which you can drag and place anywhere.

While keeping the Player layer selected → go to the player assets in the project browser → select and expand the player animation into individual frames with the arrow icon → select all the individual frames → drag and drop them in the animation window → It creates different keyframes with the timeline → Hit Play in the animation window → set the sample rate to something appropriate → close the animation window or push it into the bottom panel


Repeat the same steps to create another animation named Player_Running
To tell Unity when to switch to Idle animation and when to switch to running animation, go to Window → Animation → Animator


Now to make transitions from running to idle, Right Click on Player_Running → Make Transition → Drag the arrow to Idle → Right Click on Player_Idle → Make Transition → Drag the arrow to running

We can define conditions to the transitions by clicking on the arrows and making changes in the right panel.

Set a parameter to enable changes to the transitions. Go to Parameter tab in Animator → Click on + sign → Select bool → rename to running

Click on one arrow → Go to Inspector window at right → Deselect Has Exit Time → Change transition duration to 0. This makes it feel more like a 2d game with abrupt changes. Do the same with other arrow.

Now we need to add conditions.

Select the running rectangle in the animator panel → go to parameters tab → click on + icon on left → select bool → name as running → select the arrow from running to idle → go to conditions in inspector panel → click on + icon → select false

This means that if running condition is false, animation will switch from running to idle.

Do the same for idle to running arrow, but select true for condition. This means that if running is true, animation will switch from idle to running.

Now we add the C# script for animation within the Player Movement Script:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class PlayerMovement : MonoBehaviour

{

    private Rigidbody2D rb; // defines the rigidbody component a variable name

    private Animator anim; //defines the animator component a variable

    private SpriteRenderer sprite; // defines the sprite renderer variable

    private float dirX = 0f; // sets the float variable at 0f


    [SerializeField] private float moveSpeed = 7f; // instead of using numeric value we set a variable. SerializeField allows you to control values within Unity with a controller in the display panel.

    [SerializeField] private float jumpForce = 14f; // make your code more readable by adding variables instead of numeric values


    // Start is called before the first frame update

    void Start()

    {

        rb = GetComponent<Rigidbody2D>(); // gets the rigidbody component once at the beginning of the game instead of every frame.

        sprite = GetComponent<SpriteRenderer>(); //gets the sprite renderer component to flip the x axis while changing direction in running 

        anim = GetComponent<Animator>(); // gets the animator component once at the start of the game.

    }


    // Update is called once per frame

    void Update()

    {

        dirX = Input.GetAxisRaw("Horizontal"); // for left and right movement of the player. Raw makes the movement abrupt instead of smooth.

        rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y); // we don't use if method for joystick purposes. 


        UpdateAnimationState();


    }

    private void UpdateAnimationState() // You can change multiple texts in VS by selecting it and CTRL+R twice.

    {

        if (Input.GetButtonDown("Jump"))

        {

            rb.velocity = new Vector2(rb.velocity.x, jumpForce); // for change in y-axis which makes the player jump when space key is pressed

        }


        if (dirX > 0f)

        {

            anim.SetBool("running", true); // if x direction is greater than 0, i.e. moving right then the running animation will be played.

            sprite.flipX = false; //doesn't flip the sprite when x direction is greater than 0

        }


        else if (dirX < 0f)

        {

            anim.SetBool("running", true); // if x direction is less than 0, i.e. moving left then the running animation will be played.

            sprite.flipX = true; // flips the sprite when the x direction is less than 0 

        }

        else

        {

            anim.SetBool("running", false); //the player will switch to idle when there will be no movement along the x axis.

        }

    }

}


Now we need to set the camera out of the player component and create another C# script for camera control. We call this CameraController Script.

First drag the main camera out of the player component in heriarchy panel and then add the following script to the main camera.


using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class CameraController : MonoBehaviour

{

    [SerializeField] private Transform player; // since we cannot get a component from a different layer such as the player, we need to add the ability in unity to drag and drop the player file into the field

    // after this, please drag and drop your player file from the hierarchy menu to the empty field under this script in the inspector panel of main camera.


    private void Update() 

    {

        transform.position = new Vector3 (player.position.x, player.position.y, transform.position.z); // this makes the camera follow the player in x and y direction while remaining in the z direction.

    }

}


Multiple Animations and Switching b/w Them

Create all the animations for jumping, falling, running and idle and then work in animator to connect them and give them a logic.

Make sure you click on each animation and set ‘Loop Time’ option to yes otherwise you will see the image only once.

Right click on each rectangle in animator panel → make transitions from each to all


Once all the connections are made, the logic is given in the Player Movement Script to switch between different states.

remove the running bool which was a parameter previously added

click on + icon in parameter → select Int → name it as ‘state’

now we need to change the conditions for all the arrows in the animator and set them to the corresponding number defined in script

idle = 0

running = 1

jumping = 2

falling = 3

now go to unity → animator → click on each arrow → look into inspector → add condition → set it to equal to → insert the number corresponding to the state tending towards.

remove exit time and transition duration for a more rigid 2d game effect.

We change the PlayerMovement Script in the following way in VS Code: 


using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class PlayerMovement : MonoBehaviour

{

    private Rigidbody2D rb; // defines the rigidbody component a variable name

    private Animator anim; //defines the animator component a variable

    private SpriteRenderer sprite; // defines the sprite renderer variable

    private float dirX = 0f; // sets the float variable at 0f


    [SerializeField] private float moveSpeed = 7f; // instead of using numeric value we set a variable. SerializeField allows you to control values within Unity with a controller in the display panel.

    [SerializeField] private float jumpForce = 14f; // make your code more readable by adding variables instead of numeric values


    private enum MovementState { idle, running, jumping, falling} // combines all the states within a single method which can be configured in unity animator



    // Start is called before the first frame update

    void Start()

    {

        rb = GetComponent<Rigidbody2D>(); // gets the rigidbody component once at the beginning of the game instead of every frame.

        sprite = GetComponent<SpriteRenderer>(); //gets the sprite renderer component to flip the x axis while changing direction in running 

        anim = GetComponent<Animator>(); // gets the animator component once at the start of the game.

    }


    // Update is called once per frame

    void Update()

    {

        dirX = Input.GetAxisRaw("Horizontal"); // for left and right movement of the player. Raw makes the movement abrupt instead of smooth.

        rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y); // we don't use if method for joystick purposes. 


        UpdateAnimationState();


    }

    private void UpdateAnimationState() // You can change multiple texts in VS by selecting it and CTRL+R twice.

    {

        MovementState state;


        if (Input.GetButtonDown("Jump"))

        {

            rb.velocity = new Vector2(rb.velocity.x, jumpForce); // for change in y-axis which makes the player jump when space key is pressed

        }


        if (dirX > 0f)

        {

            // anim.SetBool("running", true); 

            // if x direction is greater than 0, i.e. moving right then the running animation will be played. remove it after setting Int

            state = MovementState.running;

            sprite.flipX = false; //doesn't flip the sprite when x direction is greater than 0

        }


        else if (dirX < 0f)

        {

            // anim.SetBool("running", true); 

            // if x direction is less than 0, i.e. moving left then the running animation will be played. remove it after setting Int

            state = MovementState.running;

            sprite.flipX = true; // flips the sprite when the x direction is less than 0 

        }

        else

        {

            // anim.SetBool("running", false); 

            //the player will switch to idle when there will be no movement along the x axis.remove it after setting Int

            state = MovementState.idle;

        }


        if (rb.velocity.y > .1f) // if y axis velocity is more then the player will jump

            {

            state = MovementState.jumping;

        }


        else if (rb.velocity.y < -.1f) // or if the y velocity is less then the player will fall

        {

            state = MovementState.falling;

        }


        anim.SetInteger("State", (int)state); // used to set the animation for the specific state

    }

}


Grounding Check using Boxcast

right now the player can fly away with multiple jumps and fall into emptiness. we need some movement constraints

Go to Terrain → Layer in Inspector panel → Add Layer → User Layer 6 → Name as ‘Ground’ → Back to Terrain → Layer → Select Ground from list

alter the player movement script in the following way:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class PlayerMovement : MonoBehaviour

{

    private Rigidbody2D rb; // defines the rigidbody component a variable name

    private BoxCollider2D coll; // create a variable for the box collider of player

    private Animator anim; //defines the animator component a variable

    private SpriteRenderer sprite; // defines the sprite renderer variable

    private float dirX = 0f; // sets the float variable at 0f


    [SerializeField] private LayerMask jumpableGround;


    [SerializeField] private float moveSpeed = 7f; // instead of using numeric value we set a variable. SerializeField allows you to control values within Unity with a controller in the display panel.

    [SerializeField] private float jumpForce = 14f; // make your code more readable by adding variables instead of numeric values


    private enum MovementState { idle, running, jumping, falling} // combines all the states within a single method which can be configured in unity animator



    // Start is called before the first frame update

    void Start()

    {

        rb = GetComponent<Rigidbody2D>(); // gets the rigidbody component once at the beginning of the game instead of every frame.

        coll = GetComponent<BoxCollider2D>(); // gets the boxcollider component of the player at the start

        sprite = GetComponent<SpriteRenderer>(); //gets the sprite renderer component to flip the x axis while changing direction in running 

        anim = GetComponent<Animator>(); // gets the animator component once at the start of the game.

    }


    // Update is called once per frame

    void Update()

    {

        dirX = Input.GetAxisRaw("Horizontal"); // for left and right movement of the player. Raw makes the movement abrupt instead of smooth.

        rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y); // we don't use if method for joystick purposes. 


        UpdateAnimationState();


    }

    private void UpdateAnimationState() // You can change multiple texts in VS by selecting it and CTRL+R twice.

    {

        MovementState state;


        if (Input.GetButtonDown("Jump") && IsGrounded()) // add the condition that player jumps only when grounded

        {

            rb.velocity = new Vector2(rb.velocity.x, jumpForce); // for change in y-axis which makes the player jump when space key is pressed

        }


        if (dirX > 0f)

        {

            // anim.SetBool("running", true); 

            // if x direction is greater than 0, i.e. moving right then the running animation will be played. remove it after setting Int

            state = MovementState.running;

            sprite.flipX = false; //doesn't flip the sprite when x direction is greater than 0

        }


        else if (dirX < 0f)

        {

            // anim.SetBool("running", true); 

            // if x direction is less than 0, i.e. moving left then the running animation will be played. remove it after setting Int

            state = MovementState.running;

            sprite.flipX = true; // flips the sprite when the x direction is less than 0 

        }

        else

        {

            // anim.SetBool("running", false); 

            //the player will switch to idle when there will be no movement along the x axis.remove it after setting Int

            state = MovementState.idle;

        }


        if (rb.velocity.y > .1f) // if y axis velocity is more then the player will jump

            {

            state = MovementState.jumping;

        }


        else if (rb.velocity.y < -.1f) // or if the y velocity is less then the player will fall

        {

            state = MovementState.falling;

        }


        anim.SetInteger("state", (int)state); // used to set the animation for the specific state

    }


    private bool IsGrounded() // check wether the player is grounded or not

    {

       return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround); // imagine it as a tiny box moved a little bit down to check if it overlaps with ground

    } 

    // after this step, select ground from the jumpable ground list in the player movement script component

}


to resolve the problem of player sticking to the terrain,

go to terrain → add platform effector 2d component → deselect use one way → in composite collider 2d component → tick use by effector


Collect and Count Items

Tick on ‘Is Trigger’ option in Boxcollider 2d component of the collectible item to avoid clashing with it.

write the following code for ItemCollector component in player element

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // used to access the ui elements of unity

public class ItemCollector : MonoBehaviour
{
    private int collectible = 0; // define integer variable for collectibles

    [SerializeField] private Text collectibleText; // to be used to enter the canvas text in unity where the score needs to be displayed

    private void OnTriggerEnter2D(Collider2D collision) // on trigger is used when you pass through the object without colliding with it. use on collision for traps
    {
        if (collision.gameObject.CompareTag("Collectible")) // condition compares the tag you gave to your collectible items in unity 
        {
            Destroy(collision.gameObject); // removes the object from the scene
            collectible++; // adds to the number of collectibles by the amount collected
            collectibleText.text = "Score: " + collectible; // changes the collectible text which is displayed as score in unity
        }
    }
}

the “Collectible” string should match the name of the tag you provided for your collectible. make sure the names are correct

drag and drop the score text from the heirarchy to the collectible text empty field in the item collector script in the player’s inspector panel


Player Death Logic

First add an animation for player death.

go to animator → drag the death above any state → right click on any state → make transition → connect with death → go to parameters → click on + → add trigger → name it as death → click on the arrow → go to inspector panel → click on conditions → change state to death → remove exit time and transition duration

Create a new ‘Player Life’ Script to add logic for player death and revival:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.SceneManagement; // gains access to the scene manager which allows us to restart levels or change levels


public class PlayerLife : MonoBehaviour

{

    private Rigidbody2D rb; // will be used to stop player from moving after death

    private Animator anim; // for death animation 


    private void Start()

    {

        rb = GetComponent<Rigidbody2D>(); // get the rigidbody component from the player

        anim = GetComponent<Animator>(); // get the animation

    }


    private void OnCollisionEnter2D(Collision2D collision) // to be used fr traps which hapens right at collision

    {

        if (collision.gameObject.CompareTag("Trap")) // condition to touch any trap with the tag of trap with it. be careful with tagging

        {

            Die(); // defined by us

        }

    }


    private void Die()

    {       

        rb.bodyType = RigidbodyType2D.Static; // player changes state from dynamic to static and can't be moves

        anim.SetTrigger("death"); // death animation is triggered right now

    }


    private void RestartLevel() // to restart after death

    {

        SceneManager.LoadScene(SceneManager.GetActiveScene().name); //activates scene manager and gets the active scene present for restart. can be changed to zero level or start screen if required

    }

}


To create a delay, add another keyframe in the animation panel of player_death by recording and turning off sprite renderer for a moment.

To add the restart function, go to animation panel → click on little bookmark icon called event → add it at a later stage in the animation → go to top right panel → select restart function from drop down. → test


Connecting Mobile Device to Unity

  • Make sure you downloaded the android and iOS package before installing unity editor. If nit you can do so from unity hub.
  • Go to build settings and change build from windows to Android.
  • Download android studio.
  • Go to project settings in unity, go to editor, select all android devi
  • Go to preferences, go to player, under screen resolution deselect portrait modes
  • Change your game display to 16:9 and adjust the camera and other elements accordingly
  • Connect your mobile via USB, select transfer files option
  • On your android phone, go to about phone, go to software version or OS version and tap on it multiple times. It will say, ‘You're now a developer’
  • Search for developer tools in the settings
  • Switch on USB debugging and select OK
  • Now hit play in Unity and the game should display simultaneously to your android phone.

Script for player movement to add move controls to the UI:


using System.Collections;

using System.Collections.Generic;

using Unity.VisualScripting;

using UnityEngine;

//using System;


public class PlayerMovement : MonoBehaviour

{

    private Rigidbody2D rb; // defines the rigidbody component a variable name

    private BoxCollider2D coll; // create a variable for the box collider of player

    private Animator anim; //defines the animator component a variable

    private SpriteRenderer sprite; // defines the sprite renderer variable

    private float dirX = 0f; // sets the float variable at 0f



    private bool moveLeft; // define boolean variable for moving left

    private bool moveRight; // define boolean variable for moving right


    [SerializeField] private LayerMask jumpableGround;


    [SerializeField] private float moveSpeed = 7f; // instead of using numeric value we set a variable. SerializeField allows you to control values within Unity with a controller in the display panel.

    [SerializeField] private float jumpForce = 14f; // make your code more readable by adding variables instead of numeric values


    private enum MovementState { idle, running, jumping, falling } // combines all the states within a single method which can be configured in unity animator

    MovementState state;


    // Start is called before the first frame update

    void Start()

    {

        rb = GetComponent<Rigidbody2D>(); // gets the rigidbody component once at the beginning of the game instead of every frame.

        coll = GetComponent<BoxCollider2D>(); // gets the boxcollider component of the player at the start

        sprite = GetComponent<SpriteRenderer>(); //gets the sprite renderer component to flip the x axis while changing direction in running 

        anim = GetComponent<Animator>(); // gets the animator component once at the start of the game.


        moveLeft = false; // at the beginning the buttons should not be activated 

        moveRight = false;

    }




    // Update is called once per frame

    void Update()

    {

        dirX = Input.GetAxisRaw("Horizontal"); // for left and right movement of the player. Raw makes the movement abrupt instead of smooth.

        //if (dirX >= 1) { System.Diagnostics.Debug.WriteLine("Value of Dirx is " + dirX + "Velocity = " + rb.velocity); }

        rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y); // we don't use if method for joystick purposes. 


        UpdateAnimationState(); // calls for the animation state from the next method


        MovementPlayer(); // for player movement using the buttons

    }


    // I am pressing the left button

    public void PointerDownLeft()

    {

        moveLeft = true;

        state = MovementState.running;

        sprite.flipX = true;

    }


    // I am not pressing the left button

    public void PointerUpLeft()

    {

        moveLeft = false;

        state = MovementState.idle;

    }


    // repeat the above two steps with the right button


    // I am pressing the right button

    public void PointerDownRight()

    {

        moveRight = true;

        state = MovementState.running;

        sprite.flipX = false;

    }


    // I am not pressing the right button

    public void PointerUpRight()

    {

        moveRight = false;

        state = MovementState.idle;

    }


    private void UpdateAnimationState() // You can change multiple texts in VS by selecting it and CTRL+R twice.

    {

        // MovementState state; //Change by Abhishek - unomment it


        if (Input.GetButtonDown("Jump") && IsGrounded()) // add the condition that player jumps only when grounded

        {

            rb.velocity = new Vector2(rb.velocity.x, jumpForce); // for change in y-axis which makes the player jump when space key is pressed

        }


        if (dirX > 0f)

        {

            // anim.SetBool("running", true); 

            // if x direction is greater than 0, i.e. moving right then the running animation will be played. remove it after setting Int

            state = MovementState.running;

            sprite.flipX = false; //doesn't flip the sprite when x direction is greater than 0

            rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);

            if (dirX >= 1) { System.Diagnostics.Debug.WriteLine("Value of Dirx is " + dirX + " Velocity = "+ rb.velocity + " moveSpeed = "+ moveSpeed); }


        }


        else if (dirX < 0f)

        {

            // anim.SetBool("running", true); 

            // if x direction is less than 0, i.e. moving left then the running animation will be played. remove it after setting Int

            state = MovementState.running;

            sprite.flipX = true; // flips the sprite when the x direction is less than 0 

        }

        else

        {

            // anim.SetBool("running", false); 

            // the player will switch to idle when there will be no movement along the x axis.remove it after setting Int

            state = MovementState.idle;

        }


        if (rb.velocity.y > .1f) // if y axis velocity is more then the player will jump

        {

            state = MovementState.jumping;

        }


        else if (rb.velocity.y < -.1f) // or if the y velocity is less then the player will fall

        {

            state = MovementState.falling;

        }


        anim.SetInteger("state", (int)state); // used to set the animation for the specific state

    }


    private bool IsGrounded() // check wether the player is grounded or not

    {

        return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround); // imagine it as a tiny box moved a little bit down to check if it overlaps with ground

    }

    // after this step, select ground from the jumpable ground list in the player movement script component


    private void MovementPlayer()

    {

        // if I press the left button

        if (moveLeft)

        {

            dirX = -moveSpeed;

            UpdateAnimationState();

        }


        // if I press the right button


        else if (moveRight)

        {

            dirX = moveSpeed;

            UpdateAnimationState();

        }


        // if I don't press any button

        else

        {

            dirX = 0f;

            UpdateAnimationState();


        }

    }


    // add the movement force to the player

    private void FixedUpdate()

    {

        rb.velocity = new Vector2(dirX, rb.velocity.y);

    }


    public void jumpButton()

    {

        if (rb.velocity.y == 0)

        {

            rb.velocity = Vector2.up * jumpForce;

        }

    }


}


I hope this was helpful to beginners. It helped me tremendously during my game design and development journey and I felt it would be beneficial for others too. 

I will be adding some methods to connect different scenes, adding start screen and end scenes, adding sounds and creating a finish script for when the player hits the finish checkpoint! 

Look around this space for more educational content in the field of mobile/web and game design/development. Share it with your students, batchmates, and friends!

Checkout the outcome of first two weeks of game design and development on YoutTube: Mario Goes to College | Unity | C# | Game Iteration | Assignment | TU Dublin | CDM & UX - YouTube


Popular Posts

Perform CRUD (Create, Read, Update, Delete) Operations using PHP, MySQL and MAMP : Part 4. My First Angular-15 Ionic-7 App

Visualize Your Data: Showcasing Interactive Charts for Numerical Data using Charts JS Library (Part 23) in Your Angular-15 Ionic-7 App

How to Build a Unit Converter for Your Baking Needs with HTML, CSS & Vanilla JavaScript