Carl Vitasa | Gameplay Programmer

Carl Vitasa - Gameplay Programmer Portfolio

 MORPH | 2D Action Platformer

Sole Programmer | Tool Creator

"Help MORPH traverse through the Darklands by transforming into the different phases of matter; liquid, solid, and gas!"

Unity 5, C#



Goals for This Game

  • Outline understanding of programming mechanics through documentation
  • Learn new 2D programming techniques in the Unity Engine.
  • Create a fun and well polished experience.

Proof of Concept 


MORPH is a 2D action puzzle game in which the player must navigate through an array of different maps by “morphing” between 3 unique forms. The aesthetic is derived solely from primitives to pull focus towards gameplay mechanics and level design.

States of Matter Breakdown

After some consideration the concept of the cube Jack, the level designer for this game, and I felt that it was not unique enough state since the player can always turn into a gas state and travel larger distances. We then decided to play with concept of the phases of matter.

Water (Liquid Phase)

  • Movement: left and right
  • Gravity: yes
  • Interactive Objects: water-wheels
  • Hazards: Black Tar

Gas Cloud (Gas Phase)

  • Movement: left and right 
  • Gravity: N/A (forced to ascend upwards indefinitely)
  • Interactive Objects: air vents
  • Hazards: Vacuum 

Ice Cube (Solid Phase)

  • Movement: N/A (moves purely based on momentum carried over by previous states or graviy)
  • Gravity: yes
  • Interactive Objects: pressure buttons
  • Hazards: Spike Pit

Icons by Iconmonstr


Creating MORPH

The water was constructed with the help from thecodeartist.mx liquid physics 2D tutorial. I used enums to store what "phase type" each state was. From there I created a function to set the parameters of each phase type.

 

public enum PhaseType
        {
            water,
            gasCloud,
            iceCube
        }
        public PhaseType phaseType;
        
        void CheckPhases()
        {
            switch (phaseType)
            {
                case PhaseType.water:
                    waterObject.SetActive(true);
                    gasCloudObject.SetActive(false);
                    iceCubeObject.SetActive(false);
                    myRigidBody2D.velocity = new Vector2(Input.GetAxis("Horizontal") * movementSpeed, myRigidBody2D.velocity.y);
                    myRigidBody2D.gravityScale = 1;
                    myRigidBody2D.constraints = RigidbodyConstraints2D.FreezeRotation;
                    break;
                case PhaseType.gasCloud:
                    waterObject.SetActive(false);
                    gasCloudObject.SetActive(true);
                    iceCubeObject.SetActive(false);
                    myRigidBody2D.velocity = new Vector2(Input.GetAxis("Horizontal") * movementSpeed, gasCloudRiseSpeed);
                    myRigidBody2D.gravityScale = 1;
                    myRigidBody2D.constraints = RigidbodyConstraints2D.FreezeRotation;
                    break;
                case PhaseType.iceCube:
                    waterObject.SetActive(false);
                    gasCloudObject.SetActive(false);
                    iceCubeObject.SetActive(true);
                    myRigidBody2D.gravityScale = 2;
                    myRigidBody2D.constraints &= ~RigidbodyConstraints2D.FreezeRotation;
                    break;
            }
        }
 
 

void SwitchPhases()
        {
          // Water Phase
          if (phaseType == PhaseType.water)
          {
              if (Input.GetKeyDown(KeyCode.RightArrow))
              {
                  phaseType = PhaseType.gasCloud;
              }
              else if (Input.GetKeyDown(KeyCode.LeftArrow))
              {
                  phaseType = PhaseType.iceCube;
              }
          }
          // Gas Cloud Phase
          else if (phaseType == PhaseType.gasCloud)
          {
              if (Input.GetKeyDown(KeyCode.RightArrow))
              {
                  GameObject GasAwayParticlesClone = Instantiate(Resources.Load("GasAwayParticles"), transform.position, 
                                                     Quaternion.identity) as GameObject;
                  Destroy(GasAwayParticlesClone, 3.0f);
                  phaseType = PhaseType.iceCube;
              }
              else if (Input.GetKeyDown(KeyCode.LeftArrow))
              {
                  GameObject GasAwayParticlesClone = Instantiate(Resources.Load("GasAwayParticles"), transform.position, 
                                                     Quaternion.identity) as GameObject;
                  Destroy(GasAwayParticlesClone, 3.0f);
                  phaseType = PhaseType.water;
              }
          }
          // Ice Cube Phase
          else if (phaseType == PhaseType.iceCube)
          {
              if (Input.GetKeyDown(KeyCode.RightArrow))
              {
                  GameObject IceAwayParticlesClone = Instantiate(Resources.Load("IceAwayParticles"), transform.position, 
                                                     Quaternion.identity) as GameObject;
                  Destroy(IceAwayParticlesClone, 3.0f);
                  phaseType = PhaseType.water;
              }
              else if (Input.GetKeyDown(KeyCode.LeftArrow))
              {
                  GameObject IceAwayParticlesClone = Instantiate(Resources.Load("IceAwayParticles"), transform.position, 
                                                     Quaternion.identity) as GameObject;
                  Destroy(IceAwayParticlesClone, 3.0f);
                  phaseType = PhaseType.gasCloud;
              }
          }
 

Water

Traversing through the level as water felt smooth; the water gushing through the walls created a beautiful aesthetic and its behavior paved the way for a lot of potentially creative mechanics, but it was still just a body of water.
This made it hard to connect with for it lacked character

Water Wheel Script

 

using UnityEngine;
      using System.Collections;

      public class WaterWheel : MonoBehaviour
      {
          public bool activateObject = false;
          public float wheelSpeed;

          private float timer = 0.1f;

          void OnCollisionStay2D()
          {
              timer = 0.1f;
              GetComponent().useMotor = false;
              activateObject = true;
          }

          void Update ()
          {
              wheelSpeed = GetComponent().angularVelocity;
              if (timer > 0)
              {
                  timer -= Time.deltaTime;
              }
              else if (timer < 0)
              {
                  GetComponent().useMotor = true;
                  activateObject = false;
                  timer = 0;
              }
          }
      }
 

Gas


We decided to make the gas constantly rise in order to act as a jump of sorts.
This would be your way of navigating upwards as well as being teleported to a new location through vents. 

Vents Script

 

using UnityEngine;
      using System.Collections;

      public class Vents : MonoBehaviour
      {
          private MORPH morphScript;
          public GameObject ventExit;

          void Start ()
          {
              morphScript = GameObject.Find("MORPH").GetComponent();
          }
        
          void OnCollisionEnter2D(Collision2D other)
          {
              if (other.gameObject.name == "MORPH" && morphScript.phaseType == MORPH.PhaseType.gasCloud)
              {
                  StartCoroutine(WaitThenTranferGas(1));
                  other.transform.position = new Vector2 (ventExit.transform.position.x, ventExit.transform.position.y + 0.5f);
              }
          }

          IEnumerator WaitThenTranferGas(float waitTime)
          {
              yield return new WaitForSeconds(waitTime);
          }
      }

 

Ice


As an ice block you have no control over where you want to go. You are heavy and move purely based on momentum. 
Since water and gas intuitively is light in terms of weight we needed a state in which you could smash things

Breakable Walls Script

 

using UnityEngine;
      using System.Collections;

      public class BreakableWall : MonoBehaviour
      {
          private MORPH morphScript;
          private int forceVelocity = 15;

          void Start()
          {
              morphScript = GameObject.Find("MORPH").GetComponent();
          }

          void OnCollisionEnter2D(Collision2D other)
          {
              if (other.gameObject.name == "MORPH" && morphScript.phaseType == MORPH.PhaseType.iceCube && 
                                                                               other.relativeVelocity.magnitude > forceVelocity)
              {
                  Debug.Log(other.relativeVelocity.magnitude);
                  GameObject wallBreakParticles = Instantiate(Resources.Load("WallBreak"), transform.position, Quaternion.identity) 
                                                  as GameObject;
                  Destroy(wallBreakParticles, 10.0f);
                  GetComponent().enabled = false;
                  GetComponent<().enabled = false;
              }
          }
      }
 

Intractables

Pipes

Using an Area Effector 2D all movement is locked to the flow of the pipe and morph cannot switch phases into different forms of matter.

 

Vents

Vents can only be accessed by morph in gas form and will only reveal its exit location once Morph has entered it. The vents are not two way meaning Morph can only go through the entrance vent.

Water Wheels
 

Water wheels are used to activate a number of different interactions, most of which being moving platforms.  

Breakable Walls

Breakable walls are highlighted through a variation in color and can only be destroyed by the Ice phase and require a specific amount of velocity to be applied.

Hazards

Lava

Lava can instantly kill Morph in any state forcing him to spawn at hist starting position or the most recent checkpoint. while in water steam is created if droplets pour into the lava.

 

Spikes

Spikes are a hazard that only affects the ice phases. The logic behind this is that water and gas can seep through the crevices of the spikes.
 

Vaccums

Vacuums currently suck Morph into hazardous vents from any state. Later on a more appropriate mechanic would be to make the vents only suck Morph in the gas phase, if in the water phase it clogs the vent allowing Morph to phase into ice to drop down. 

Main Menu

I wanted the main menu to be fun and quirky like MORPH himself. I sketched out the functionality on my notebook and began implementing it. I also implemented a simple pause menu.
 

© Carl Vitasa, All rights reserved.