Unity Capstone Project

 





About the Project

This project was a three month capstone for a Game Development class.

I chose to make an action-rpg game similar to Diablo and Darkstone.

The completed project included the ability to move by clicking a point in the 3D game world, attack enemies, and explore a small dungeon with a final boss.

 

Code Samples

public class ClickToMove : MonoBehaviour {

	public float speed;
	public CharacterController controller;

	private Vector3 position;
	public Vector3 curPosition;

	public AnimationClip animationIdle;
	public AnimationClip animationRun;
	
	public static bool attack;
	public static bool die;
	public bool running;

	public PlayerCombat player;
	public EnemyCombat enemy;
	public NPC NPC;

	void Start () {
		position = transform.position;
		curPosition = transform.position;
	} 

	void Update () {
		curPosition = new Vector3(transform.position.x, transform.position.y, transform.position.z);

		if(player.opponent != null) {
			enemy = player.opponent.GetComponent();
		} else {
			enemy = null;
		}

		if (Input.GetMouseButton(0)) {
			locatePosition();
		}

		if (!attack && !die) {
			if (enemy == null) {
				moveToPosition();
			} else {
				moveToTarget();
			}

		} else {}

		getTarget();
	}

	void locatePosition() {
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		RaycastHit hit;

		if (Physics.Raycast (ray, out hit, 100)) {
			if(hit.collider.tag!="Player") {
				position = hit.point;
			}
		}
	}

	void OnCollisionEnter(Collision col){
		if (col.gameObject.name == "cube") {
			Debug.Log ("Collision on " + gameObject.name);
		}

	}

	void moveToPosition() {
		if (Vector3.Distance (transform.position, position) > 0.60) {
			Quaternion newRotation = Quaternion.LookRotation (position - transform.position);

			newRotation.x = 0f;
			newRotation.z = 0f;

			transform.rotation = Quaternion.Slerp (transform.rotation, newRotation, Time.deltaTime * 100);
			controller.SimpleMove (transform.forward * speed);

			animation.CrossFade(animationRun.name);
		} else {
			animation.CrossFade(animationIdle.name);
		}
	}

	void moveToTarget() {
		if (Vector3.Distance (transform.position, enemy.transform.position) > 1.00) {
			Quaternion newRotation = Quaternion.LookRotation (enemy.transform.position - transform.position);
			
			newRotation.x = 0f;
			newRotation.z = 0f;
			
			transform.rotation = Quaternion.Slerp (transform.rotation, newRotation, Time.deltaTime * 100);
			controller.SimpleMove(transform.forward * speed);
			
			animation.CrossFade(animationRun.name);

			position = curPosition;
		} else {
			animation.CrossFade(animationIdle.name);
		}
	}

	void moveToNPC() {

	}

	void getTarget() {
		if( Input.GetMouseButtonDown(0)) {
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
			RaycastHit hit;
			
			if(Physics.Raycast( ray, out hit, 100)) {
				Debug.Log(hit.transform.gameObject.name);

				if (hit.transform.gameObject.tag == "Environment" || hit.transform.gameObject.tag == "Player" || hit.transform.gameObject.tag == "NPC") {
						player.GetComponent().opponent = null;
						attack = false;
				} else {
					player.GetComponent().opponent = hit.transform.gameObject;
				}
			}
		}
	}
}
public class PlayerCombat : MonoBehaviour {

	public GameObject opponent;

	public AnimationClip animationAttack;
	public AnimationClip animationDie;

	public double maxHealth;
	public double curHealth;
	public double damage;

	public Vector3 currentPosition;

	private double impactLength;

	public double impactTime;
	public bool impacted;

	public float range;

	bool started;
	bool ended;

	void Start () {
		curHealth = maxHealth;
		impactLength = (animation[animationAttack.name].length * impactTime);
	}

	void Update () {
		currentPosition = new Vector3(transform.position.x, transform.position.y, transform.position.z);

		if (opponent != null && inRange()) {
			animation.Play(animationAttack.name);
			ClickToMove.attack = true;

			if (opponent != null) {
				transform.LookAt(opponent.transform.position);
			}
		} else {
			transform.position = currentPosition;
			impacted = false;
		}

		if (animation[animationAttack.name].time > 0.9 * animation [animationAttack.name].length) {
			impacted = false;
		}

		impact();
		enemyDie();
	}

	void impact() {
		if (opponent != null && animation.IsPlaying(animationAttack.name) && !impacted) {
			if ((animation[animationAttack.name].time > impactLength && animation[animationAttack.name].time < 0.9 * animation[animationAttack.name].length)) {
				opponent.GetComponent().takeDamage(damage);
				impacted = true;
			}
		}
	}

	public void takeDamage(double damage) {
		curHealth -= damage;

		if (curHealth < 0) {
			curHealth = 0;
		}
	}

	bool inRange() {
		if (Vector3.Distance(opponent.transform.position, transform.position) <= range) {
			return true;
		} else {
			return false;
		}
	}

	public bool isDead() {
		if (curHealth == 0) {
			return true;
		} else {
			return false;
		}
	}

	void enemyDie() {
		if (isDead() && !ended) {
			if (!started) {
				ClickToMove.die = true;
				animation.CrossFade(animationDie.name);
				started = true;
			}

			if (started && !animation.IsPlaying(animationDie.name)) {
				Debug.Log ("You have died");
				curHealth = 100;

				ended = true;
				started = false;
				ClickToMove.die = false;
			}
		}
	}
}
public class EnemyCombat : MonoBehaviour {
	
	public float attackRange;
	public float aggroRange;
	
	private float maxDistanceToHomePosition = 0.6f;
	
	public Vector3 homePosition;
	public Vector3 homeRotation;
	
	public CharacterController controller;
	public Transform player;
	
	private PlayerCombat opponent;
	public PlayerLevel playerExp;
	public EnemyStats enemyStats;

	public AnimationClip animationIdle;
	public AnimationClip animationRun;
	public AnimationClip animationAttack;
	public AnimationClip animationDie;
	
	public double impactTime = 0.036;
	public bool impacted;

	public float expGiven;
	
	void Start () {
		opponent = player.GetComponent();
		homePosition = new Vector3(transform.position.x, transform.position.y, transform.position.z);
		homeRotation = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
	}
	
	void Update () {
		if (!isDead()) {
			if (inAggroRange()) {
				if (!inAttackRange()) {
					chase();
				} else {
					animation.CrossFade(animationAttack.name);
					attack();
					
					if (animation[animationAttack.name].time > 0.9 * animation[animationAttack.name].length) {
						impacted = false;
					}
				}
			} else if (!inAggroRange()){
				//Go back to home position
				if ((transform.position - homePosition).magnitude > maxDistanceToHomePosition) {
					animation.CrossFade(animationRun.name);
					transform.LookAt(homePosition);
					controller.SimpleMove(transform.forward * enemyStats.speed);
				} else {
					//Enemy is at home position
					animation.CrossFade(animationIdle.name);
					transform.eulerAngles = homeRotation;
				}
			}
		} else {
			dieMethod();
		}
	}
	
	void attack() {
		if (animation[animationAttack.name].time > animation[animationAttack.name].length * impactTime && !impacted && animation[animationAttack.name].time < 0.9 * animation[animationAttack.name].length) {
			opponent.takeDamage(enemyStats.attack);
			impacted = true;
		}
	}
	
	public bool inAggroRange() {
		if (Vector3.Distance (transform.position, player.position) < aggroRange) {
			return true;
		} else {
			return false;
		}
	}
	
	public bool inAttackRange() {
		if (Vector3.Distance (transform.position, player.position) < attackRange) {
			return true;
		} else {
			return false;
		}
	}
	
	public void takeDamage(double damage) {
		enemyStats.curHealth -= damage;
		
		if (enemyStats.curHealth <= 0) {
			playerExp.GetComponent().exp += expGiven * playerExp.GetComponent().bonusExpModifier;
			enemyStats.curHealth = 0;
		}
	}
	
	public void chase() {
		transform.LookAt(player.position);
		controller.SimpleMove(transform.forward * enemyStats.speed);
		animation.CrossFade(animationRun.name);
	}
	
	public void dieMethod() {
		animation.CrossFade (animationDie.name);
		player.GetComponent().opponent = null;
		
		if (animation[animationDie.name].time > animation[animationDie.name].length * 0.9) {
			gameObject.SetActive(false);
			//Respawn();
		}

		ClickToMove.attack = false;
	}
	
	public bool isDead() {
		if (enemyStats.curHealth <= 0) {
			return true;
		} else {
			return false;
		} 
	}
}

-- Most of this code is depreciated from older versions of Unity. Code is not commented.

Features

  • Click to Move Using Raycasting
  • Player & Enemy Floating Health Bars
  • Environment Interactability (Portals, Doors, Items)
  • Minor Looting System (Enemies would drop select items based on a RNG system)
 

Technologies Used

Unity 3D C#