FIRST PERSON CONTROLLER ADVANCED

A first-person character controller designed around the built-in CharacterController script

Many controllers use rigidbody movement as their base, we decided to go with the built-in solution as it is capable of producing the same quality result as the rigidbody movement and it is a very extendedly tested solution, since it comes from the PhysX controller from Nvidia.

Notably, it is predictable, which means that it can support client-predicted multiplayer games or simulation games.

The FirstPersonController creates a movement vector from a variety of states and variables that gets executed by the CharacterController translating the transform and resolving the collisions

UNITY CHARACTER CONTROLLER


CharacterController is the built-in solution to create a movable character in Unity. It uses behind the scene the Nvidia PhysX controller, we recommend checking out their documentation for more in-depth documentation

and the Unity Manual for the exposed variables

QUICK INSTALLATION GUIDE

BUILT-IN

Download the package from the Package Manager and import the files. Be aware that the project settings will be overridden, be sure to backup your project.

HDRP

From an HDRP project, download the package and import only 'HDRP.unitypackage' and 'InputManager.asset'. Once imported, open HDRP.unitypackage inside the project and import all the files. Be aware that the input settings will be overridden, be sure to backup your project.

URP

From an URP project, download the package and import only 'URP.unitypackage' and 'InputManager.asset'. Once imported, open URP.unitypackage inside the project and import all the files. Be aware that the input settings will be overridden, be sure to backup your project.

FIRST PERSON CONTROLLER

FirstPersonController is responsible for all the movements. Each moveset can be toggled and has a variety of exposed settings.

HORIZONTAL SPEED SETTINGS


  • Default Speed - Character speed while walking

  • Movement Deceleration - After the character stops apply a deceleration force. 0 means no deceleration and the character will stop instantly

  • Run Speed - Character speed while running

  • Backwards Speed - Character speed while moving backwards

  • Horizontal Max Speed - Maximum horizontal speed reachable by the character

JUMP SETTINGS


  • Adaptive Jump - If toggled on, the character will move vertically as long as the jump button is pressed and the Adaptive Jump Duration timer has not reached the threshold

  • Adaptive Jump Duration - When Adaptive Jump is toggled on, this is the maximum amount of time the player can press the jump button to propel the character

  • Jumps Count - Number of jumps available to the character while in the air

  • Coyote Time - Amount of time for which the character can jump when falling off an edge



  • Air Control - While in the air the character will be able to control the horizontal movement by this percentage



  • Air Momentum Friction - Amount of friction applied when the character is in the air. The more friction the less distance the character will move horizontally when a jump occurs

  • Vertical Max Speed - Maximum vertical speed reachable by the character

  • In Air Collider Height - Character height while in air. Can be useful in order to jump inside narrow spaces like windows

RUN SETTINGS


  • Run Speed - Character speed while running

  • Can Run While Strafing - If toggled on, the character will be able to run diagonally

TACTICAL SPRINT SETTINGS


  • Speed - Character speed while in tactical sprint

  • Duration - Amount of time available to the character when activating the tactical sprint

PRONE SETTINGS


  • Speed - Character speed while prone

  • Collider Height - Character height while prone

  • Collider Morph Speed - How fast the collider shrinks to 'Collider Height'

CROUCH SETTINGS


  • Speed - Character speed while crouched

  • Collider Height - Character height while crouched

  • Collider Morph Speed - How fast the collider shrinks to 'Collider Height'

CLIMB SETTINGS


  • Max Duration - The duration of the climb is dynamic. This is the duration when the character climbs at Duration Max Distance.

  • Duration Max Distance - The duration of the climb is dynamic. This is the maximum distance for which the duration is calculated


  • Animation Speed Curve - This allows the animation to move slower or faster during its duration. A linear curve means the speed is constant

  • Climbable Object Layer Mask - Layer mask of objects that be climbed on

  • Max Distance From Climbable Object - Trigger distance for beginning the climb

  • Max Height - How high you can climb objects from the base of the character collider.

  • Max Height - How high you can climb objects from the base of the character collider.


  • Camera Inclination Intensity - The higher the more camera inclination is applied during the climb

  • Camera Inclination Intensity Curve - This curve represents the speed of the camera pitch during the climb animation. The value of this curve will be multiplied by Camera Inclination

SLIDE SETTINGS


  • Slide Gravity - Gravity-like force applied when sliding

  • Minimum Stop Velocity - When the character reaches this horizontal velocity it will stop sliding

  • Initial Force - Initial force applied when the character starts sliding

  • Horizontal Control - While sliding the character will be able to control the horizontal movement by this percentage

  • Ground Friction - Friction applied to the character while sliding

  • Camera Rotation Friction Factor - Friction applied when the camera looks in a different direction the character is moving

  • Collider Height - Character height while sliding

  • Collider Morph Speed - How fast the collider shrinks to 'Collider Height'

GRAPPLING HOOK SETTINGS


  • Cooldown - The cooldown of the grappling hook. This will begin as soon as the grappling launches

  • Speed While Hooked - Character speed while grappling

  • Horizontal Control Strength - How strong can the character pull itself horizontally while grappling

  • Detach Speed Limit Condition - The character will detach upon reaching this velocity (movement velocity or momentum)

  • Detach Angle Condition - The character will detach when the angle from the attached point to the current position is greater than this value. The character will be still attached if Detach Timer Condition is set

  • Detach Timer Condition - The character will detach when upon reaching the angle detach limit this timer goes off

  • Detach Min Distance Condition - The character will detach when it is this minimum distance from the hooked point

  • Initial Vertical Force - Force received by the character when the grappling is launched

  • Hookable Object Layer Mask - Layer mask for objects that are hookable

  • Launch Max Distance - Maximum launch distance of the grappling hook

  • Grappling launch Speed - Speed of the grappling hook

  • Hook Prefab - Prefab model of the hook attached at the end of the line renderer

  • Hook Offset From Target - Prefab model attach position offset

  • Line Renderer Start Position Transform - This is where the line renderer will be launched from

  • Line Renderer Material - Material of the line

  • Line Renderer Width - Width of the line

  • Line Renderer Segment Count - Controls the quality of the line in the animation

  • Line Renderer Wave Stiffness - Controls the rigidity of the line in the animation

  • Line Renderer Wave Count - Number of waves in the animation

  • Crosshair Color - The color of the crosshair when the grappling hook can get to a target

WALL RUN SETTINGS


  • Walkable Object Layer Mask - Layer mask of the objects wall runnable by the character

  • Speed While Running - Speed of the character while wall running

  • Auto Detach Timer Condition - The character will automatically detach from the wall after this amount of time

  • Auto Detach Force - Force applied when the character gets detached because of Auto Detach Timer Condition

  • Vertical Wall Jump Force - Vertical force applied to the character when it jumps while wall running (wall jump)

  • Horizontal Wall Jump Force - Horizontal force applied to the character when it jumps while wall running (wall jump)

  • Cooldown - The character won’t be able to do another wall run if this timer is in progress

  • Wall Run Gravity - Special gravity applied to character while attached to the wall

  • Wall Run Gravity While Moving - Special gravity applied to character while attached to the wall and moving

  • Attach Vertical Boost Min - Minimum vertical force applied to the character when it begins the wall run. The force vary linearly depending on how strong the character hits the wall (its velocity)

  • Attach Vertical Boost Max - Maximum vertical force applied to the character when it begins the wall run. The force vary linearly depending on how strong the character hits the wall (its velocity)

  • Attach Min Distance Condition - Minimum distance for which a wall run triggers

  • Attach Side Angle Condition - The character will detect walls on its side relative to this angle

  • Dynamic Camera Tilt - Whether the camera tilt lerp value target changes depending on the angle relative to the character forward direction to the wall. If this variable is NOT checked The camera tilt value target is fixed. Rotation value of 0 if the character is looking at the wall within 20 degrees, cameraTiltAngle if the character is looking away from the wall

  • Camera Tilt Angle - Camera tilt amount when the character is wall running

  • Camera Tilt Lerp Speed - Camera tilt lerp speed when the character is wall running

  • Camera Tilt Reset Lerp Speed - Camera reset tilt speed when the character stops wall running

LEANING SETTINGS


  • Head Offset - Local position offset of the head while leaning

  • Head Offset Lerp Speed - Local position offset speed of the head while leaning

  • Head Offset Reset Lerp Speed - Local position reset offset speed of the head when leaning stops

  • Camera Tilt Angle - Camera tilt amount while leaning

  • Camera Tilt Speed - Camera tilt lerp speed while leaning

  • Camera Tilt Reset Speed - Camera reset tilt lerp speed when leaning stops

  • Head Obstacle Layer Mask - Leaning doesn’t work if the offset head would hit an object with this layer mask

ZOOM SETTINGS


  • FOV While Zooming - Camera FOV while zooming

  • FOV Lerp Speed - Camera FOV lerp speed while zooming

  • FOV Reset Lerp Speed - Camera FOV lerp speed when zooming stops

FIRST PERSON CONTROLLER AUDIO

FirstPersonControllerAudio handles all audio produced by the controller, it links with FirstPersonController through events



  • Master Volume - Overall volume of the sounds

  • Footstep Minimum Distance - Minimum traveled distance in Unity units (meters, as of 2022) for which the footsteps are played

  • Footstep Random Volume Delta - Random volume offset when the sounds footstep sounds are played. Simulates sound caused by different “weight” for each footstep

  • Prone Footstep Minimum Distance - Minimum traveled distance in Unity units (meters, as of 2022) for which the footsteps are played when prone

  • Footstep Random Volume Delta - Random volume offset when the sounds footstep sounds are played. Simulates sound caused by different “weight” for each footstep when prone

  • Footstep Clips - List of footstep clips played randomly

  • Prone Footstep Clips - List of footstep clips played randomly when prone

  • Jump Count Increase Clips - Each index of the list represent the number of jumps played + 1 Example: Element 0 is the first jump clip, Element 1 is the double jump clip, Element 2 is triple jump clip, etc…

Note: For the first jump clip you can use either Element 0 of Jump Count Increase Clips or Jump Clip

CHARACTER INPUT

CharacterInput gathers the inputs from the InputManager. Keys for each action can be modified in the inspector.



  • Use Raw Input - If on, Horizontal and Vertical values are input are fixed to -1 , 0 , 1. If off, Horizontal and Vertical values input are between -1 to 1 using decimal numbers

CAMERA CONTROLLER

This script is responsible for handling the camera movement, it supports virtual heads or attachment to a character model



  • Stick Camera Root To Collider Ceil - If toggled on, the camera will follow the ceil of the collider. It is recommended to be turned on if don’t have a bone for the camera to be attached on

  • Camera Root Offset From Collider Ceil - When Stick Camera Root To Collider Ceil is toggled on, the local camera position will be offset from the ceil of the collider by this amount

  • Upper Limit - Camera look upper limit angle

  • Lower Limit - Camera look lower limit angle

  • Camera Speed - How fast does the camera move

HEADBOB


  • Target - Target transform that will be "head bobbed"

  • Cycle Distance - Minimum distance required for one headbob cycle. Represents the footstep distance of the character

  • Max Vertical Intensity - Maximum vertical headbob intensity for 'Vertical Intensity Curve'.

  • Max Horizontal Intensity - Maximum horizontal headbob intensity for 'Horizontal Intensity Curve'.

  • Intensity Speed Multiplier - Exponential value. The headbob intensity is stronger the faster the character moves. 1 means linear growth.

  • Vertical Intensity Curve - Curve representing the vertical intensity change over the movement of the character

  • Horizontal Intensity Curve - Curve representing the vertical intensity change over the movement of the character

  • Lerp Speed - Headbob camera interpolation speed

  • Reset Lerp Speed - Headbob camera reset interpolation speed

CAMERA INPUT

Handles all the input coming from the mouse (or controller) through the InputManager. You can change the mouse sensitivity and invert the input if necessary.



  • Mouse Horizontal Input - Horizontal input key (in the InputManager)

  • Mouse Vertical Input - Vertical input key (in the InputManager)

  • Sensitivity - Mouse sensitivity

  • Invert Input Horizontal - If toggled on, the horizontal input will be inverted

  • Invert Input Vertical - If toggled on, the vertical input will be inverted

EXECUTION ORDER

CharacterInput script

Update (gathers input)


CameraController script

LateUpdate (camera follow collider) -> LateUpdate (handle headbob)


FirstPersonController script

Update (gathers input) -> Update (execute the simulation) -> LateUpdate (draw the grappling line -> LateUpdate (handle edge detection movement) -> LateUpdate (handle platform detection movement)

SIMULATE


The simulation happens after the input is gathered. The ‘Simulate’ method takes as parameter the delta time desirable for the simulation, in this case it gets called in the Update method so we use Time.deltaTime. If we were to use ‘Simulate’ in the FixedUpdate method we would have to use time.fixedDeltaTime, it can be useful to call ‘Simulate’ inside FixedUpdate, in case of multiplayer support, or other purposes that requires the simulation to be replicated precisely.


We start the simulation by acquiring information on the ground normal and making a ground check. In the next instructions, via the method ‘DetermineControllerState’, we generate a controller state that will be used by all the affecting methods to create the final movement vector. The final movement vector is a combination of momentum and input movement, we calculate those inside ‘HandleMomentum’ and ‘HandleMovementVelocity’ respectively.



example inside ‘HandleMovementVelocity’


Jumping is handled inside ‘HandleJump’, affecting the Y of input movement vector, just like gravity through ‘HandleGravity’. The actual invoke that makes the controller move happens inside ‘ApplyMovement’ where the final movement vector is calculated and gets fed to CharacterController.Move(Vector3). As a consequence of ‘CharacterController.Move’ the method ‘OnControllerColliderHit’ may get called, there we gather information about the actual collider hit ground normal, which is different to the ground normal calculated at the beginning of the simulation and push physics objects.


HOW TO ADD A CUSTOM STATE


States are represented through the ControllerState struct, this is where you can add your custom state



The state machine gets evaluated every frame through the DetermineControllerState() method. States enter and exit here, add your custom state ‘case’ and link it to the other states.


An example could be how Prone state works


We created a ‘case’ handling the transitions from Prone to the other states, at the end of the case scope, if we don’t want to transition to any other state we just return ControllerState.Proned.



This is how transitioning to Prone from Crouched state looks



If the conditions are met we just return ControllerState.Proned as the evaluated state.

Q&A

Why aren’t you using a more usual class state machine instead of the switch shenanigans for the controller?

While a class state machine (or a more complex behavior tree or goap) would have made more sense from a theoretical point of view, the practical world of game programming comes down to commodity and performance. With respect to performance, the difference is not much as you would probably not have many of these controllers, in respect of legibility and workflow that’s where the choice was made. This controller has many different movesets, while they seem to be independent of each other when it comes to transition they need to talk and that’s where the mess starts. Dividing each moveset into different classes means you are moving the pieces of the puzzle further away from each other while needing to be perfectly at their right spot. Having different scripts and classes may seem to be more organized but you are just throwing complexity under the rug of inheritance, because in the end, each piece needs to talk to each other. A solution to this problem would have been to create a complex system to handle transitions but this comes at the cost of legibility and difficulty to maintain and expand the code. We decided to go for the simplest solution and let the controller exist as a single unit. It may seem scary because there are many lines of code, but it is organized with sense, and once you get a grasp of it, it becomes very easy to know where to go and what to touch.

How to change input keys?

Input keys can be changed in the Input Manager in the Project Settings

If you create new ones, remember to assign them in CharacterInput and CameraInput scripts in the inspector of the controller prefab

How to change the crosshair?

The crosshair can be changed in the prefab inspector under HUD Canvas -> Crosshair -> Crosshair

In order to change the color of the crosshair via code, you can use Crosshair.SetCrosshairColor(Color), you can do the same for the crosshair fill if an image is assigned via Crosshair.ShowCrosshairFillAndSetColor(Color)

How does the audio get played? (I don’t see any audio sources)

Audio gets played through the AudioLibrary script. You can play audio via AudioLibrary.Play2D(AudioClip) or AudioLibrary.Play3D(AudioClip, Vector3). When this class methods are called, a pool of AudioSources will be instantiated and used for 2D audio and they will be reused from thereon. You can call .MuteAll2D() and .UnmuteAll2D() to mute and unmute the audio sources

How do I change the position of the camera?

By default the camera is attached to the ceil of the collider, if so, you can change the offset from this position in the inspector inside CameraControls: Camera Root Offset From Collider

If not, you can just move CameraRoot wherever you prefer

I tried to move the controller via code with transform.position but it does anything

Since CharacterController overrides the position every frame, it must first be disabled. We already created a method that takes care of this, FirstPersonController.Teleport(Vector3), this method can be used for each frame without issues if your goal is to create animations.

I want to make the character jump higher if the jump button is held pressed, how do I do that?

It can be done by enabling Adaptive Jump inside jump settings in the prefab inspector, more in JUMP SETTINGS

How do I make certain walls not wall-runnable?

You can make specific objects not wall-runnable by setting their layer mask different from the one of Walkable Object Layer Mask inside the wall run settings.

How do I remove the speed indicator?

The speed indicator can be removed from the scene in the inspector of the controller prefab, under HUD Canvas -> Speed

How do I make the character ascend higher stairs?

The variable is Step Offset, inside the prefab inspector in the CharacterController script

In this case, the value is 0.3, which means the character can surpass stairs up to 30 cm. We do not recommend putting a value higher than 0.4

Why does the collider shrink while jumping?

You can control the height of the character while in the air with the variable In Air Height inside the controller prefab inspector in the jump settings.

We have this option to add the possibility of parkour inside windows and small entrances while jumping.