Allie Keats
Game Programmer
Senior Systems Programmer at Behaviour Interactive with 5 years of professional C++ experience.

Capstone - Semester 2 Week 4

By: Allie Keats | 09 February 2018

Another week, another re[Mod] technical update!

WeaponView Refactor

One of the issues we were running into as re[Mod] has expanded more and more is that the BaseWeapon class had grown a little out of control. BaseWeapon handled combining all of the stats from its component WeaponParts, handling the logic for things like shooting, reloading, aiming down sights, serializing all of the necessary data across the network, and presenting the player with a visual response to their actions (such as switching the model pieces when changing weapon parts, activating particle effects while shooting, etc). The result was a file that was over 1,000 lines long. It was split up into #regions, but I still always dreaded it a bit when going into that file. 

The first step in the solution to this problem was extracting out all of the “view” code out of this script. This included all of the code that handled VFX and animation, model switching, and any other visual code. In effect, it became similar to an MVC architecture, where the Model and Controller were combined in the BaseWeapon script and all of the view logic became the WeaponView script. Through this, we managed to significantly reduce the amount of code in the BaseWeapon script down to a mostly-manageable level, and the separation of concerns allows both scripts to be reasoned about more easily. Going forward, a potential next step would be to further implement the MVC architecture, pulling the Controller and Model for the Weapon out into two different classes.

Weapon Inertia

Another major improvement I made this week for “game feel” was the addition and tweaking of inertia in the movement of the weapon. This was something that we had been planning for a while, and the refactor of the WeaponView made it significantly easier. The subtle but important effect is demonstrated in the video below made by our designer Charlie Carucci, who is showing off some new weapon parts he made (with placeholder art):

This system works by accumulating the player’s “look” rotation over several frames, sampling them with a bias towards the recent ones to establish a rotational velocity, and then setting its own rotation to the current look rotation with the velocity added and a slight smoothing factor. Working on this gave me a renewed appreciation for Quaternions, because doing this with Euler angles would’ve been a nightmare. Here’s a look at the code that achieves this effect:

/// <summary>
/// Rotate the weapon to follow the player's looking direction
/// and the desired weapon inertia using the player's rotationalvelocity.
/// </summary>
/// <param name="bearer">The bearer of this weapon.</param>
private void HandleWeaponInertia(IWeaponBearer bearer)
{
       Quaternion bearerRotVelocity = CalculateAverageRotationalVelocity();
       Quaternion targetRot = bearer.eye.rotation;
       targetRot = targetRot * bearerRotVelocity;

       transform.rotation = Quaternion.Slerp(
        transform.rotation,
        targetRot,
        mCurrentMovementData.cameraRotationFollowFactor * Time.deltaTime);
}

/// <summary>
/// Calculate a weighted average of the change in the player's localrotations.
/// Essentially gives a weighted angular velocity in the form of a Quaternion.
/// </summary>
private Quaternion CalculateAverageRotationalVelocity()
{
       float sampleWeightSum = CalculateTotalWeightCurveSum(mRecentRotations.Length);

       Quaternion accumulation = Quaternion.identity;
       for (int i = 0; i < mRecentRotations.Length - 1; ++i)
       {
              Quaternion a = mRecentRotations[i], b =mRecentRotations[i + 1];
              Quaternion diff = b * Quaternion.Inverse(a);

              float weight = CalculateRotationWeight(
                     i,
                     mRecentRotations.Length,
                     sampleWeightSum);
                          
              Quaternion weightedDiff = Quaternion.Slerp(Quaternion.identity, diff,weight);
              accumulation = accumulation * weightedDiff;
       }
 
       return accumulation;
} 

That’s it for this week so far, but there’s a lot coming up! Weapon/head bob while walking, a UI overhaul, and a first-person hand viewmodel are all on the docket for this sprint, so look forward to the next blog post!

Back to blog