Gladiator Gore
ROLE
Project Manager, Lead Designer, and Programmer
DESCRIPTION
This first person onslaught game pins the player against waves of gladitorial skeletons. Given various weapons such as daggers, crossbows, and bombs, the player must vanquish the undead army, collect power-ups, and conquer the colosseum .
PROJECT TIMELINE
Mar 2022 - May 2022
GENRE
Round-Based Onslaught Survival
PLATFORM
PC
DEVELOPMENT
Unity - C#

TAKEAWAYS
Developing this game exposed me to multiple challenges that cultivated my skills of enemy AI, player experience, and project management. As such I grew as a programmer, designer, and leader.
PROGRAMMING
In programming the behavior for the enemies I learned a great deal about the complexity of how the state machine must work with the associated coroutines and threads and other systems to popperly function.
DESIGNING
In working on this project I learned, not just how to make a good game, but what makes a good game. I thought alot about how the player will play the game and what controls and weapons will feel the most natural. Additionally, I discovered that AI must be carefully tailored to immerse the player in a fluid and dynamic combat.
LEADING
Operating as project manager, lead designer, and programmer gave me a deep connection to the team and the project. I developed management and communication skills that helped my team members excel at their assigned tasked and create a terrific game.
THE GAME

Grand and Immersive Arena
Detailed and Engaging Enemies


Diverse and Fun Weaponry
MY CODE
ENEMY BASE
PLAYER CONTROLLER
MELEE WEAPON
PROJECTILE WEAPON
Downloads
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; enum AI_States { HUNT_STATE, GOver_STATE, HIT_STATE, ATTACK_STATE, DEAD_STATE } public class enemyBase : MonoBehaviour { private Player_Equiped playerReference; private spawnerController spawnController; [SerializeField] private GameObject spawnControllerObject; [SerializeField] private AI_States state = AI_States.HUNT_STATE; [SerializeField] private Transform selfLocation; [SerializeField] private Transform playerTrans; [Header("Enemy")] [SerializeField] float enemyHealth = 100.0f; [SerializeField] float enemyCurrHealth = 100.0f; [SerializeField] float enemyDamage = 10.0f; [SerializeField] float enemyKnockbackPower = 3; [SerializeField] float attackRange = 5.0f; [SerializeField] float attackCooldown = 3.0f; [SerializeField] float enemySpeed = 3.5f; [SerializeField] GameObject enemyAttackTrigger; [Header("Power-Ups")] [SerializeField] private GameObject healthPU; [SerializeField] private GameObject armorPU; [SerializeField] private GameObject speedPU; [SerializeField] private GameObject meleePU; [SerializeField] private GameObject rangedPU; bool attacking = false; //private Transform moveTarget; private moveTo steering; private float distanceToTarget; private Rigidbody rb; private Animator animator; private bool isDead; // Start is called before the first frame update void Start() { gameObject.layer = LayerMask.NameToLayer("Enemies"); spawnControllerObject = GameObject.FindWithTag("GameController"); NavMeshAgent agent = GetComponent(); agent.speed = enemySpeed; spawnController = spawnControllerObject.GetComponent(); playerReference = GameObject.FindWithTag("Player").GetComponent(); playerTrans = playerReference.gameObject.transform; rb = gameObject.GetComponent(); animator = gameObject.GetComponent(); animator.SetBool("isDead", false); selfLocation = gameObject.transform; isDead = false; steering = GetComponent(); } // Update is called once per frame void Update() { //selfLocation = gameObject.transform; distanceToTarget = Vector3.Distance(transform.position, playerTrans.position); switch (state) { case AI_States.HUNT_STATE: doHunt(); break; case AI_States.GOver_STATE: doGOver(); break; case AI_States.HIT_STATE: doHit(); break; case AI_States.ATTACK_STATE: doAttack(); break; case AI_States.DEAD_STATE: doDead(); break; } //print("State: " + state); } //-----------STATE FUNCTIONS------------ private void doHunt() { if (!isDead) { steering.setTarget(playerTrans); //print("Dist to Target: " + distanceToTarget); animator.SetBool("isWalking", true); animator.SetBool("isAttacking", false); if (distanceToTarget = attackRange) { //state = AI_States.HUNT_STATE; StopCoroutine(enemyAttacking()); } if (isDead) { state = AI_States.DEAD_STATE; } if (playerReference.playerDead) { state = AI_States.GOver_STATE; }//else hunt down the player } private void doAttack() { steering.setTarget(selfLocation); animator.SetBool("isAttacking", true); if (!attacking) { print("Enemying is Attacking"); StartCoroutine(enemyAttacking()); } if (distanceToTarget > attackRange && !isDead) { StopCoroutine(enemyAttacking()); animator.SetBool("isAttacking", false); state = AI_States.HUNT_STATE; } if (playerReference.playerDead) { state = AI_States.GOver_STATE; }//else hunt down the player } //end doAttack(); private void doDead() { steering.setTarget(selfLocation); } //----------HELPER FUNCTIONS------------- public void enemyHit(float knockback, float damage) { if (enemyCurrHealth > 0) { StopCoroutine(enemyAttacking()); attacking = false; animator.SetTrigger("isHit"); state = AI_States.HIT_STATE; StartCoroutine(enemyHitCo()); enemyCurrHealth -= damage; rb.AddForce(-1 * transform.forward * knockback, ForceMode.Impulse); if (enemyCurrHealth 6) { print("Item has been dropped"); var position = new Vector3(transform.position.x, 0.25f, transform.position.z); float chancePU = Random.Range(1, 5); GameObject itemPref = null; switch (chancePU) { case 1: itemPref = healthPU; break; case 2: itemPref = armorPU; break; case 3: itemPref = speedPU; break; case 4: itemPref = meleePU; break; case 5: itemPref = rangedPU; break; } Instantiate(itemPref, position, Quaternion.identity); } } //---------------CoRountines------------ IEnumerator enemyHitCo() { //play hit animation yield return new WaitForSeconds(1); // wait for hit animation to finish //Stop the Agent to prevent recoil state = AI_States.HUNT_STATE; } IEnumerator enemyAttacking() { attacking = true; //play attack animation yield return new WaitForSeconds(0.5f); //playerReference.playerDamaged(enemyDamage); GameObject eAT = Instantiate(enemyAttackTrigger, gameObject.transform); print("Enemy Attacking With " + enemyDamage + " damage and " + enemyKnockbackPower + " Knockback"); eAT.GetComponent().setEffects(enemyDamage, enemyKnockbackPower); eAT.transform.position = gameObject.transform.position; yield return new WaitForSeconds(1); attacking = false; } IEnumerator enemyDeath() { yield return new WaitForSeconds(4); dropChance(); Destroy(gameObject); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; enum Weapon_Equip {Dagger, Sword, Crossbow, Bow, GreekFire}; public class Player_Equiped : MonoBehaviour { /* * * List of Public Functions to Call * - changeMaxHealth * - changeCurrHealth * - changeArmor * - playerDamaged * - playerHeal * * - changeSpeed * * - changeMeleeDamageFactor * - changeRangeDamageFactor * */ [Header("Player Stats")] [SerializeField] private float maxHealth = 100; //maximum health [SerializeField] private float currHealth = 100; //current health [SerializeField] public float meleeDamageFactor = 1; //melee multiplier [SerializeField] public float rangedDamageFactor = 1; //ranged multiplier [SerializeField] private float armor = 0; //% of reduced damage [Header("Weapons")] //Serialized Weapon Game Objects [SerializeField] private GameObject hand; [SerializeField] private GameObject dagger; [SerializeField] private GameObject sword; [SerializeField] private GameObject crossBow; [SerializeField] private GameObject bow; [SerializeField] private GameObject greekFire; [Header("Screens")] // Accesss Screens [SerializeField] private GameObject GameOverCanvas; [SerializeField] private HealthBar healthBar; //Weapon variables (for swapping) private Weapon_Equip weaponEquiped; //Enum representation of weapon private GameObject weapon; //Weapon player is using private GameObject wepPrefab;//Prefab of weapon to create //for movement purposes private FirstPersonMovement firstPersonMovement; //used for speed management //attacking varibales private bool canFire = true; private float dur = 0; private Animator handAnimator = null; //is the player dead public bool playerDead = false; // Start is called before the first frame update void Start() { firstPersonMovement = gameObject.GetComponent(); GameObject firstDagger = Instantiate(dagger, hand.transform); weaponEquiped = Weapon_Equip.Dagger; weapon = firstDagger; weapon.transform.parent = hand.transform; GameOverCanvas.SetActive(false); } // Update is called once per frame void Update() { handleWeaponSwap(); handleAttack(); } //----------------------PRIVATE FUNCTIONS---------------------- //-----------Weapon Management----------- private void handleWeaponSwap() { bool swapped = false; //swap weapon left if (Input.GetKeyDown(KeyCode.Q)) { swapped = true; switch (weaponEquiped) { case Weapon_Equip.Dagger: weaponEquiped = Weapon_Equip.GreekFire; break; case Weapon_Equip.Sword: weaponEquiped = Weapon_Equip.Dagger; break; case Weapon_Equip.Crossbow: weaponEquiped = Weapon_Equip.Sword; break; case Weapon_Equip.Bow: weaponEquiped = Weapon_Equip.Crossbow; break; case Weapon_Equip.GreekFire: weaponEquiped = Weapon_Equip.Bow; break; }//end switch } else if (Input.GetKeyDown(KeyCode.E)) { swapped = true; //swap weapons right switch (weaponEquiped) { case Weapon_Equip.Dagger: weaponEquiped = Weapon_Equip.Sword; break; case Weapon_Equip.Sword: weaponEquiped = Weapon_Equip.Crossbow; break; case Weapon_Equip.Crossbow: weaponEquiped = Weapon_Equip.Bow; break; case Weapon_Equip.Bow: weaponEquiped = Weapon_Equip.GreekFire; break; case Weapon_Equip.GreekFire: weaponEquiped = Weapon_Equip.Dagger; break; }//end switch } if (swapped) { switch (weaponEquiped) { case Weapon_Equip.Dagger: wepPrefab = dagger; break; case Weapon_Equip.Sword: wepPrefab = sword; break; case Weapon_Equip.Crossbow: wepPrefab = crossBow; break; case Weapon_Equip.Bow: wepPrefab = bow; break; case Weapon_Equip.GreekFire: wepPrefab = greekFire; break; } while (hand.gameObject.transform.childCount > 0) { GameObject removed = hand.gameObject.transform.GetChild(0).gameObject; removed.transform.parent = null; Destroy(removed); } weapon = Instantiate(wepPrefab, hand.transform); weapon.transform.parent = hand.transform; } } private void handleAttack() { if (Input.GetButtonDown("Fire1") && canFire) { ProjectileWeapon projectileWeapon = weapon.GetComponent(); if (projectileWeapon != null) { projectileWeapon.projectileFired(); dur = projectileWeapon.getRechangeTime(); } MeleeWeapon meleeWeapon = weapon.GetComponent(); if (meleeWeapon != null) { meleeWeapon.meleeFired(); dur = meleeWeapon.getRechargeTime(); } canFire = false; StartCoroutine(resetCanFire()); print("Dur = " + dur); } } private void playerDeath() { GameOverCanvas.SetActive(true); playerDead = true; } //----------------------PUBLIC FUNCTIONS---------------------- //-----------Health / Armor Functions----------- public void changeMaxHealth(float delta) { maxHealth += delta; healthBar.setMaxHealth(maxHealth); healthBar.setHealth(currHealth); } public void changeCurrHealth(float delta) { currHealth += delta; healthBar.setHealth(currHealth); } public void changeArmor(float delta) { armor += delta; } public void playerDamaged(float damage, float knockbackPower) { currHealth -= damage * ((100.0f - armor) / 100.0f); gameObject.GetComponent().AddForce(-1 * knockbackPower * transform.forward, ForceMode.Impulse); healthBar.setHealth(currHealth); if (currHealth maxHealth) { currHealth = maxHealth; } healthBar.setHealth(currHealth); } //-----------Speed Functions----------- public void changeSpeed(float delta) { firstPersonMovement.buffSpeed(delta); } //-----------Factor Buffs----------- public void changeMeleeDamageFactor(float delta) { meleeDamageFactor += delta; } public void changeRangedDamageFactor(float delta) { rangedDamageFactor += delta; } public void pickedUp(float healthBoost, float speedBoost, float armorBoost, float meleeBoost, float rangedBoost) { StartCoroutine(pickedUpCo(healthBoost, speedBoost, armorBoost, meleeBoost, rangedBoost)); } //-----------Corountines----------- IEnumerator resetCanFire() { yield return new WaitForSeconds(dur); canFire = true; } IEnumerator pickedUpCo(float healthBoost, float speedBoost, float armorBoost, float meleeBoost, float rangedBoost) { print("Picked Up Coroutine Called"); healPlayer(healthBoost); changeSpeed(speedBoost); changeArmor(armorBoost); changeMeleeDamageFactor(meleeBoost); changeRangedDamageFactor(rangedBoost); //ITEM PICK UP CODE GOES HERE yield return new WaitForSeconds(10); changeSpeed(-1 * speedBoost); changeArmor(-1 * armorBoost); changeMeleeDamageFactor(-1 * meleeBoost); changeRangedDamageFactor(-1 * rangedBoost); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MeleeWeapon : MonoBehaviour { [Header("Melee Weapon")] private Player_Equiped player; [SerializeField] private GameObject triggerBox; [SerializeField] private Transform spawnPoint; [SerializeField] private bool semiauto; [SerializeField] private string animationTitle; [Header("Weapon Info")] [SerializeField] private float rechargeTime; [SerializeField] private AudioSource audio; private void Start() { } public void meleeFired() { if (audio != null) { audio.Play(); } player = GameObject.FindWithTag("Player").GetComponent(); GameObject box = Instantiate(triggerBox, spawnPoint.position, Quaternion.identity); box.transform.position = spawnPoint.position; box.GetComponentInChildren().updateDamage(player.rangedDamageFactor); gameObject.GetComponent().Play(animationTitle); } public float getRechargeTime() { if (!semiauto) { return rechargeTime; } else { return 0.0f; } } IEnumerator resetBoolAfterDuration(bool toReset, float duration) { yield return new WaitForSeconds(duration); toReset = true; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ProjectileWeapon : MonoBehaviour { [Header("Projectile Weapon")] private Player_Equiped player; [SerializeField] private GameObject projectile; [SerializeField] private Transform launchPoint; [SerializeField] private bool semiauto; [Header("Fire Info")] [SerializeField] private float rechargeTime; [SerializeField] private float power; [SerializeField] private AudioSource audio; public void projectileFired() { if (audio != null) { audio.Play(); } player = GameObject.FindWithTag("Player").GetComponent(); GameObject proj = Instantiate(projectile, launchPoint.localPosition, launchPoint.rotation); proj.transform.position = launchPoint.position; proj.GetComponent().updateDamage(player.rangedDamageFactor); proj.GetComponent().AddForce(proj.transform.forward * power, ForceMode.Impulse); } public float getRechangeTime() { if (!semiauto) { return rechargeTime; } else { return 0.0f; } } }
