top of page

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

Gladiator Gore
Screen Shot 2022-09-26 at 12.14.37 PM.png

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; } } }

bottom of page