Super Mario Bros Clone

About

My Super Mario Bros clone is a 2D platformer aimed at replicating the original game's mechanics, from player movement and gravity to AI and the iconic flag pole ending. This project was an exercise in learning Unity by closely mirroring the classic game's gameplay elements, offering a deep dive into game development fundamentals through hands-on practice.

Project Info

  • Role: Gameplay/AI Programmer
  • Team Size: 1
  • Time frame: 1 Month
  • Engine: Unity Engine
  • Langauge: C#

Things that I did in this project:

  • Developed a GameManager and SceneController class to manage shared game instances across different classes and ensure smooth transitions between scenes
  • Crafted a Camera Manager and Player Movement class to mimic the original game's camera dynamics and player movement, accurately replicating Mario's gravity and physics as well as those of other entities
  • Implemented a Sprite Renderer, complete with animations for Mario, enemies, and other entities' movements and demises
  • Recreated the movements and animations of the Goomba and Koopa, along with the coin collection and Flag Pole sequences

Movement Component

My initial focus was on replicating the movement and physics of Mario and other entities, given that Unity's default physics and gravity systems differ significantly from those of the original game. I aimed to closely mimic the original game's movement mechanics and physics for all entities, ensuring that the gameplay experience remained true to the classic while utilizing Unity's capabilities. For the entities I created a base script that has the common functionality for all, and just overriding the functions for any specific movement behaviours.

Mario's Movement
Entities Movement

Animations

Following the implementation of movement and physics, my next task was to recreate the Goomba and Koopa hit events accurately. For the Koopas, the turtle-like entities, I programmed them to retract into their shells when Mario jumps on them from above, subsequently allowing them to slide and eliminate other entities. During this process, I encountered challenges with the collision detection between Mario and the Koopas. To resolve these issues and streamline the interaction, I devised a solution where, upon Mario hitting a Koopa, the entity's sprite would switch to that of a shell. I then implemented a LayerMask check to enable the now-shell Koopa to impact and take out other entities.

For the Goombas, I replicated their iconic squishing effect by switching their sprites from a normal appearance to a squeezed one whenever Mario jumps on them. This sprite-changing technique not only preserved the authentic reaction seen in the original game but also provided me with valuable insights into sprite manipulation. Inspired by this approach, I applied a similar concept to Mario, enabling him to grow in size upon consuming mushrooms. By changing Mario's sprite in response to interacting with power-ups, I could effectively mimic the classic mechanic of Mario's transformation.

For all of the animations I developed a single unified script, covering sprite movement and death animations for Mario, enemies, and other entities.

Mushroom Powerup
Koopa Damage other entities

Flag Pole Scene

After successfully replicating the core mechanics, my curiosity led me to recreate the iconic Flag Pole scene, where Mario slides down the Flag Pole and enters the Castle. To accomplish this, I implemented a detection system to check if and where Mario makes contact with the Flag Pole. Depending on the point of contact, I programmed Mario to automatically move down the pole and subsequently transition towards the Castle.

public class FlagPole : MonoBehaviour
{
    [SerializeField]
    private Transform _flag;

    [SerializeField]
    private Transform _poleBottom;

    [SerializeField]
    private Transform _castle;

    [SerializeField]
    private float _speed = 6f;

    private void OnTriggerExit2D(Collider2D other)
    {
        if(other.CompareTag("Player"))
        {
            // making the flag to move to the bottom of the pole
            StartCoroutine(MoveTo(_flag, _poleBottom.position));    
            
            // making the flag to move to the bottom of the pole
            StartCoroutine(LevelCompleteSequence(other.transform));                
        }
    }

    private IEnumerator LevelCompleteSequence(Transform player)
    {
        // disabling marios movement
        player.GetComponent().enabled = false;              

        yield return MoveTo(player, _poleBottom.position);
        yield return MoveTo(player, player.position + Vector3.right);

        // making the mario move down once he hits the flag pole
        yield return MoveTo(player, player.position + Vector3.right + Vector3.down);  
        
        // making the mario to move towards the castle
        yield return MoveTo(player, _castle.position);                                          

        // trigger the next level or gameOver Screen
        player.gameObject.SetActive(false);                                         

        yield return new WaitForSeconds(2f);

        // loads the game over screen
        GameManager.Instance.GameOver();                                

    }

    private IEnumerator MoveTo(Transform subject, Vector3 destination)
    {
        while(Vector3.Distance(subject.position, destination) > 0.125f)
        {

            // moving the flag
            subject.position = Vector3.MoveTowards(subject.position, destination, _speed * Time.deltaTime);     
            yield return null;
        }

        subject.position = destination;
    }
}