Project Overview

Fennec Peak is a local multiplayer arena fighting game. It was created during a seven week project with a team of twelve using Unity. The goal of the game is to be the last player alive, to achieve this you need to knock the other players off the map using abilites and objects in the enviroment.

Game Info

Roles: Game Programmer

Time: 7 Weeks

Date: May 2019 - June 2019

Team Size: 12 (3 Programmers)

Genre: Party Arena

Engine: Unity

Version Control: Perforce

Code Language: C#

My Role

I worked on the movement and combat system for the characters together with the designers. But also some gameplay elements such as the power-up and exploding barrel.

The Combat System

The game centers around fluid combat and responsive movement, so getting the feel just right was essential. Initially, we experimented with colliders and triggers for knockback, but we quickly realized that this approach did not deliver the desired experience. To enhance the combat feel, we shifted to using raycasts in an arc to simulate the swinging of the player's weapon.

However, we encountered a challenge with the raycast method: the rays originated from the character's center and extended straight outward. This became problematic when we added height variations, jumping mechanics, and smaller objects into the game.

To improve hit detection, we implemented three ray arcs at different starting heights on the character, one from the head, one from the feet, and one from the middle. Ensuring more accurate interactions during combat.


//TOP ATTACK int angle = (int)(hitArcAngle / 2) * -1; 
rayNumber = (int)hitArcAngle / 5; 
for (int i = 0; i < rayNumber; i++) 
{
    //Calculate ray direction
    Vector3 forwardDirection = Quaternion.Euler(0, angle, 0) * transform.forward; 
    angle += 5; 
    //Ray origin 
    Vector3 topPos = new Vector3(transform.position.x, transform.position.y + 1.5f, transform.position.z); 
    
    RaycastHit[] hits = Physics.RaycastAll(topPos, forwardDirection, maxAttackLength); 
    for (int j = 0; j < hits.Length; j++) 
    { 
        if (!hits[j].collider.isTrigger) 
        { 
            IKnockback knockbackEffect = hits[j].collider.GetComponent<IKnockback>();
            if (knockbackEffect != null) 
            { 
                Vector3 knockback = transform.forward * knockBackForce * attackCharge; 
                knockbackEffect.Knockback(gameObject, knockback); 
            } 
            break; 
        } 
    } 
    //Debug lines to see the rays
    Debug.DrawLine(topPos, topPos + forwardDirection * maxAttackLength, Color.red, 2); 
}
            

Power-up

The power-up structure was implemented in a hurry and could definitely benefit from further refinement. It features a spawner that manages a timer and disables or hides the power-up’s mesh and collider. The power-up itself is responsible for handling collisions. When a player enters the collision area, it searches the player’s child objects for specific components. This includes the character’s weapon shader and a script that manages weapon variables, such as knockback power.

powerup structure

An ideal structure would involve referencing the shader and variables directly on the main player object, which would simplify and expedite component retrieval. Since the game only features a single power-up, it could manage both spawning and collisions independently. This approach would reduce the number of objects involved, making it more straightforward for level designers to work with.

wanted powerup structure
//Powerup.css
//Player collides with the powerup
private void OnTriggerEnter(Collider other) 
{ 
    if(other.tag == "Player" && !other.isTrigger) 
    { 
        PowerUpEffect(other); 
    } 
}

public override void PowerUpEffect(Collider player) 
{ 
    //Gets the player 
    playerMe = player.GetComponent<PlayerMovement>(); 
    if(playerMe) 
    { 
        //finds player weapon Changes material to gold 
        player.GetComponentInChildren<PlayerWeaponPowerUP>()?.ChangeMaterialToGold(); 
    }
}

//PlayerWeaponPowerUP.css
//Do visuals for powerup on player
public void ChangeMaterialToGold()
{
    //If weapon has multiple materials change all to gold and keep a list of old ones
    Material[] materials = new Material[oldMatList.Length];
    for (int i = 0; i < materials.Length; i++)
    {
        materials[i] = gold;
    }
    GetComponent().materials = materials;
    
    if (vfxPowerup && vfxPowerup.isStopped)
        vfxPowerup.Play();

    //Do actual weapon power increase
    IncreasePower();
}

public void IncreasePower()
{
    isReset = false;
    isPoweredUp = true;
    if(spawner)
    {
        spawner.isPoweredUp = true;
    }
    powerUPTimer = powerupTime;
    oldKnockbackForce = playerMe.GetComponent().knockBackForce;
    oldGroundslamStrength = playerMe.GetComponent().strength;
    playerMe.GetComponent().knockBackForce = newKnockbackForce;
    playerMe.GetComponent().strength = groundslamStrength;
}

Exploding barrel

The exploding barrels remain inactive until a player strikes them. If a barrel is knocked into another player, it detonates instantly. Otherwise, it starts a timer and will explode after a set duration, knocking back any players within the explosion radius.