Designing an Immersive FPS in Unreal Engine 4! Part 3: Aiming Down Sights with Procedural Animation…and Math!

Designing an Immersive FPS in Unreal Engine 4! Part 3: Aiming Down Sights with Procedural Animation…and Math!

Welcome to the third part of my blog series about creating an immersive FPS in Unreal Engine 4!  In this part, we’ll be examining how I implemented one of the most common mechanics found in modern first person shooters; an aim down the sight system and the animations to go with it!

At the end of this part, our character will be able to bring the gun up to their line of sight and use the front sight post to aim more precisely. The character will also move their arms to the correct position when switching between shouldered and sight aiming states.

Series Index:

  1. Intent, Setup, & True First Person Character 
  2. Hand IK and Deadzone Rotation
  3. Aiming Down Sights with Procedural Animation…and Math!
  4. Firing Projectiles and Controlling the Pump Action
  5. Procedural Recoil Animations
  6. Manual Reloading Controls and Procedural Animations

 

Result of this part: animated transition between aim states and correct camera positioning

There are many ways to achieve this sort of effect. One way involves creating keyframed animations of the character bringing the gun up to their sight line for each gun. Another involves switching between two different cameras for each gun, one for the normal view and one for the sight view. The way I’ve implemented this system uses a different method; a mix of mathematical calculations on a single camera and procedural animations on the character’s arms.

Before diving into the implementation, I feel its best to explain the different types of procedural animations at a basic level as well as which method I’ve used for this mechanic and the other procedurally animated mechanics found in the next parts of this series.

Types of "Procedural Animation"

The term “procedural animation” is actually quite a broad way to describe what these techniques actually involve. At a basic level, a procedural animation is an animation that is not keyframed individually frame by frame by an animator, but rather uses code and/or animation curves to interpolate from one pose to another pose. For example, if you’ve ever used Unity’s built in animation keyframe system to have an object start at one position in the first frame and end at another position in another frame, you’ve essentially done the basics of procedural animation already.

The reason this term is quite broad is because there’s a lot of different ways to go about doing this. One way involves creating two separate poses for a “skeleton” (any hierarchy of parented transforms)  and interpolating between them with a curve value, much like you would do with the Unity animator example from the last paragraph. Another involves interpolation based on world or local position instead of a pose. Combine either of these with inverse kinematics, ragdoll physics, or traditional keyframed animations and you have even more new ways to make animations with code!

I highly recommend watching the below video from Overgrowth developer David Rosen if you want to learn more about procedural animations before trying it for yourself. While he doesn’t mention anything about Unreal, these same concepts can be easily put into practice using the Timeline feature as well as the hand IK setup from the previous part in the series! 

Now its time to get into the animation part of the tutorial. Before we can do any animation, however, first we need to do some setup in the character blueprint for user input as well as the timeline values we’LL need to get the arms moving!

ADS Animation Step 1: Input & Timeline Setup

Before our character can even start animating between the shouldered and aiming states, it needs to know when it should start playing these animations. This should occur when the aim down sight button or key is pressed and released using a boolean state value. Additionally, the animation blueprint for the character is going to need to receive a notification when the button has been pressed or released. We could use the same boolean value, but I’ve elected to setup an event call that the anim blueprint will receive instead.

Go ahead and open up the character blueprint and create a new input action event for aiming down the sights if you haven’t already. I’m using the middle mouse button since the right mouse button is used for pumping the shotgun (which you’ll see in the next part).

Next, in the character blueprint, add an event for your aim down sight input you just created. You can opt to hook it up to a Flip Flop if you want it to be a toggle in and out state when pressed instead of holding the button down, which is what I’ve done. 

Then, create a variable called “isAimDownSights”. This is the boolean state variable that will let the rest of the character blueprint know if we’re in the aim down sight state. When the button is pressed, flag the bool as true; when its released/pressed again, flag it as false.

Finally, create two Event Dispatcher variables in the character blueprint called “OnAimIN” and “OnAimOUT”. We will use these later in the anim blueprint to notify it of the aiming input state. Hook up the OnAimIN to when isAimDownSights is true and OnAimOut to when isAimDownSights is set to false.

Now we need to create variables that are going to change over time. These will be used as the alpha value from 0-1 when interpolating from one animation state to the next procedurally. Unreal comes with a feature called Timelines which allow us to easily do just that! We’re going to need two separate Timeline nodes for the full implementation: one for the hand animation and one for the camera transition. We also need variables to store the values sent out through the timelines as time progresses. Once again, we need one float for the hand animation alpha and one for the camera transition alpha. 

Go ahead and create two new timeline nodes after the ADS input event as well as two new float variables. Name them ADS Lerp Alpha Camera/Hands respectively. Open up the camera timeline and make one float track named “Alpha”, then close it since we’re not using it right now. Hook up the alpha value to the ADS Lerp Alpha Camera value off of the timeline’s update node.

The hand animation, which we’re focused on right now, is a little different. Since we’re using two different positions when procedurally animating the hands, we need two separate float tracks, one for the forward animation (bringing the gun up) and one for the backward animation (bringing the gun back down). Create those float tracks in the hand timeline, then off of its update execution, add a branch to check if we’re in the aim down sight state. If its true, set the hand lerp alpha to the forward timeline value, if false, set it to the backward value. To make sure the hand timeline plays when we toggle the aim state, simply hook up both the pressed and released input toggles to the hand timeline’s “Play from Start” input. This will play the tracks from the beginning each time the button is pressed; if the state is aiming down sights, the forward track will be used; if its not aiming, the backward track will be used.

To finalize this step, all we need to do is setup the actual float values in our forwards and backwards tracks using curves. These values decide the transition speed and overall feel of the animation. I’ve set these up using curves with a bit of a bounce to them to give the animation a little bit more weight. You can create these curves however you like, as they will require a lot of tweaking to get the feeling you want from the animation! The forward curve should go from 0 to 1 and the backward curve should go from 1 back to 0.

 

Forwards and backwards timeline curves for the hands with a bit of "bounce" to them.

ADS Animation Step 2: Binding the Events

Now that we’ve got the states and alpha values all setup, its time to get into the nitty gritty of the animation itself in the character’s animation blueprint! We’re going to:

  1. Bind the event dispatchers from the character blueprint to custom events in the anim graph. These events will use a local anim graph boolean to detect the aim down sight state in much the same way we did in the character blueprint.
  2.  Set up a “Rear Sight” socket on the gun mesh and convert the hand bone’s space to the relative space of this socket. This will keep the sight relatively in the center of the screen and also keep the hands in line with the shotgun’s position for later animations played on top of the aim down sight animations.
  3.  Perform the procedural animation using lerps to and from different offset positions and setting the Right Hand IK Transform Location to the result of the lerp. This will move the hands to the desired hand poses at the speed and with the feel of the timeline values.

Go ahead and open up the anim blueprint for the character and go to the Event Graph. Since the anim graph needs to know if the aim state has changed at all times, we need to bind the custom character blueprint event dispatchers to the custom anim blueprint events when the game starts. To do this, we’ll the “Event Blueprint Initialize Animation” event so the events are bound and ready to listen for input as soon as the game is launched. We can’t bind the events, however, without a reference to the character blueprint class, since this is where the dispatchers are stored. 

To get a reference to the character, first create a local variable of your character blueprint class and name it “CharacterReference”. Next, call the “Try Get Pawn Owner” node and cast it to your character class. Hook the exec of the Initialize Animation event to the cast and hook the successful cast exec into a Set for Character Reference. Set CharacterReference to the result of the cast, and now we have our reference variable!  Now binding the events is easy: drag off of the CharacterReference set return value and type “Assign to OnAimIN/OUT”. This will automatically create a new custom event that is binded to the event dispatcher! Do this for both dispatchers.

Now all we need is the local aim state boolean to check for aiming input. Create a new bool called “isCharacterADS” as well as another bool called “LerpFinished”. LerpFinished will prevent the animation from playing constantly after its completed since the code will be in tick. Finally, drag off of both of the events and set isCharacterADS; for the aim in, set it to true; for the aim out, set it to false. Set LerpedFinish to false after each of these nodes, as this will reset the value. The anim blueprint is now ready to detect the aiming state based on input! 

ADS Animation Step 3: Converting Hand Space to Sight Socket Space

Next up is the sight socket on the gun which we’re going to convert the right hand’s transform space to. This is important moving forward, since it will not only help keep the sight within the center of the screen when aiming but also keep any other animations played when aiming down sights stay relative to one another.

Open up the skeletal mesh for your gun and create a socket called “RearSight”. Position it about where the rear sight should be relative to where the character will be looking. Make sure that the socket’s X rotation is pointing forwards towards the barrel, the Z axis is pointing upwards, and the Y axis is pointing to the right. This will keep the forward vector of the sight relative to Unreal’s forward vector, which will help later on. You’ll need to tweak the position of this socket while the game is running to get this perfect after we actually set up the animation code.

Rear sight socket.

Now we need to simply convert the transform space of the right hand bone to the transform space of this socket! Head back into the anim blueprint event graph and continue extending off of the Update Animation event. We need to get a reference to our character blueprint’s character mesh and get the “socket” transform of the right hand bone as well as the socket transform of the current weapon’s RearSight socket. 

Then all we need to do is use MakeRelativeTransform with the hand transform being the A input and the sight socket being the transform to make A relative to. Then set this to a new transform variable that will be used when we start adding the animation code.

Simple conversion of the right hand's space to the sight's space used when entering the aiming state

ADS Animation Step 4: Procedural Animation Code!

Its time for the most complicated (and fun) part of this process; writing the procedural animation code! While it will look like its relatively complicated, once you wrap your head around it the first time, it makes a lot more sense and is way easier to do for future animations!

 Here’s the pseudo code for what this process looks like, as well as the finished code that I’m currently using. Try doing it yourself with the pseudo code first before looking at my finished code!

  1. Is the character in the aiming state/has the lerp not finished?
  2. If in the aiming state, lerp from a “normal” (non aiming) location offset vector to the converted sight space location plus a manually entered location offset using the hands alpha from the character blueprint timeline. 
  3. Apply the resulting lerp value to the RightHandIKTransformLocation variable.
  4. If not in the aiming state, lerp from the ADS offset location vector to the normal location vector, then apply the result to the RightHandIKTransformLocation variable.
  5. After both results, check if the RightHandIKTransformLcoation is nearly equal to the current aim state offset. If it is, set LerpFinished to true so the lerp isn’t continuously happening once its finished.
Finished procedural animation code for aiming down sights.

*Note: Don’t worry about the “Easing” function’s I’m using here, as they’re not part of Unreal by default. They’re from this function library created by a fellow developer! While not necessary for this animation, easing may provide you with a smoother feel. Give it a try if you want!*

All that’s left to do now is tweak the offset variables until the arms are in the correct position! Since this is a true first person character, I have my aim down sight offset values set with the gun staying at the shoulder, pulled up to head level, and slightly closer to the center of the head. Avoid moving the offset in a way that the gun is directly in near the center of the head, as this will cause the arms to be sticking out of the character’s body!

Setting the offset variables. A good way to test this is running the game with the animation blueprint open and setting the Right Hand FABRIK to use the offset values.

Go ahead and tweak the timeline curves, socket position, and offset values until you’re satisfied with the result! You want to end up with something like this:

Procedural animation result!

You’re probably wondering “um…why isn’t the sight in the center of the screen? Isn’t the chracter supposed to be aiming?”. You’re completely correct; the animation is ONLY controlling the way the arms move up and downTo get the sight lined up in the center of the screen, we’re going to use some mathematical calculations with the camera, which is where that camera timeline we made earlier comes in!

ADS Camera Math

The math we’re going to use is based on the same calculations used in the tactical FPS game Ground Branch. One of the devs of the game (“Kris” as he’s known on the Unreal forums) was kind enough to share the game’s camera ADS math with the Unreal community, and it works perfectly with the procedural hand animation we just created! Check out this thread to see the original math behind my implementation!

On a basic level, the camera math will find a parallel location to the current view location which will line up with wherever the sight socket is positioned. It can move up and right but not forwards. To use the following math code, you’ll need to create a new PlayerCameraManager class and override the BlueprintUpdateCamera function. Go ahead and do that then add the following code:

Code based off of the math from Kris from the Ground Branch team
Cont. from prev image: Actual lerp calculations for the position and FOV change

Once you’ve added the math, you’ll need to setup the timeline curve for the camera lerp alpha we made at the beginning of this post. A simple linear curve works nicely. Hook up the on aim in state to the Play input of the timeline and the on aim out state to the Reverse input on the timeline. This will move the camera from position 0 to 1  or 1 to 0 respectively when toggling the aim state!

Simple curve for the camera timeline

Result

After tweaking all the timeline values, offsets, and sight socket, you want to end up with something that looks like this:

Final aim down sight implementation: Arms animate procedurally and the camera snaps to the sight's position smoothly!

I hope you found this part of the series informative, interesting, and/or useful for your own project! Keep in mind that there’s several other different ways to achieve this same result, and my implementation may not be best suited for your project. There’s also other ways you could expand or improve on this; maybe you want to turn off the deadzone effect when aiming down the sights or have scope attachments that change the position of the sight socket.

If you’ve used this for one of your projects, leave a comment with a link to it down below! I’d love to see your work! In the next part of this series, i’ll be getting into perhaps my favorite part of this system: firing projectiles and manually pumping the action of the shotgun using mouse movements! Its a very cool, easy to setup, and kinetic feeling system that I think adds a lot to standard FPS gameplay.

See you next time,

– Karl

Next part: Firing Projectiles and Controlling the Action

This Post Has 2 Comments

  1. Very interesting article, thanks for sharing!

Leave a Reply