Enthusiastic, adaptable, productive software developer.
Algonquin College graduate of
Computer Engineering - Computing Science
One year in successful co-op work experience:
CSDC - (SQL) Data Migration
Genband - (Python) Disk Space Management
Genband - (HTML/CSS/JS) WebRTC Application
WindRiver - (Python) Web Automated Testing and Configuration
Hard working, passionate programmer and gamer.
Excellent communication skills and friendly team player.
I'm eager to join a software development team that allows me to use and extend my skills.
1. 2.5D RPG adventure
These Unity Projects were created with a mindset of a minimal viable product.
I took basic concepts and mechanics to create these projects below.
Here are the Player Controller classes from the 4 Unity projects above.
// Rigibody_Movement requires the GameObject to have a Rigidbody component
[RequireComponent (typeof (Rigidbody))]
/// summary
/// This class controls a players movement applying forces to a Rigidbody component
/// This class can be used for 3D 3rd person gamepad/controller based games
/// summary
public class Rigidbody_3rdPerson_Gamepad_Movement : MonoBehaviour {
Rigidbody rb; // Rigidbody component reference
Camera cam; // Camera component reference
Animator anim; // Animator component reference
// Private Serialized variables to allow tweaking in Unity inspector
[SerializeField] float rbDrag = 1f; // Rigidbody drag
[SerializeField] float speed = 25.0f; // Player speed
[SerializeField] float maxSpeed = 8.0f; // Player max speed
[SerializeField] float turnSpeed = 3.0f; // Player turn speed
// Private variables
float dampTime = 0.1f;
float controllerHorizontalCalibration = 0.4f;
float maxSpeedLTargetV = 8.0f;
float maxSpeedLTargetH = 7.5f;
bool attacking = false;
/// summary
/// Start is called first
/// The function initializes all variables and components
/// summary
void Start () {
rb = GetComponent Rigidbody ();
anim = GetComponent Animator();
rb.drag = rbDrag;
cam = Camera.main;
}
/// summary
/// FixedUpdate is called every physics step
/// The function gets input from the user and adds a force to the Rigidbody component
/// summary
void FixedUpdate () {
Vector3 forward;
if (attacking)
return;
if (ZeldaMechanics.lTargetEnabled) {
// Get input
float h = speed * Input.GetAxis ("JoystickXAxis");
float v = speed * Input.GetAxis ("JoystickYAxis");
forward = cam.transform.TransformDirection (Vector3.forward);
Vector3 right = cam.transform.TransformDirection (Vector3.right);
// Move a bit faster with weapon equipped
if (ZeldaMechanics.weaponEquiped) {
h = Mathf.Clamp (h, -maxSpeedLTargetH, maxSpeedLTargetH);
v = Mathf.Clamp (v, (-maxSpeedLTargetV), (maxSpeedLTargetV + 2));
} else {
h = Mathf.Clamp (h, -maxSpeedLTargetH, maxSpeedLTargetH);
v = Mathf.Clamp (v, -maxSpeedLTargetV, maxSpeedLTargetV);
}
// Small fix for controller calibration
if (Mathf.Abs (h) < controllerHorizontalCalibration)
h = 0;
// Play animations
anim.SetFloat ("HorizontalTL", h, dampTime, Time.deltaTime);
anim.SetFloat ("Forward", v, dampTime, Time.deltaTime);
if (h == 0 && v > h || v < 0) { // Vertical movement
rb.AddForce (forward * v); // Add vertical force
} else {
rb.AddForce (right * h); // Add horizontal force
}
} else {
// Get input
transform.Rotate (0f, Input.GetAxis ("Horizontal") * turnSpeed, 0f);
forward = transform.TransformDirection (Vector3.forward);
float verticalSpeed = speed * Input.GetAxis ("Vertical");
verticalSpeed = Mathf.Clamp (verticalSpeed, -maxSpeed, maxSpeed);
// Play animations
anim.SetFloat ("Forward", verticalSpeed, dampTime, Time.deltaTime);
anim.SetFloat ("HorizontalTL", 0f, dampTime, Time.deltaTime);
rb.AddForce (forward * verticalSpeed);
}
}
}
/// summary
/// Character controls.
/// This class controls the character checking grounded, movement, jumping, and bouncing.
/// summary
public class CharacterControls : MonoBehaviour {
// Public variables
public float speed = 10.0f;
public float gravity = 10.0f;
public float maxVelocityChange = 10.0f;
public bool canJump = true;
public float jumpHeight = 2.0f;
// Public static variables
public static bool grounded = false;
public static bool isBouncing = false;
public static bool onWall = false;
// Private variables
private Rigidbody rb;
private Camera cam;
private float distToGround;
/// summary
/// Awake is called first.
/// The function initializes all variables.
/// summary
void Awake () {
distToGround = GetComponent< Collider >().bounds.extents.y;
Cursor.visible = false;
cam = GetComponentInChildren< Camera > ();
rb = GetComponent< Rigidbody > ();
rb.freezeRotation = true;
rb.useGravity = false;
}
/// summary
/// Update is called every frame.
/// Checks for userinput 'c'.
/// summary
void Update () {
if (Input.GetButtonUp ("Checkpoint")) {
GameManager gm = GameObject.Find ("GameManager").GetComponent< GameManager > ();
gm.GotoCheckPoint ();
}
}
/// summary
/// FixedUpdate is called every physics step.
/// The function calculates the player velocity and applies the forces to the character.
/// If character is onWall, stop user input for velocity.
/// summary
void FixedUpdate () {
if (onWall) {
// Don't take user input
} else {
Vector3 velocityChange = CalculateTargetVelocity ();
rb.AddForce (velocityChange, ForceMode.VelocityChange);
}
if (IsGrounded ()) {
onWall = false;
// Jump
if (canJump && Input.GetButton ("Jump") && !isBouncing) {
rb.velocity = new Vector3 (rb.velocity.x, CalculateJumpVerticalSpeed (), rb.velocity.z);
}
}
// Apply gravity manually for more tuning control
rb.AddForce (new Vector3 (0, -gravity * rb.mass, 0));
grounded = false;
}
/// summary
/// Raises the collision enter event.
/// The function checks ground 'type'. Bounce platforms or the rest of the level.
/// summary
/// param name="other">Other.param
void OnCollisionEnter (Collision other) {
if (other.collider.CompareTag ("Untagged")) {
isBouncing = false;
}
}
/// summary
/// Determines whether this instance is grounded.
/// summary
/// returns true if this instance is grounded; otherwise, false
private bool IsGrounded() {
return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
}
/// summary
/// Calculates the jump vertical speed.
/// summary
/// returns The jump vertical speed
private float CalculateJumpVerticalSpeed () {
// From the jump height and gravity, deduce the upwards speed
// for the character to reach at the apex.
if (isBouncing)
return rb.velocity.y;
return Mathf.Sqrt(2 * jumpHeight * gravity);
}
/// summary
/// Raises the GU event.
/// Creates simple crosshair.
/// summary
void OnGUI(){
GUI.Box(new Rect(Screen.width/2,Screen.height/2, 10, 10), "");
}
/// summary
/// Calculates the target velocity.
/// summary
/// returns The target velocity
private Vector3 CalculateTargetVelocity(){
// Calculate how fast we should be moving
Vector3 targetVelocity = new Vector3 (Input.GetAxis ("Horizontal"), 0, Input.GetAxis ("Vertical")); // Get input
targetVelocity = cam.transform.TransformDirection (targetVelocity); // Which direction is the player (camera in this case) facing
targetVelocity *= speed;
// Apply a force that attempts to reach our target velocity
Vector3 velocity = rb.velocity; // current velocity
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp (velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp (velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
return velocityChange;
}
/// summary
/// Disables the control.
/// summary
public void DisableControl () {
rb.isKinematic = true;
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
/// summary
/// Enables the control.
/// summary
public void EnableControl () {
rb.isKinematic = false;
}
}
/// summary
/// Player control.
/// This class controls the character checking grounded, walking, running, and jumping.
/// summary
public class PlayerControl : MonoBehaviour
{
// Public Hidden variables
[HideInInspector] public bool facingRight = true;
[HideInInspector] public bool jump = false;
[HideInInspector] public bool run = false;
[HideInInspector] public GameObject instance;
// Public variables
public float moveForce = 365f;
public float maxSpeed = 5f;
public float runSpeed = 100f;
public float jumpForce = 1000f;
public Transform groundCheck;
public Text livesText;
// Public static variables
public static int lives = 3;
// Private variables
private bool grounded = false;
private Animator anim;
private Rigidbody2D rb2d;
private AudioSource audioPlayer;
private AudioClip[] audioPlayerclips;
/// summary
/// Awake is called first.
/// The function initializes all variables.
/// summary
void Awake () {
anim = GetComponent< Animator > ();
rb2d = GetComponent< Rigidbody2D > ();
if (lives == 1)
livesText.text = "Last Life!";
else
livesText.text = "Lives: " + lives;
audioPlayer = GetComponent< AudioSource > ();
audioPlayerclips = Resources.LoadAll < AudioClip > ("Sounds/Player");
}
/// summary
/// Update is called every frame.
/// Checks for grounded, run, and jump.
/// summary
void Update () {
grounded = Physics2D.Linecast (transform.position, groundCheck.position, 1 << LayerMask.NameToLayer ("Ground"));
if (Input.GetButtonDown ("Jump") && grounded) { // Must be grounded to jump
jump = true;
} else if (Input.GetButton ("Run") && grounded && Mathf.Abs(rb2d.velocity.x) > maxSpeed) { // Must be walking to run and also grounded
run = true;
}
}
/// summary
/// FixedUpdate is called every physics step.
/// The function calculates the player velocity and applies the forces to the character.
/// summary
void FixedUpdate () {
float h = Input.GetAxisRaw ("Horizontal");
if (h * rb2d.velocity.x < maxSpeed) {
rb2d.AddForce (Vector2.right * h * moveForce); // Not moving
}
if (Mathf.Abs (rb2d.velocity.x) > maxSpeed) {
anim.SetTrigger ("isWalking");
rb2d.velocity = new Vector2 (Mathf.Sign (rb2d.velocity.x) * maxSpeed, rb2d.velocity.y); // Moving
}
if (h > 0 && !facingRight) {
Flip ();
} else if (h < 0 && facingRight) {
Flip ();
}
if (jump) {
audioPlayer.clip = audioPlayerclips[0];
audioPlayer.Play();
anim.SetTrigger ("isJumping");
if (run)
rb2d.velocity += new Vector2 (Mathf.Sign (rb2d.velocity.x) * runSpeed, 5f); // Running Jump
rb2d.AddForce (new Vector2 (0f, jumpForce));
jump = false;
}
if (run) {
anim.SetTrigger ("isRunning");
rb2d.velocity += new Vector2 (Mathf.Sign (rb2d.velocity.x) * runSpeed, 0f); // Running
run = false;
}
}
/// summary
/// Flip the player sprite.
/// summary
void Flip () {
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
/// summary
/// Disables the control of the player.
/// summary
public void DisableControl () {
rb2d.velocity = Vector3.zero;
rb2d.angularVelocity = 0f;
rb2d.isKinematic = true;
}
/// summary
/// Enables the control of the player.
/// summary
public void EnableControl () {
rb2d.isKinematic = false;
}
/// summary
/// Reset player variables.
/// summary
public void Reset () {
lives = 3;
livesText.fontSize = 30;
livesText.alignment = TextAnchor.UpperCenter;
}
/// summary
/// Player is dead.
/// Disable all player controls, and edit text values.
/// summary
public void PlayerDead () {
DisableControl ();
anim.Stop ();
livesText.fontSize = 70;
livesText.alignment = TextAnchor.MiddleCenter;
livesText.text = "GAME OVER";
}
}
/// summary
/// Player.
/// This class controls the player's actions. Checking turn, attacking, and changing pokemon.
/// Both the player and NPC get 3 random pokemon from a list of 10.
/// summary
public class Player : MonoBehaviour {
// Public variables
public bool myTurn = false;
public Text PokemonName;
public Text PokemonHealthPoints;
public Text Turn;
// Private variables
private float healthPoints = 100f;
private float attack = 25f;
private int currentPokemon = 0;
private bool pokemonList = false;
private bool isPanelActive = false;
private bool isCoroutineRunning = false;
private bool isCoroutineRunningSpriteFlicker = false;
private ArrayList pokemon;
private Pokemon pk;
// Private game variables
private NPC npc;
private GameManager gm;
private Button buttonAttack;
private Button buttonPokemon;
private GameObject panel;
private Sprite[] playerSprites;
private SpriteRenderer sr;
private Slider slider;
private AudioSource audioPlayer;
private AudioClip[] audioPlayerclips;
/// summary
/// Start is called on first frame.
/// The function initializes all variables.
/// The function also adds two onclick listeners.
/// summary
void Start () {
npc = GameObject.Find ("NPC").GetComponent< NPC >(); // Instance of NPC script
gm = GameObject.Find ("GameManager").GetComponent< GameManager >(); // Instance of GameManager script
buttonAttack = GameObject.FindGameObjectWithTag("Player_Attack").GetComponent< Button >();
buttonPokemon = GameObject.FindGameObjectWithTag("Player_Pokemon").GetComponent< Button >();
panel = GameObject.FindGameObjectWithTag ("Panel");
sr = GetComponent< SpriteRenderer > ();
sr.transform.localPosition = new Vector3 (-3, -3, 0);
sr.transform.localScale = new Vector3 (5,5,0);
slider = GameObject.Find ("PlayerSlider").GetComponent< Slider > ();
audioPlayer = GetComponent< AudioSource > ();
Init ();
panel.SetActive (false);
buttonAttack.onClick.AddListener (delegate() {
Attack();
});
buttonPokemon.onClick.AddListener (delegate() {
ShowPokemon();
audioPlayer.clip = audioPlayerclips[0];
audioPlayer.Play();
});
}
/// summary
/// Update is called every frame.
/// Checks for gameover, all pokemon dead, and player's turn.
/// summary
void Update () {
Pokemon temp = (Pokemon)pokemon [currentPokemon];
if (gm.gameOver) {
DisableMenu ();
return;
}
else if (temp.HealthPoints == 0) {
// Switch to next pokemon with health != 0
AllPokemonDead();
ShowPokemon();
temp.HealthPoints--; // Stops spam of showing menus
} else {
if (myTurn) {
// Wait for button click Player_Attack
buttonAttack.enabled = true;
}
}
}
/// summary
/// Raises the GU event.
/// Creates player pokemon list.
/// summary
void OnGUI () {
if (pokemonList) {
// Loop to create buttons based on pokemon with HP left
float buttonShift = 0.10f;
for (int i = 0; i < pokemon.Count; i++) {
Pokemon p = (Pokemon)pokemon [i];
if (p.HealthPoints > 0) {
if (GUI.Button (new Rect (Screen.width* (0.45f), Screen.height* (0.6f + buttonShift), 200, 50), p.Name)) {
ChangePokemon (p, i);
buttonShift = 0.10f;
return;
}
buttonShift += 0.05f;
}
}
buttonShift = 0.10f;
myTurn = false;
}
}
/// summary
/// Init called inside Start() to create player's pokemon.
/// Init also sets all UI text for the player's first pokemon.
/// summary
private void Init () {
// Get sprites
playerSprites = Resources.LoadAll < Sprite > ("Sprites/pokePlayer");
// Get audioPlayer clips
audioPlayerclips = Resources.LoadAll < AudioClip > ("Sounds/Player");
pokemon = new ArrayList();
for (int i = 0; i < 3; i++) {
int randomIndex = Random.Range (1, 10);
// Choose pokemon name
Pokemon p = (Pokemon)gm.pokemon[randomIndex];
// Create new pokemon
switch(p.Type){
case "Starter": {
for (int j = 0; j < playerSprites.Length; j++) {
if (playerSprites [j].name.Contains (p.Name.ToLower())) {
pokemon.Add (new Pokemon (p.Name, 250, 50, p.Type, playerSprites[j]));
break;
}
}
break;
}
case "Basic": {
for (int j = 0; j < playerSprites.Length; j++) {
if (playerSprites [j].name.Contains (p.Name.ToLower())) {
pokemon.Add (new Pokemon (p.Name, 100, 25, p.Type, playerSprites[j]));
break;
}
}
break;
}
case "Useless": {
for (int j = 0; j < playerSprites.Length; j++) {
if (playerSprites [j].name.Contains (p.Name.ToLower())) {
pokemon.Add (new Pokemon (p.Name, 100, 0, p.Type, playerSprites[j]));
break;
}
}
break;
}
default: {
break;
}
}
}
// Assign attack and hp from first pokemon
Pokemon temp = (Pokemon)pokemon [0];
healthPoints = temp.HealthPoints;
attack = temp.AttackDamage;
// Set sprite
sr.sprite = temp.Image;
// Set health slider
switch (temp.Type) {
case "Starter":
{ slider.maxValue = Pokemon.STARTERHP; break; }
case "Basic":
{ slider.maxValue = Pokemon.BASICHP; break; }
case "Useless":
{ slider.maxValue = Pokemon.USELESSHP; break; }
}
slider.value = healthPoints;
Turn.text = "Player's TURN NOW";
PokemonName.text = temp.Name;
PokemonHealthPoints.text = "HP: " + temp.HealthPoints;
}
/// summary
/// Shows the pokemon panel.
/// summary
private void ShowPokemon () {
// Enable panel
if (isPanelActive) {
panel.SetActive (false);
isPanelActive = false;
pokemonList = false;
} else {
isPanelActive = true;
panel.SetActive (true);
pokemonList = true;
}
}
/// summary
/// Checks if all pokemon are dead.
/// summary
private void AllPokemonDead () {
int count = 0;
for (int i = 0; i < pokemon.Count; i++) {
Pokemon p = (Pokemon)pokemon [i];
if (p.HealthPoints > 0) {
// Found Pokemon with health
// Update values
healthPoints = p.HealthPoints;
attack = p.AttackDamage;
// Update text on screen
PokemonName.text = p.Name;
PokemonHealthPoints.text = "HP: " + p.HealthPoints;
// Change index to current Pokemon
currentPokemon = i;
break;
}
// Count pokemon with 0 hp
if (p.HealthPoints <= 0) {
count++;
// All pokemon have 0 hp
if (count == pokemon.Count)
gm.GameOver ();
}
}
}
/// summary
/// Disables the menu.
/// summary
private void DisableMenu () {
buttonAttack.enabled = false;
buttonPokemon.enabled = false;
}
/// summary
/// Changes the pokemon.
/// Changes all UI text to changed pokemon.
/// summary
/// param name="p"
/// param name="index"
private void ChangePokemon (Pokemon p, int index) {
audioPlayer.clip = audioPlayerclips[0];
audioPlayer.Play();
// Update values
healthPoints = p.HealthPoints;
attack = p.AttackDamage;
// Set sprite
sr.sprite = p.Image;
// Set health slider
switch (p.Type) {
case "Starter":
{ slider.maxValue = Pokemon.STARTERHP; break; }
case "Basic":
{ slider.maxValue = Pokemon.BASICHP; break; }
case "Useless":
{ slider.maxValue = Pokemon.USELESSHP; break; }
}
slider.value = healthPoints;
// Update text on screen
PokemonName.text = p.Name;
PokemonHealthPoints.text = "HP: " + p.HealthPoints;
// Change index to current Pokemon
currentPokemon = index;
// Hide Pokemon panel
panel.SetActive (false);
isPanelActive = false;
pokemonList = false;
Turn.text = "NPC's TURN NOW";
myTurn = false;
npc.myTurn = true;
buttonAttack.enabled = false;
}
/// summary
/// Attack NPC pokemon.
/// summary
private void Attack () {
audioPlayer.clip = audioPlayerclips[1];
audioPlayer.Play ();
// Pokemon attack animation
sr.transform.localPosition += new Vector3 (0.5f, 0, 0);
StartCoroutine(ResetSprite());
npc.TakeDamage (attack);
myTurn = false;
buttonAttack.enabled = false;
Turn.text = "NPC's TURN NOW";
npc.myTurn = true;
}
/// summary
/// Resets the sprite.
/// summary
/// returns The sprite
IEnumerator ResetSprite(){
if (isCoroutineRunning)
yield break;
isCoroutineRunning = true;
yield return new WaitForSeconds (0.15f);
sr.transform.localPosition -= new Vector3 (0.5f, 0, 0);
isCoroutineRunning = false;
}
/// summary
/// Take damage from NPC pokemon.
/// summary
/// param name="damage"
public void TakeDamage (float damage) {
Pokemon p = (Pokemon)pokemon [currentPokemon];
p.HealthPoints -= damage;
if (p.HealthPoints < 0)
p.HealthPoints = 0;
PokemonHealthPoints.text = "HP: " + p.HealthPoints;
slider.value = p.HealthPoints;
StartCoroutine(Spriteflicker());
buttonAttack.enabled = true;
}
/// summary
/// Spriteflicker when taking damage.
/// summary
IEnumerator Spriteflicker(){
if (isCoroutineRunningSpriteFlicker)
yield break;
isCoroutineRunningSpriteFlicker = true;
sr.enabled = true;
yield return new WaitForSeconds (0.5f);
sr.enabled = false;
yield return new WaitForSeconds (0.1f);
sr.enabled = true;
yield return new WaitForSeconds (0.1f);
sr.enabled = false;
yield return new WaitForSeconds (0.1f);
sr.enabled = true;
isCoroutineRunningSpriteFlicker = false;
}
}
If employment opportunities arise please contact me at your convenience @ joeyanbarber@gmail.com