Medusa's Last Stand

Unity

Project Overview

  • Project Type: Game Jam Game
  • Team Size: 3
  • Role: Gameplay Programmer & UI Programmer
  • Software: Unity & VS Code
  • Languages: C#
  • Download link: Play Game
  • Programming Reel: Watch

Project Brief

Medusa's Last Stand is a top-down 2D action shoot 'em up where you play as Medusa shooting snakes, venom and petrifying the cyber greek soldiers that captured you. Won Best Design Game Jam Award.


Responsibilities/Achievements

  • - Create a weapon system shared by the player and enemies
  • - Built a reusable UI system
  • - Store high scores with user preferences
  • - Use post processing effects when firing and overheating the player's weapon
  • - Implement some of the particles in the game

Code Samples


/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/

public abstract class Weapon : MonoBehaviour, IAttack
{
    [SerializeField]
    protected Transform origin;
    
    [SerializeField]
    private float fireRate;
    
    private float deltaFire;

    [SerializeField, Range(0,1f)]
    private float cookOffStep = 0.1f;
    
    private void Update()
    {
        deltaFire += Time.deltaTime;
    }

    public float Attack(Quaternion direction)
    {
        if(deltaFire > fireRate)
        {
            WeaponAttack(direction);
            deltaFire = 0f;
            return cookOffStep;
        }
        return 0;
    }
    
    protected abstract void WeaponAttack(Quaternion direction);
}

// Example with a close range weapon
public class CloseRangeWeapon : Weapon
{
    [SerializeField]
    private float damage = 5f;

	[SerializeField]
    private GameObject hitBox;

    protected override void WeaponAttack(Quaternion direction)
    {
        GameObject newGO = Instantiate(hitBox, origin.position, direction);
        newGO.GetComponent<HitBox>().damage = damage;
    }

    public float GetDamage()
    {
        return damage;
    }
}

// Example with a ranged weapon
public class MachineGun : Weapon 
{
    [SerializeField]
    private GameObject bullet;
    
	protected override void WeaponAttack(Quaternion direction)
    {
        Instantiate(bullet, origin.position, direction);
        AkSoundEngine.PostEvent("Play_MachineGun", gameObject);
    }
}
										

/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/

// Base UI class
public abstract class UIScreen : MonoBehaviour 
{
    private void OnEnable()
    {
        UIScreenEnabled();	
    }

    private void OnDisable()
    {
        UIScreenDisabled();
    }
    
    protected abstract void UIScreenEnabled();
    protected abstract void UIScreenDisabled();
}

// Example Game Screen Usage
public class GameScreen : UIScreen 
{
    [Header("Kills & Score")] 
    [SerializeField]
    private Text killText;
    [SerializeField]
    private Text scoreText;

    [Header("Petrify Counter")]
    [SerializeField]
    private GameObject petrifyPowerIcon;
    [SerializeField]
    private GameObject petrifyContainer;

    [Space]
    [SerializeField]
    private GameObject healthBar;
    [SerializeField]
    private Color fullhealth;
    [SerializeField]
    private Color lowhealth;
    

    #region Screen Life Cycle

        protected override void UIScreenEnabled() 
        {
            Cursor.visible = false;
            AudioManager.Instance.PlayEvent("Stop_Music_Loop_Menu");
        }
        
        protected override void UIScreenDisabled()
        {
            Cursor.visible = true;
        }

    #endregion

    private void Update()
    {

        if((Input.GetKeyDown(KeyCode.Escape)))
		{
            UIManager.Instance.ShowScreen<PauseScreen>();
		}

        if(GameManager.Instance.IsGameOver())
        {
            StartCoroutine(ShowGameOverScreen());
        }
    }

    public void SetPowerIcons(int powersLeft)
    {
        foreach (Transform child in petrifyContainer.transform) 
        {
            GameObject.Destroy(child.gameObject);
        }

        for(int i = 0; i < powersLeft; i++)
        {
            GameObject newIcon = Instantiate(petrifyPowerIcon, Vector3.zero, Quaternion.identity);
            newIcon.transform.SetParent(petrifyContainer.transform);
            newIcon.transform.localScale = Vector3.one;
        }
    }

    public void SetKills(int count)
    {
        killText.text = "Kills: " + count;
    }
    
    public void SetScore(int count)
    {
        scoreText.text = "Score: " + count;
    }

    public void SetHealth(float health)
    {
        Vector3 newScale = healthBar.transform.localScale;
        newScale.x = health / 100f;
        healthBar.transform.localScale = newScale;
        healthBar.GetComponent<Image>().color = Color.Lerp(lowhealth, fullhealth, newScale.x);
    }

    IEnumerator ShowGameOverScreen()
    {
        yield return new WaitForSeconds(0.5f);
        UIManager.Instance.ShowScreen<GameOverScreen>();
        yield return null;
    }

}