Follow Camera Controller
Summary#
The FollowCameraController follows a moving Target from behind, positioning the camera at a configurable distance, height, and lateral offset and pointing it at a "lookahead" point in front of the target. It's well-suited to third-person chase cameras for vehicles or characters.
The user is expected to update the Target (and optionally TargetForward / TargetUp) every frame to track the followed object.
Additionally:
FollowDistancecontrols how far behind the target the camera sits;FollowHeightcontrols how far above (or below) the target the camera sits;FollowLateralOffsetcontrols how far to the side of the target the camera sits.
You can change the Follow[...] properties each frame (either directly or via Adjust[...]() methods) for a more dynamic camera; alternatively you can set these values once and just update the Target[...] properties to keep the camera at a fixed distance/offset from the target.
For an overview of how camera controllers work in general, see Camera Controllers.
Example Usage#
// One time setup:
var controller = camera.CreateController<FollowCameraController>(); // (1)!
controller.LookaheadDistance = 2.4f; // (2)!
// Per-frame:
controller.Target = followedObject.Position; // (3)!
controller.TargetForward = followedObject.Forward; // (4)!
controller.TargetUp = Direction.Up; // (5)!
controller.AdjustAllViaDefaultControls(input.KeyboardAndMouse, deltaTime); // (6)!
controller.AdjustAllViaDefaultControls(input.GameControllersCombined, deltaTime); // (7)!
controller.Progress(deltaTime); // (8)!
-
This creates the controller, attached to the given
camera. -
This sets how far ahead of the
Targetthe camera should look. Any value of zero or greater is permitted (negative values are ignored).This value could also be changed per-frame too if desired.
-
This sets where the
Targetobject is in the world. -
This sets which way is "forward" for the target. The camera will always be positioned behind the target (i.e. the opposite direction to this).
-
This sets which way is "up" for the target; the camera will be oriented so its "up" direction matches.
You may not need to set this value every frame if your target's "up" orientation is always the same direction. Because setting
TargetForwardre-orthogonalizesTargetUpagainst it, setTargetForwardfirst when setting both. -
This manipulates the camera's offsets according to the default keyboard and mouse scheme.
You can replace this with more specific control code if desired (see below); or remove it entirely if you do not wish to allow user keyboard/mouse input control.
-
This manipulates the camera's offsets according to the default game controller scheme for all game controllers combined.
You can replace this with more specific control code if desired (see below); or remove it entirely if you do not wish to allow user gamepad input control.
-
Calling
Progress()once per frame is required on all camera controllers in order for them to actually alter their targetCamera's parameters.
Properties#
Per-Frame Targets#
-
Target -
The location in the world of the target the camera is following.
Typically updated every frame to match the moving object's position. Invalid values are ignored.
Defaults to
Location.Origin. -
TargetForward -
The direction the followed object is facing. The camera positions itself behind this direction.
Setting this also re-orthogonalizes
TargetUpagainst the new direction. Invalid values (includingDirection.None) are ignored.Defaults to
Direction.Forward. -
TargetUp -
The "up" direction relative to the followed object. The camera positions itself above the target along this direction.
This value is automatically orthogonalized against
TargetForward, so reading it back may not return exactly what you set. If the value is parallel toTargetForward, an arbitrary perpendicular direction is chosen instead. Invalid values (includingDirection.None) are ignored.Defaults to
Direction.Up. -
FollowDistance -
How far behind the target (along
-TargetForward) the camera sits.Must be zero or greater (negative and non-finite values are ignored).
You do not need to change this value every frame if you want to maintain a fixed follow distance from the
Target.Defaults to
0.6f. -
FollowHeight -
How far above (or below) the target (along
TargetUp) the camera sits.A positive value results in the camera sitting above the target. Can be negative to make the camera sit below the target, or zero for a neutral camera position.
You do not need to change this value every frame if you want to maintain a fixed follow height relative to the
Target.Defaults to
0.3f. -
FollowLateralOffset -
How far to the side of the target the camera sits, perpendicular to both
TargetForwardandTargetUp. Positive values offset to the left, negative values to the right.You do not need to change this value every frame if you want to maintain a fixed lateral distance relative to the
Target.Defaults to
0.4f.
Configuration#
-
LookaheadDistance -
How far in front of the
Target(alongTargetForward) the camera will look.Higher values give a "racing game" feel by aiming the camera ahead of the followed object.
Lower values will make the camera look directly at the target which reduces the distance ahead of the target that can be seen. A value of
0faims the camera directly at the target.Must be zero or greater (negative and non-finite values are ignored).
Defaults to
2.4f. -
HeightViewShiftMultiplier -
Determines how much the camera's look-at point shifts vertically when
FollowHeightchanges.Increase this value if you want changes in
FollowHeightto more greatly affect how much above/below the camera looks, decrease it to reduce the effect.A value of
1fmeans the camera will look exactly as far above/below the target (alongTargetUp) as specified byFollowHeight.A value of
0fkeeps the look-at point level with the target (alongTargetUp), disabling this functionality entirely.Must be zero or greater (negative and non-finite values are ignored).
Defaults to
0.44f. -
LateralOffsetViewShiftMultiplier -
Determines how much the camera's look-at point shifts laterally when
FollowLateralOffsetchanges.Increase this value if you want changes in
FollowLateralOffsetto more greatly affect how much to the left/right the camera looks, decrease it to reduce the effect.A value of
1fmeans the camera will look exactly as far to the left/right of the target's forward direction as specified byFollowLateralOffset.A value of
0fkeeps the look-at point centered on the target's forward axis always, disabling this functionality entirely.Must be zero or greater (negative and non-finite values are ignored).
Defaults to
0.28f.
Convenience Methods#
-
Progress(deltaTime, target, targetForward, targetUp) -
Sets
Target,TargetForward, andTargetUp(in that order), then callsProgress(deltaTime). -
Progress(deltaTime, target, targetForward, targetUp, followDistance, followHeight, followLateralOffset) -
As above, but also sets
FollowDistance,FollowHeight, andFollowLateralOffset. -
SetConstraints(lookaheadDistance, heightViewShiftMultiplier, lateralOffsetViewShiftMultiplier) -
Sets all of the configuration properties above in one call.
-
ResetParametersToDefault() -
Resets every property (including smoothing) to its default value.
Reacting to Input#
As camera controllers are often meant to be affected by user input, there are some convenience methods supplied for controlling the primary per-frame target properties:
Adjusting Follow Distance#
Keyboard / Mouse#
-
AdjustFollowDistanceViaMouseCursor(...) -
Adjusts
FollowDistanceaccording to the captured mouse cursor movement for this frame.The
axissets which cursor movement direction will be used (defaults toY, e.g. up/down).The
adjustmentPerPixelvalue is the amount to add toFollowDistancefor each pixel moved according to the givenaxis. If null,DefaultFollowDistanceSensitivityMouseCursorwill be used.If
invertMouseControlistrue, the calculated adjustment will be reversed. -
AdjustFollowDistanceViaMouseWheel(...) -
Adjusts
FollowDistanceaccording to the captured mouse wheel movement for this frame.The
adjustmentPerWheelIncrementvalue is the amount to add toFollowDistancefor each scroll increment on the mouse wheel. If null,DefaultFollowDistanceSensitivityMouseWheelwill be used.If
invertMouseControlistrue, the calculated adjustment will be reversed. -
AdjustFollowDistanceViaKeyPress(...) -
Adjusts
FollowDistanceaccording to whether a certain key is depressed for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
keyToTestForis the key that, when pressed, will adjust this property.If
reverseistrue, the calculated adjustment will be reversed. This parameter is required, and lets you specify two keys in a pair that mirror each other by invoking this method twice (once withreverseasfalseand once withreverseastrue).The
adjustmentPerSecvalue is the amount to add toFollowDistancefor each second this key is depressed. If null,DefaultFollowDistanceSensitivityKeyOrButtonPresswill be used.
Gamepad#
-
AdjustFollowDistanceViaControllerStick(...) -
Adjusts
FollowDistanceaccording to the captured controller stick position for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
axissets which stick movement direction will be used (defaults toY, e.g. up/down).The
maxAdjustmentPerSecvalue is the amount to add toFollowDistancewhen the stick is fully displaced along the givenaxis. If null,DefaultFollowDistanceSensitivityControllerStickwill be used.If
useLeftStickis true, the left controller stick will be measured; otherwise the right stick will be measured. Defaults tofalse.If
invertStickControlistrue, the calculated adjustment will be reversed. -
AdjustFollowDistanceViaControllerTriggers(...) -
Adjusts
FollowDistanceaccording to the captured controller trigger positions for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
maxAdjustmentPerSecvalue is the amount to add toFollowDistancewhen the trigger is fully displaced. If null,DefaultFollowDistanceSensitivityControllerTriggerwill be used.If
leftTriggerIncreasesDistanceis true, the left trigger will increase the follow distance and the right trigger decrease it; otherwise these directions will be reversed. Defaults totrue. -
AdjustFollowDistanceViaButtonPress(...) -
Adjusts
FollowDistanceaccording to whether a certain button is depressed for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
buttonToTestForis the button that, when pressed, will adjust this property.If
reverseistrue, the calculated adjustment will be reversed. This parameter is required, and lets you specify two buttons in a pair that mirror each other by invoking this method twice (once withreverseasfalseand once withreverseastrue).The
adjustmentPerSecvalue is the amount to add toFollowDistancefor each second this button is depressed. If null,DefaultFollowDistanceSensitivityKeyOrButtonPresswill be used.
Other#
-
AdjustFollowDistance(...) -
Adjusts
FollowDistanceaccording to the given rate (adjustmentPerSec) and time step (deltaTime).This method does not inspect any user input data but is provided as a convenience for building custom per-frame control code.
Adjusting Follow Height#
Keyboard / Mouse#
-
AdjustFollowHeightViaMouseCursor(...) -
Adjusts
FollowHeightaccording to the captured mouse cursor movement for this frame.The
axissets which cursor movement direction will be used (defaults toY, e.g. up/down).The
adjustmentPerPixelvalue is the amount to add toFollowHeightfor each pixel moved according to the givenaxis. If null,DefaultFollowHeightSensitivityMouseCursorwill be used.If
invertMouseControlistrue, the calculated adjustment will be reversed. -
AdjustFollowHeightViaMouseWheel(...) -
Adjusts
FollowHeightaccording to the captured mouse wheel movement for this frame.The
adjustmentPerWheelIncrementvalue is the amount to add toFollowHeightfor each scroll increment on the mouse wheel. If null,DefaultFollowHeightSensitivityMouseWheelwill be used.If
invertMouseControlistrue, the calculated adjustment will be reversed. -
AdjustFollowHeightViaKeyPress(...) -
Adjusts
FollowHeightaccording to whether a certain key is depressed for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
keyToTestForis the key that, when pressed, will adjust this property.If
reverseistrue, the calculated adjustment will be reversed. This parameter is required, and lets you specify two keys in a pair that mirror each other by invoking this method twice (once withreverseasfalseand once withreverseastrue).The
adjustmentPerSecvalue is the amount to add toFollowHeightfor each second this key is depressed. If null,DefaultFollowHeightSensitivityKeyOrButtonPresswill be used.
Gamepad#
-
AdjustFollowHeightViaControllerStick(...) -
Adjusts
FollowHeightaccording to the captured controller stick position for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
axissets which stick movement direction will be used (defaults toY, e.g. up/down).The
maxAdjustmentPerSecvalue is the amount to add toFollowHeightwhen the stick is fully displaced along the givenaxis. If null,DefaultFollowHeightSensitivityControllerStickwill be used.If
useLeftStickis true, the left controller stick will be measured; otherwise the right stick will be measured. Defaults tofalse.If
invertStickControlistrue, the calculated adjustment will be reversed. -
AdjustFollowHeightViaControllerTriggers(...) -
Adjusts
FollowHeightaccording to the captured controller trigger positions for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
maxAdjustmentPerSecvalue is the amount to add toFollowHeightwhen the trigger is fully displaced. If null,DefaultFollowHeightSensitivityControllerTriggerwill be used.If
rightTriggerRaisesHeightis true, the right trigger will raise the camera and the left trigger lower it; otherwise these directions will be reversed. Defaults totrue. -
AdjustFollowHeightViaButtonPress(...) -
Adjusts
FollowHeightaccording to whether a certain button is depressed for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
buttonToTestForis the button that, when pressed, will adjust this property.If
reverseistrue, the calculated adjustment will be reversed. This parameter is required, and lets you specify two buttons in a pair that mirror each other by invoking this method twice (once withreverseasfalseand once withreverseastrue).The
adjustmentPerSecvalue is the amount to add toFollowHeightfor each second this button is depressed. If null,DefaultFollowHeightSensitivityKeyOrButtonPresswill be used.
Other#
-
AdjustFollowHeight(...) -
Adjusts
FollowHeightaccording to the given rate (adjustmentPerSec) and time step (deltaTime).This method does not inspect any user input data but is provided as a convenience for building custom per-frame control code.
Adjusting Follow Lateral Offset#
Keyboard / Mouse#
-
AdjustFollowLateralOffsetViaMouseCursor(...) -
Adjusts
FollowLateralOffsetaccording to the captured mouse cursor movement for this frame.The
axissets which cursor movement direction will be used (defaults toX, e.g. left/right).The
adjustmentPerPixelvalue is the amount to add toFollowLateralOffsetfor each pixel moved according to the givenaxis. If null,DefaultFollowLateralOffsetSensitivityMouseCursorwill be used.If
invertMouseControlistrue, the calculated adjustment will be reversed. -
AdjustFollowLateralOffsetViaMouseWheel(...) -
Adjusts
FollowLateralOffsetaccording to the captured mouse wheel movement for this frame.The
adjustmentPerWheelIncrementvalue is the amount to add toFollowLateralOffsetfor each scroll increment on the mouse wheel. If null,DefaultFollowLateralOffsetSensitivityMouseWheelwill be used.If
invertMouseControlistrue, the calculated adjustment will be reversed. -
AdjustFollowLateralOffsetViaKeyPress(...) -
Adjusts
FollowLateralOffsetaccording to whether a certain key is depressed for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
keyToTestForis the key that, when pressed, will adjust this property.If
reverseistrue, the calculated adjustment will be reversed. This parameter is required, and lets you specify two keys in a pair that mirror each other by invoking this method twice (once withreverseasfalseand once withreverseastrue).The
adjustmentPerSecvalue is the amount to add toFollowLateralOffsetfor each second this key is depressed. If null,DefaultFollowLateralOffsetSensitivityKeyOrButtonPresswill be used.
Gamepad#
-
AdjustFollowLateralOffsetViaControllerStick(...) -
Adjusts
FollowLateralOffsetaccording to the captured controller stick position for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
axissets which stick movement direction will be used (defaults toX, e.g. left/right).The
maxAdjustmentPerSecvalue is the amount to add toFollowLateralOffsetwhen the stick is fully displaced along the givenaxis. If null,DefaultFollowLateralOffsetSensitivityControllerStickwill be used.If
useLeftStickis true, the left controller stick will be measured; otherwise the right stick will be measured. Defaults tofalse.If
invertStickControlistrue, the calculated adjustment will be reversed. -
AdjustFollowLateralOffsetViaControllerTriggers(...) -
Adjusts
FollowLateralOffsetaccording to the captured controller trigger positions for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
maxAdjustmentPerSecvalue is the amount to add toFollowLateralOffsetwhen the trigger is fully displaced. If null,DefaultFollowLateralOffsetSensitivityControllerTriggerwill be used.If
leftTriggerOffsetsLeftis true, the left trigger will shift the camera leftward and the right trigger rightward; otherwise these directions will be reversed. Defaults totrue. -
AdjustFollowLateralOffsetViaButtonPress(...) -
Adjusts
FollowLateralOffsetaccording to whether a certain button is depressed for this frame.The
deltaTimevalue is expected to be the time in seconds of this frame iteration.The
buttonToTestForis the button that, when pressed, will adjust this property.If
reverseistrue, the calculated adjustment will be reversed. This parameter is required, and lets you specify two buttons in a pair that mirror each other by invoking this method twice (once withreverseasfalseand once withreverseastrue).The
adjustmentPerSecvalue is the amount to add toFollowLateralOffsetfor each second this button is depressed. If null,DefaultFollowLateralOffsetSensitivityKeyOrButtonPresswill be used.
Other#
-
AdjustFollowLateralOffset(...) -
Adjusts
FollowLateralOffsetaccording to the given rate (adjustmentPerSec) and time step (deltaTime).This method does not inspect any user input data but is provided as a convenience for building custom per-frame control code.
Default Controls#
The following snippets show the implementation of AdjustAllViaDefaultControls(...) for keyboard/mouse and gamepad respectively. Every parameter after deltaTime is optional: invertDistanceControl, invertHeightControl, and invertLateralControl reverse each control, and the sensitivity parameters (distanceAdjustmentPerWheelIncrement, heightAdjustmentPerPixel, and lateralAdjustmentPerPixel for keyboard/mouse; maxDistanceAdjustmentPerSec, maxHeightAdjustmentPerSec, and maxLateralAdjustmentPerSec for gamepad) fall back to the default sensitivity constants when null:
// AdjustAllViaDefaultControls(input.KeyboardAndMouse, deltaTime):
AdjustFollowHeightViaMouseCursor(input, heightAdjustmentPerPixel, invertMouseControl: invertHeightControl);
AdjustFollowDistanceViaMouseWheel(input, distanceAdjustmentPerWheelIncrement, invertMouseControl: invertDistanceControl);
AdjustFollowLateralOffsetViaMouseCursor(input, lateralAdjustmentPerPixel, invertMouseControl: invertLateralControl);
// AdjustAllViaDefaultControls(input.GameControllersCombined, deltaTime):
AdjustFollowDistanceViaControllerTriggers(input, deltaTime, maxDistanceAdjustmentPerSec, leftTriggerIncreasesDistance: !invertDistanceControl);
AdjustFollowHeightViaControllerStick(input, deltaTime, maxHeightAdjustmentPerSec, invertStickControl: invertHeightControl);
AdjustFollowLateralOffsetViaControllerStick(input, deltaTime, maxLateralAdjustmentPerSec, invertStickControl: invertLateralControl);
Smoothing#
Smoothing changes how quickly the controller adjusts the camera to match the current target properties.
The FollowCameraController exposes two smoothing strengths:
PositionSmoothingStrengthcontrols how quickly the camera moves to match its target offset (the combination ofFollowDistance,FollowHeight, andFollowLateralOffset).TrackingSmoothingStrengthcontrols how quickly the camera's look-at point catches up to the target. Higher values make the camera "lag" behind the followed object's direction changes, which produces a more cinematic feel.
// Set properties' smoothing individually:
controller.PositionSmoothingStrength = SmoothingStrength.VeryMild;
controller.TrackingSmoothingStrength = SmoothingStrength.VeryMild;
// Set all properties' smoothing simultaneously:
controller.SetGlobalSmoothing(SmoothingStrength.VeryMild);
The default smoothing for all properties is VeryMild. You can choose from VeryMild, Mild, Moderate, Strong, VeryStrong, or None.
- Smoothing makes the camera feel more 'real' or physical.
- Higher strengths increase this feeling but also increase the latency between setting a target value and the camera actually meeting that target.
- Setting the smoothing to
Nonedisables smoothing entirely. This means the camera will always be updated to meet exactly the target value of each property on each frame; reducing latency to 0 but making the camera feel less physical.
Custom Smoothing Values
If the enum-based approach is not specific enough for your needs, every property can instead have a custom smoothing strength applied via SetCustomPositionSmoothingStrength() or SetCustomTrackingSmoothingStrength().
Each method takes a single float parameter that indicates the half-life of decay between the current value of a property and its target value. A half-life of 0f disables smoothing.
The half-life is the time (in seconds) the camera takes to cover half the distance to a new target value, starting from rest. For example, if the current value of X is 50 and the target value of X is 100, a half-life of 1f would move X to 75 after one second. Because the smoothing carries momentum (see below), X would then be at roughly 92 after the next second, rather than 87.5.
Advanced: Smoothing is implemented via a critically-damped spring, which is why it carries momentum. The smoothingHalfLife parameter is translated to become the Ω of the spring equation via the formula Ω = 1.6783469f / smoothingHalfLife.