UDN
Search public documentation:

CameraTechnicalGuide
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > Gameplay Programming > Camera Technical Guide

Camera Technical Guide


Overview


The camera system for players in Unreal Engine 3 is comprised of three main classes: Camera, Pawn, and PlayerController. These classes all interact to control the position, rotation, and any other special effects that should be applied to the player’s camera during play.

The PlayerController holds a reference to the Camera being used as well as the Pawn being controlled. The PlayerController takes in the input from the player and uses that to update the positions and rotation of the Pawn it is controlling. By default, the Camera passes off its update to the Pawn, which in turn updates the position and rotation of the camera.

By modifying one or more of these classes and the way they interact, the player’s camera can be set to show the world to the player using any perspective that suits the type of game you are making. By default, the player’s camera uses a first-person perspective with the option to toggle it to a third-person over-the-shoulder perspective. This can easily be modified to show the world from a top-down perspective, an isometric view, a side-scrolling perspective, or any other view your game might require.

Camera


The camera class represents the player’s view into the world. The position and rotation of the player’s camera determines the viewpoint from which the scene is rendered when displayed on-screen. The Camera class also contains properties for controlling the way the world is seen through the camera, such as setting the field of view, aspect ratio, etc. Cameras also have special effects that can be applied to them including such things as post processing, lens effects, camera modifiers, camera animations, etc.

Note that all FOV variables are full angles, expressed in degrees.

Camera Properties

  • General
    • PCOwner – Reference to the PlayerController which owns this camera.
    • CameraStyle – Current camera mode for this camera. Used to determine the style of camera (i.e., first-person, third-person, free, etc.) when point of view is not being overridden by the view target.
    • ViewTarget – Current data defining the view target. Type is TViewTarget.
    • TViewTarget
      • Target – Target actor that the camera is "following".
      • Controller – Controller of the Target, if a Pawn.
      • POV – Ideal point of view for the Target. Type is TPOV.
        • TPOV
          • Location – Location for the point of view.
          • Rotation – Rotation of the point of view.
          • FOV – Field of view angle for the point of view.
        • AspectRatio – Aspect ratio to use for the Target.
        • PRI – Player replication info used to track the same player through pawn transitions.
  • FOV
    • DefaultFOV – Default field of view for the camera.
    • bLockedFOV – If True, the camera’s field of view will be locked to the value of LockedFOV.
    • LockedFOV – Field of view to use when FOV is locked.
  • Aspect Ratio
    • DefaultAspectRatio – Default aspect ratio for the camera.
    • bConstrainAspectRatio – If True, the camera’s aspect ratio will be constrained to the value of ConstrainedAspectRatio.
    • ConstrainedAspectRatio – Aspect ratio to use when the aspect ratio of the camera is being constrained.

Camera Functions

  • General
    • UpdateCamera [DeltaTime] – Called once per frame to perform an update of the camera.
      • DeltaTime – The amount of time since the last update occurred.
    • GetCameraViewPoint [OutCamLoc] [OutCamRot] – Retrieves the camera’s location and rotation. This function should not be called directly. Instead, the GetPlayerViewPoint() function of the PCOwner should be called instead.
      • OutCamLoc – Outputs the camera’s location.
      • OutCamRot – Outputs the camera’s rotation.
    • ProcessViewRotation [DeltaTime] [OutViewRotation] [OutDeltaRot] – Called by the PCOwner to give the camera an opportunity to alter this frame's view rotation changes.
      • DeltaTime – The amount of time since the last update occurred.
      • OutViewRotation – Outputs the adjusted view rotation of the camera.
      • OutDeltaRot – Outputs the adjusted delta rotation of the camera.
  • FOV
    • GetFOVAngle – Returns the camera’s current field of view angle.
    • SetFOV [NewFOV] – Sets the camera’s field of view to the NewFOV value.
      • NewFOV – Value to set the camera’s field of view to.
  • View Target
    • SetViewTarget [NewViewTarget] [TransitionParams] – Sets the view target of the camera.
      • NewViewTarget – Actor to set as the new view target of the camera.
      • TransitionParams – Blending parameters to use when transitioning to the new view target.
    • UpdateViewTarget [OutVT] [DeltaTime] – Called per-frame to update the view target to it's new position, rotation, and fov. If using custom cameras (as opposed to overriding CalcCamera() in the view target actor), this is the key function to override to implement your desired behavior.
      • OutVT – Outputs a data structure holding the view target and point of view of the camera.
      • DeltaTime – The amount of time since the last update occurred.

Post Process Effects

Post process effects are effects applied to the rendered scene before it is displayed to the player. Each camera has the ability to apply its own set of post process settings that can override the world, volume, or default post process settings.

For more information on post process effects, see PostProcessEditorUserGuide and PostProcessTechnicalGuide and PostProcessMaterials.

Post Process Effects Properties

  • CameOverridePostProcessAlpha – Sets the influence of the camera’s post process settings with respect to the world, volume, or default post process settings. A value of 0.0 means the world, volume, or default post process has full influence. A value of 1.0 means the camera’s post process has full influence.
  • CamPostProcessSettings – The post process settings to use when the camera is overriding the world, volume, or default post process.
  • bEnableColorScaling – If True, color channels in the final image will be scaled using the ColorScale values.
  • ColorScale – Vector for scaling individual color channels in final image.
  • bEnableColorScaleInterp – If True, the camera will interpolate between color scaling values when new color scaling values are set through the SetDesiredColorScale() function.
  • bEnableFading – If True, the camera will apply the FadeAmount of the FadeColor to the screen.
  • FadeColor – Color to apply to the screen when the camera is fading.
  • FadeAmount – Amount of fading to apply. In essence, the alpha of the fade.

Post Process Effects Functions

  • SetDesiredColorScale [NewColorScale] [InterpTime] – Sets new color scaling values and optionally interpolates depending on the value of bEnableColorScaleInterp.
    • NewColorScale – New values to use for color scaling.
    • InterpTime – Amount of time to take to interpolate to the new color scaling values.

Lens Effects

Lens effects are particle effects that are applied to the lens of the player’s camera. These lens effects can be used to create things like rain dripping down the camera lens, blood splatter, dirt or dust on the lens, etc. The camera class contains functions for applying these types of effects.

For more information on particle systems and effects, see ParticleSystemReference.

Lens Effects Properties

    • CameraLensEffects – Array of all particle effects currently applied to the camera.

Lens Effects Functions

  • FindCameraLensEffect [LensEffectEmitterClass] – Searches the lens effects applied to the camera currently and returns any of the matching type.
    • LensEffectEmitterType – Class of lens effect to search for.
  • AddCameraLensEffect [LensEffectEmitterClass] – Applies a new lens effect of the given type to the camera.
    • LensEffectEmitterClass – Class of lens effect to apply to the camera.
  • RemoveCameraLensEffect [Emitter] – Removes a lens effect from the camera.
    • Emitter – Lens effect to remove from the camera.
  • ClearCameraLensEffects – Removes all lens effects currently applied to the camera.

Camera Animations

Camera animations are animations that can be created within Matinee (or optionally in an external animation editor and imported) that use the translation and rotation information of the animation to offset the camera during play. They also provide the ability to animate any of the other properties of the camera that can normally be animated in Matinee, such as FOV or postprocess effects. This can be quite useful for creating effects like camera shaking, the hand-bobbing of a handheld camera, or any other animation effect.

For more information on setting up camera animations, see SettingUpCameras.

Camera Animation Functions

  • PlayCameraAnim [CameraAnim] [Rate] [Scale] [BlendInTime] [BlendOutTime] [bLoop] [bRandomStartTime] [Duration] [bSingleInstance] – Plays a camera animation on the camera.
    • CameraAnim – Camera animation to play.
    • Rate – Optional. Speed at which to play the camera animation.
    • Scale – Optional. Intensity multiplier to apply to the camera animation transformations.
    • BlendInTime – Optional. Amount of time to take to blend into the camera animation.
    • BlendOutTime – Optional. Amount of time to take to blend out of the camera animation.
    • bLoop – Optional. If True, the camera animation will continue to loop until explicitly stopped.
    • bRandomStartTime – Optional. If True, the camera animation will begin playing at a random time within the animation’s timeline.
    • Duration – Optional. Amount of time to play the animation for. If not set, the entire animation will be played.
    • bSingleInstance – Optional. If True, only one instance of the camera shake will be allowed to exist at any one time.
  • StopAllCameraAnims [bImmediate] – Stops all camera animations currently playing.
    • bImmediate – Optional. If True, the animations will stop immediately, ignoring any blending times set.
  • StopAllCameraAnimsByType [Anim] [bImmediate] – Stops all instances of a specific type of camera animation.
    • Anim – The type of camera animation to stop.
    • bImmediate – Optional. If True, the animations will stop immediately, ignoring any blending times set.
  • StopCameraAnim [AnimInst] [bImmediate] – Stops a specific instance of a camera animation.
    • AnimInst – The camera animation instance to stop.
    • bImmediate – Optional. If True, the animations will stop immediately, ignoring any blending times set.

Camera Modifiers

Camera modifiers are objects which, when applied to a camera, can modify properties of the camera. The CameraModifier class is the base class for these effects. By subclassing this class and overriding functions within it, completely custom modifiers can be created. The CameraModifier_CameraShake class is a good example of what can be accomplished with camera modifiers.

Camera Modifier Properties

    • ModifierList – Array of all camera modifiers currently applied to the camera
    • CameraShakeModClass – Class to use for cone-driven camera shake, i.e. non-camera animation screen shake from Kismet.

Camera Modifier Functions

  • PlayCameraShake [Shake] [Scale] [PlaySpace] [UserPlaySpaceRot] – Plays a camera shake effect on the camera.
    • Shake – CamerShake settings to use for the camera shake effect.
    • Scale – Scaling factor to multiply the camera shake settings by.
    • PlaySpace – Optional. Play space to use for the camera shake.
    • UserPlaySpaceRot – Optional. Rotation to use for user-defined play spaces.
  • StopCameraShake [Shake] – Stops playing a camera shake effect on the camera.
    • Shake – The camera shake to stop playing.
  • CalcRadialCameraShake [Cam] [Epicenter] [InnerRadius] [OuterRadius] [Falloff] – Calculates and returns the intensity for a particular camera of a radial shake.
    • Cam – Camera to calculate the intensity for.
    • Epicenter – Location where the camera shake originates from.
    • InnerRadius – Distance from the epicenter where falloff begins.
    • OuterRadius – Distance from epicenter where the effect of the camera shake ends.
    • Falloff – Exponent to use for calculating the falloff of the intensity.
  • PlayWorldCameraShake [Shake] [ShakeInstigator] [Epicenter] [InnerRadius] [OuterRadius] [Falloff] [bTrForceFeedback] [bOrientShakeTowardEpicenter] – Plays an in-world camera shake that affects all nearby cameras.
    • Shake – camera shake to play.
    • ShakeInstigator – Actor that instigated the camera shake.
    • Epicenter – Location where the camera shake originates from.
    • InnerRadius – Distance from the epicenter where falloff begins.
    • OuterRadius – Distance from epicenter where the effect of the camera shake ends.
    • Falloff – Exponent to use for calculating the falloff of the intensity.
    • bTryForceFeedback – If true, force feedback will attempt to be applied to any controllers being affected.
    • bOrientShakeTowardEpicenter – Optional. If True, any offsets in the camera shake will be applied relative to facing the epicenter, with the positive X-axis being towards the epicenter.
  • ClearAllCameraShakes – Removes all camera shakes currently applied to the camera.

Player Controller


The PlayerController is responsible for translating player input into game actions, such as moving a Pawn or controlling the camera. It is typical for the PlayerController's rotation to drive the camera rotation, although this isn't strictly necessary. When creating new camera perspectives, it may be necessary to update or override some functionality within the PlayerController class as how the player’s input is translated into the movement and orientation of the Pawn can differ with each type of camera. Some of the properties and functions relating to movement and cameras are described below.

Player Controller Properties

  • PlayerCamera – Reference to the player’s camera.
  • CameraClass – Class of camera to use for the player.
  • ViewTarget – The current view target of the player’s camera.
  • RealViewTarget – Player replication info of the view target of the player’s camera.
  • FOVangle – Field of view angle of the Player’s camera.
  • DefaultFOV – Default field of view angle to use for the player’s camera.

Player Controller Functions

  • GetPlayerViewPoint [out_Location] [out_Rotation] – This returns the point of view of the Controller’s Pawn. For human players, this is the camera’s viewpoint. For AI-controlled players, this is the viewpoint from the Pawn’s eyes. In this base implementation, it is simply the location and rotation of the Controller itself.
    • out_Location – Outputs the location of the player’s viewpoint.
    • out_Rotation – Outputs the rotation of the player’s viewpoint.
  • GetActorEyesViewPoint [out_Location] [out_Rotation] – This returns the point of view of the Controller or its Pawn if one exists. Essentially, this returns where the player is looking from and in which direction.
    • out_Location – Outputs the location of the player’s eyes.
    • out_Rotation – Outputs the rotation of the player’s eyes.
  • UpdateRotation [DeltaTime] – This updates the rotation of the Controller and that of the Controller’s Pawn based on the player’s input.
    • DeltaTime – The amount of time since the last update occurred.
  • ProcessViewRotation [DeltaTime] [out_ViewRotation] [DeltaRot] – This is called to allow for any modifications to be made to the controller's view rotation (e.g. clamping). This is called from UpdateRotation().
    • DeltaTime – The amount of time since the last update occurred.
    • out_ViewRotation – Outputs the player’s view rotation.
    • DeltaRot – Change in rotation due to player input.
  • PlayerMove [DeltaTime] – This calculates the new acceleration and rotation values for the current move and then calls either ProcessMove() (for single-player or listen servers) or ReplicateMove() (for network clients). This is simply a stub in the base PlayerController class but is overridden within certain states which involve movement, such as the PlayerWalking state. This function is called from the PlayerTick() function every cycle.
  • ProcessMove [DeltaTime] [newAccel] [DoubleClickMove] [DeltaRot] – This handles the current move on the client. This function is overridden inside certain states which require special functionality for movement.

Pawn


The Pawn is not only the player’s physical representation in the world, but can also be responsible for controlling the position and rotation of the player’s camera. It contains functions which can be overridden to create entirely new camera perspectives. Some of the camera-related functions are describe below.

Pawn Functions

  • CalcCamera [DeltaTime] [out_CamLoc] [out_CamRot] [out_FOV] – This calculates the camera’s viewpoint when viewing from the Pawn. This is the main camera calculation for the player.
    • DeltaTime – The amount of time since the last update occurred.
    • out_CamLoc – Outputs the camera’s location.
    • out_CamRot – Outputs the camera’s rotation.
    • out_FOV – Outputs the camera’s field of view.
  • GetDefaultCameraMode [RequestedBy] – This returns the default camera mode, as a name, that should be used for this Pawn. This is usually called by the controller when possessing the Pawn.
    • RequestedBy - The controller requesting the default camera mode.
  • ProcessViewRotation [deltaTime] [out_ViewRotation] [out_DeltaRot] – This gives the pawn the opportunity to influence the player’s view rotation and returns the final view rotation as the out_ViewRotation parameter. This is called from the UpdateRotation() function of the PlayerController.
    • deltaTime – The amount of time since the last update occurred.
    • out_ViewRotation – Outputs the rotation of the Pawn’s point of view.
    • out_DeltaRot – Outputs the delta rotation.
  • SetViewRotation [NewRotation] – Sets the rotation of the Controller, if one exists, or of the Pawn itself if no Controller exists.
    • NewRotation – New rotation to set the Pawn’s view to.
  • GetActorEyesViewPoint [out_Location] [out_Rotation] – This returns the location and orientation of the Pawn’s eyes, or the point of view of the player. For a first person perspective this is identical to the camera location and orientation. It is also the view point most traces will be performed from.
    • out_Location – Outputs the location of the Pawn’s eyes.
    • out_Rotation – Outputs the rotation of the Pawn’s eyes.

Customizing Camera Behavior


There are 2 main ways to implement your custom camera. The view target Pawn can implement CalcCamera(), or you can create a custom class extending Camera.

Implementing Pawn.CalcCamera() is useful for simple and straightforward camera modes. The tradeoff is that some functionality may not be fully functional via this method, including post process effects or camera animations.

Creating a custom camera class can take a little more overhead to set up, but is more fully featured. GameFramework.GamePlayerCamera is an example of this approach.

Examples - CalcCamera


Below, there are some basic examples of modifying the player’s camera perspective using the Pawn's CalcCamera() function and in some cases how player input is handled as well. These are not meant as complete plug and play solutions to all your camera needs. They are simply a jumping off point to get started creating your own custom camera setups.

There are obviously a great deal of other modifications that could and should be done to make these polished, shippable camera types. You might want to implement the ability to adjust the camera’s distance from the player in any of non-first person modes by scrolling the mouse wheel. Adding code to avoid the camera encroaching on world geometry in some of the modes might be a good idea if you are not planning to design your levels specifically to avoid such things. Also, changing how the crosshair is drawn instead of removing it completely would be a nice addition in some of the modes.

All of these examples will require a new custom gametype class to tell the game to use the new Pawn and PlayerController classes.

UDNGame.uc

class UDNGame extends UTDeathMatch;

defaultproperties
{
   DefaultPawnClass=class'UDNExamples.UDNPawn'
   PlayerControllerClass=class'UDNExamples.UDNPlayerController'
   MapPrefixes[0]="UDN"
}

The DefaultGame.ini will need to be modified to tell the engine to use the new gametype as the default as well.

DefaultGame.ini

[Engine.GameInfo]
DefaultGame=UDNExamples.UDNGame
DefaultServerGame=UDNExamples.UDNGame

Note: In order for the new gametype to be used, you will need to make sure your maps have the correct prefix. We set the prefix to be "UDN" in our gametype so all maps will need to be named starting with "UDN-". It is also possible to quickly test the new gametype with any map in the editor by setting the Game Type PIE property in the World Properties for the map to the new gametype.

Example First Person Camera

A first person perspective is the default camera type for all pawns extending from UTPawn. This example pulls out the main parts that make up that camera type from each class involved and places them in new subclasses in order to better demonstrate the process involved in creating a basic first person camera.

camera_first.jpg

UDNPawn.uc

class UDNPawn extends UTPawn;

simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   // Calculate first person camera position and rotation
   GetActorEyesViewPoint( out_CamLoc, out_CamRot );

   return true;
}   

defaultproperties
{
}

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;

   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      if( Pawn == None )
      {
         return;
      }

      if (Role == ROLE_Authority)
      {
         // Update ViewPitch for remote clients
         Pawn.SetRemoteViewPitch( Rotation.Pitch );
      }

      Pawn.Acceleration = NewAccel;

      CheckJumpOrDuck();
   }
}

function UpdateRotation( float DeltaTime )
{
   local Rotator   DeltaRot, newRotation, ViewRotation;

   ViewRotation = Rotation;
   if (Pawn!=none)
   {
      Pawn.SetDesiredRotation(ViewRotation);
   }

   // Calculate Delta to be applied on ViewRotation
   DeltaRot.Yaw   = PlayerInput.aTurn;
   DeltaRot.Pitch   = PlayerInput.aLookUp;

   ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
   SetRotation(ViewRotation);

   NewRotation = ViewRotation;
   NewRotation.Roll = Rotation.Roll;

   if ( Pawn != None )
      Pawn.FaceRotation(NewRotation, deltatime);
}   

defaultproperties
{
}

Example Third Person Camera

A third person camera setup is also included as an alternate camera type for all subclasses of UTPawn. This example pulls the main parts out and overrides the default camera to be this third person camera.

camera_third.jpg

UDNPawn.uc

class UDNPawn extends UTPawn;

//override to make player mesh visible by default
simulated event BecomeViewTarget( PlayerController PC )
{
   local UTPlayerController UTPC;

   Super.BecomeViewTarget(PC);

   if (LocalPlayer(PC.Player) != None)
   {
      UTPC = UTPlayerController(PC);
      if (UTPC != None)
      {
         //set player controller to behind view and make mesh visible
         UTPC.SetBehindView(true);
         SetMeshVisibility(UTPC.bBehindView);
      }
   }
}

simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   local vector CamStart, HitLocation, HitNormal, CamDirX, CamDirY, CamDirZ, CurrentCamOffset;
   local float DesiredCameraZOffset;

   CamStart = Location;
   CurrentCamOffset = CamOffset;

   DesiredCameraZOffset = (Health > 0) ? 1.2 * GetCollisionHeight() + Mesh.Translation.Z : 0.f;
   CameraZOffset = (fDeltaTime < 0.2) ? DesiredCameraZOffset * 5 * fDeltaTime + (1 - 5*fDeltaTime) * CameraZOffset : DesiredCameraZOffset;
   
   if ( Health <= 0 )
   {
      CurrentCamOffset = vect(0,0,0);
      CurrentCamOffset.X = GetCollisionRadius();
   }

   CamStart.Z += CameraZOffset;
   GetAxes(out_CamRot, CamDirX, CamDirY, CamDirZ);
   CamDirX *= CurrentCameraScale;

   if ( (Health <= 0) || bFeigningDeath )
   {
      // adjust camera position to make sure it's not clipping into world
      // @todo fixmesteve.  Note that you can still get clipping if FindSpot fails (happens rarely)
      FindSpot(GetCollisionExtent(),CamStart);
   }
   if (CurrentCameraScale < CameraScale)
   {
      CurrentCameraScale = FMin(CameraScale, CurrentCameraScale + 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*fDeltaTime);
   }
   else if (CurrentCameraScale > CameraScale)
   {
      CurrentCameraScale = FMax(CameraScale, CurrentCameraScale - 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*fDeltaTime);
   }

   if (CamDirX.Z > GetCollisionHeight())
   {
      CamDirX *= square(cos(out_CamRot.Pitch * 0.0000958738)); // 0.0000958738 = 2*PI/65536
   }

   out_CamLoc = CamStart - CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ;

   if (Trace(HitLocation, HitNormal, out_CamLoc, CamStart, false, vect(12,12,12)) != None)
   {
      out_CamLoc = HitLocation;
   }

   return true;
}   

defaultproperties
{
}

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;

   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      if( Pawn == None )
      {
         return;
      }

      if (Role == ROLE_Authority)
      {
         // Update ViewPitch for remote clients
         Pawn.SetRemoteViewPitch( Rotation.Pitch );
      }

      Pawn.Acceleration = NewAccel;

      CheckJumpOrDuck();
   }
}

function UpdateRotation( float DeltaTime )
{
   local Rotator   DeltaRot, newRotation, ViewRotation;

   ViewRotation = Rotation;
   if (Pawn!=none)
   {
      Pawn.SetDesiredRotation(ViewRotation);
   }

   // Calculate Delta to be applied on ViewRotation
   DeltaRot.Yaw   = PlayerInput.aTurn;
   DeltaRot.Pitch   = PlayerInput.aLookUp;

   ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
   SetRotation(ViewRotation);

   NewRotation = ViewRotation;
   NewRotation.Roll = Rotation.Roll;

   if ( Pawn != None )
      Pawn.FaceRotation(NewRotation, deltatime);
}   

defaultproperties
{
}

Example Top-Down Camera

A top-down camera can be created by making some additional modifications. It is similar to the third-person camera setup, but also requires limiting the Pawn’s rotation, specifically its pitch as aiming up or down won’t be allowed.

camera_top.jpg

UDNPawn.uc

class UDNPawn extends UTPawn;

var float CamOffsetDistance; //distance to offset the camera above the player
var bool bFollowPlayerRotation; //If true, camera rotates with player

//override to make player mesh visible by default
simulated event BecomeViewTarget( PlayerController PC )
{
   local UTPlayerController UTPC;

   Super.BecomeViewTarget(PC);

   if (LocalPlayer(PC.Player) != None)
   {
      UTPC = UTPlayerController(PC);
      if (UTPC != None)
      {
         //set player controller to behind view and make mesh visible
         UTPC.SetBehindView(true);
         SetMeshVisibility(UTPC.bBehindView); 
         UTPC.bNoCrosshair = true;
      }
   }
}

simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   out_CamLoc = Location;
   out_CamLoc.Z += CamOffsetDistance;

   if(!bFollowPlayerRotation)
   {
      out_CamRot.Pitch = -16384;
      out_CamRot.Yaw = 0;
      out_CamRot.Roll = 0;
   }
   else
   {
      out_CamRot.Pitch = -16384;
      out_CamRot.Yaw = Rotation.Yaw;
      out_CamRot.Roll = 0;
   }

   return true;
}

simulated singular event Rotator GetBaseAimRotation()
{
   local rotator   POVRot, tempRot;

   tempRot = Rotation;
   tempRot.Pitch = 0;
   SetRotation(tempRot);
   POVRot = Rotation;
   POVRot.Pitch = 0; 

   return POVRot;
}   

defaultproperties
{
   bFollowPlayerRotation = false;
   CamOffsetDistance=384.0
}

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;

   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      if( Pawn == None )
      {
         return;
      }

      if (Role == ROLE_Authority)
      {
         // Update ViewPitch for remote clients
         Pawn.SetRemoteViewPitch( Rotation.Pitch );
      }

      Pawn.Acceleration = NewAccel;

      CheckJumpOrDuck();
   }
}

function UpdateRotation( float DeltaTime )
{
   local Rotator   DeltaRot, newRotation, ViewRotation;

   ViewRotation = Rotation;
   if (Pawn!=none)
   {
      Pawn.SetDesiredRotation(ViewRotation);
   }

   // Calculate Delta to be applied on ViewRotation
   DeltaRot.Yaw   = PlayerInput.aTurn;
   DeltaRot.Pitch   = 0;

   ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
   SetRotation(ViewRotation);

   NewRotation = ViewRotation;
   NewRotation.Roll = Rotation.Roll;

   if ( Pawn != None )
      Pawn.FaceRotation(NewRotation, deltatime);
}   

defaultproperties
{
}

Example Isometric Camera

A simple isometric-style camera is very similar to the top-down camera example shown previously. The camera is offset along two axes, X and Z, and then the pitch is rotated down to focus on the player.

camera_iso.jpg

UDNPawn.uc

class UDNPawn extends UTPawn;

var float CamOffsetDistance; //distance to offset the camera from the player
var int IsoCamAngle; //pitch angle of the camera

//override to make player mesh visible by default
simulated event BecomeViewTarget( PlayerController PC )
{
   local UTPlayerController UTPC;

   Super.BecomeViewTarget(PC);

   if (LocalPlayer(PC.Player) != None)
   {
      UTPC = UTPlayerController(PC);
      if (UTPC != None)
      {
         //set player controller to behind view and make mesh visible
         UTPC.SetBehindView(true);
         SetMeshVisibility(UTPC.bBehindView); 
         UTPC.bNoCrosshair = true;
      }
   }
}

simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   out_CamLoc = Location;
   out_CamLoc.X -= Cos(IsoCamAngle * UnrRotToRad) * CamOffsetDistance;
   out_CamLoc.Z += Sin(IsoCamAngle * UnrRotToRad) * CamOffsetDistance;

   out_CamRot.Pitch = -1 * IsoCamAngle;   
   out_CamRot.Yaw = 0;
   out_CamRot.Roll = 0;

   return true;
}

simulated singular event Rotator GetBaseAimRotation()
{
   local rotator   POVRot, tempRot;

   tempRot = Rotation;
   tempRot.Pitch = 0;
   SetRotation(tempRot);
   POVRot = Rotation;
   POVRot.Pitch = 0;

   return POVRot;
}   

defaultproperties
{
   IsoCamAngle=6420 //35.264 degrees
   CamOffsetDistance=384.0
}

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;

   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      if( Pawn == None )
      {
         return;
      }

      if (Role == ROLE_Authority)
      {
         // Update ViewPitch for remote clients
         Pawn.SetRemoteViewPitch( Rotation.Pitch );
      }

      Pawn.Acceleration = NewAccel;

      CheckJumpOrDuck();
   }
}

function UpdateRotation( float DeltaTime )
{
   local Rotator   DeltaRot, newRotation, ViewRotation;

   ViewRotation = Rotation;
   if (Pawn!=none)
   {
      Pawn.SetDesiredRotation(ViewRotation);
   }

   // Calculate Delta to be applied on ViewRotation
   DeltaRot.Yaw   = PlayerInput.aTurn;
   DeltaRot.Pitch   = 0;

   ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
   SetRotation(ViewRotation);

   NewRotation = ViewRotation;
   NewRotation.Roll = Rotation.Roll;

   if ( Pawn != None )
      Pawn.FaceRotation(NewRotation, deltatime);
}   

defaultproperties
{
}

Example Side-Scrolling Camera

A simple side-scroller camera requires not only controlling the camera’s point of view, but also modifying the way player input is handled. The player is only allowed to move left and right on the screen and is always forced to face the direction they are moving. The input is required so that the A and D keys move the player forward or backward.

camera_side.jpg

UDNPawn.uc

class UDNPawn extends UTPawn;

var float CamOffsetDistance; //Position on Y-axis to lock camera to

//override to make player mesh visible by default
simulated event BecomeViewTarget( PlayerController PC )
{
   local UTPlayerController UTPC;

   Super.BecomeViewTarget(PC);

   if (LocalPlayer(PC.Player) != None)
   {
      UTPC = UTPlayerController(PC);
      if (UTPC != None)
      {
         //set player controller to behind view and make mesh visible
         UTPC.SetBehindView(true);
         SetMeshVisibility(UTPC.bBehindView);
         UTPC.bNoCrosshair = true;
      }
   }
}

simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   out_CamLoc = Location;
   out_CamLoc.Y = CamOffsetDistance;

   out_CamRot.Pitch = 0;
   out_CamRot.Yaw = 16384;
   out_CamRot.Roll = 0;
   return true;
}

simulated singular event Rotator GetBaseAimRotation()
{
   local rotator   POVRot;

   POVRot = Rotation;
   if( (Rotation.Yaw % 65535 > 16384 && Rotation.Yaw % 65535 < 49560) ||
      (Rotation.Yaw % 65535 < -16384 && Rotation.Yaw % 65535 > -49560) )
   {
      POVRot.Yaw = 32768;
   }
   else
   {
      POVRot.Yaw = 0;
   }
   
   if( POVRot.Pitch == 0 )
   {
      POVRot.Pitch = RemoteViewPitch << 8;
   }

   return POVRot;
}   

defaultproperties
{
   CamOffsetDistance=0.0
}

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;

   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      local Rotator tempRot;

      if( Pawn == None )
      {
         return;
      }

      if (Role == ROLE_Authority)
      {
         // Update ViewPitch for remote clients
         Pawn.SetRemoteViewPitch( Rotation.Pitch );
      }

      Pawn.Acceleration.X = -1 * PlayerInput.aStrafe * DeltaTime * 100 * PlayerInput.MoveForwardSpeed;
      Pawn.Acceleration.Y = 0;
      Pawn.Acceleration.Z = 0;
      
      tempRot.Pitch = Pawn.Rotation.Pitch;
      tempRot.Roll = 0;
      if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) > 0)
      {
         tempRot.Yaw = 0;
         Pawn.SetRotation(tempRot);
      }
      else if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) < 0)
      {
         tempRot.Yaw = 32768;
         Pawn.SetRotation(tempRot);
      }

      CheckJumpOrDuck();
   }
}

function UpdateRotation( float DeltaTime )
{
   local Rotator   DeltaRot, ViewRotation;

   ViewRotation = Rotation;

   // Calculate Delta to be applied on ViewRotation
   DeltaRot.Yaw = Pawn.Rotation.Yaw;
   DeltaRot.Pitch   = PlayerInput.aLookUp;

   ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
   SetRotation(ViewRotation);
}   

defaultproperties
{
}

Example All-In-One Camera

This example puts together all the other examples into a single implementation that allows the player to switch between any of the camera types and adjust them through the use of exec functions.

UDNPawn.uc

class UDNPawn extends UTPawn;

Enum CameraPerspective
{
   CAM_FirstPerson,
   CAM_ThirdPerson,
   CAM_TopDown,
   CAM_SideScroller,
   CAM_Isometric
};

var bool bFollowPlayerRotation;
var CameraPerspective CameraType;
var float CamOffsetDistance;
var int IsoCamAngle;

exec function CameraMode(CameraPerspective mode)
{
   local UTPlayerController UTPC;

   CameraType = mode;

   UTPC = UTPlayerController(Controller);
   if (UTPC != None)
   {
      if(CameraType != CAM_FirstPerson)
      {
         UTPC.SetBehindView(true);
         if(CameraType != CAM_ThirdPerson)
         {
            UTPC.bNoCrosshair = true;
         }
         else
         {
            UTPC.bNoCrosshair = false;
         }
      }
      else
      {
         UTPC.bNoCrosshair = false;

         UTPC.SetBehindView(false);
      }
      SetMeshVisibility(UTPC.bBehindView);
   }
}

exec function IsoAngle(int angle)
{
   IsoCamAngle = angle;
}

/* BecomeViewTarget
   Called by Camera when this actor becomes its ViewTarget */
simulated event BecomeViewTarget( PlayerController PC )
{
   local UTPlayerController UTPC;

   Super.BecomeViewTarget(PC);

   if (LocalPlayer(PC.Player) != None)
   {
      UTPC = UTPlayerController(PC);
      if (UTPC != None)
      {
         if(CameraType != CAM_FirstPerson)
         {
            UTPC.SetBehindView(true);
            if(CameraType != CAM_ThirdPerson)
            {
               UTPC.bNoCrosshair = true;
            }
            else
            {
               UTPC.bNoCrosshair = false;
            }
         }
         else
         {
            UTPC.bNoCrosshair = false;

            UTPC.SetBehindView(false);
         }
         SetMeshVisibility(UTPC.bBehindView);
      }
   }
}

/**
 *   Calculate camera view point, when viewing this pawn.
 *
 * @param   fDeltaTime   delta time seconds since last update
 * @param   out_CamLoc   Camera Location
 * @param   out_CamRot   Camera Rotation
 * @param   out_FOV      Field of View
 *
 * @return   true if Pawn should provide the camera point of view.
 */
simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   // Handle the fixed camera

   if (bFixedView)
   {
      out_CamLoc = FixedViewLoc;
      out_CamRot = FixedViewRot;
   }
   else
   {
      if ( CameraType == CAM_ThirdPerson )   // Handle BehindView
      {
         CalcThirdPersonCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV);
      }
      else if ( CameraType == CAM_TopDown )   // Handle BehindView
      {
         CalcTopDownCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV);
      }
      else if ( CameraType == CAM_SideScroller )   // Handle BehindView
      {
         CalcSideScrollerCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV);
      }
      else if ( CameraType == CAM_Isometric )   // Handle BehindView
      {
         CalcIsometricCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV);
      }
      else
      {
         // By default, we view through the Pawn's eyes..
         GetActorEyesViewPoint( out_CamLoc, out_CamRot );
      }

      if ( UTWeapon(Weapon) != none)
      {
         UTWeapon(Weapon).WeaponCalcCamera(fDeltaTime, out_CamLoc, out_CamRot);
      }
   }

   return true;
}

simulated function bool CalcTopDownCam( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   out_CamLoc = Location;
   out_CamLoc.Z += CamOffsetDistance;

   if(!bFollowPlayerRotation)
   {
      out_CamRot.Pitch = -16384;
      out_CamRot.Yaw = 0;
      out_CamRot.Roll = 0;
   }
   else
   {
      out_CamRot.Pitch = -16384;
      out_CamRot.Yaw = Rotation.Yaw;
      out_CamRot.Roll = 0;
   }

   return true;
}

simulated function bool CalcSideScrollerCam( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   out_CamLoc = Location;
   out_CamLoc.Y = CamOffsetDistance;

   out_CamRot.Pitch = 0;
   out_CamRot.Yaw = 16384;
   out_CamRot.Roll = 0;

   return true;
}

simulated function bool CalcIsometricCam( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   out_CamLoc = Location; 
   out_CamLoc.X -= Cos(IsoCamAngle * UnrRotToRad) * CamOffsetDistance;
   out_CamLoc.Z += Sin(IsoCamAngle * UnrRotToRad) * CamOffsetDistance;

   out_CamRot.Pitch = -1 * IsoCamAngle;   
   out_CamRot.Yaw = 0;
   out_CamRot.Roll = 0;

   return true;
}

/**
 * returns base Aim Rotation without any adjustment (no aim error, no autolock, no adhesion.. just clean initial aim rotation!)
 *
 * @return   base Aim rotation.
 */
simulated singular event Rotator GetBaseAimRotation()
{
   local vector   POVLoc;
   local rotator   POVRot, tempRot;

   if(CameraType == CAM_TopDown || CameraType == CAM_Isometric)
   {
      tempRot = Rotation;
      tempRot.Pitch = 0;
      SetRotation(tempRot);
      POVRot = Rotation;
      POVRot.Pitch = 0;
   }
   else if(CameraType == CAM_SideScroller)
   {
      POVRot = Rotation;
      if( (Rotation.Yaw % 65535 > 16384 && Rotation.Yaw % 65535 < 49560) ||
          (Rotation.Yaw % 65535 < -16384 && Rotation.Yaw % 65535 > -49560) )
      {
         POVRot.Yaw = 32768;
      }
      else
      {
         POVRot.Yaw = 0;
      }
         
      if( POVRot.Pitch == 0 )
      {
         POVRot.Pitch = RemoteViewPitch << 8;
      }
   }
   else
   {
      if( Controller != None && !InFreeCam() )
      {
         Controller.GetPlayerViewPoint(POVLoc, POVRot);
         return POVRot;
      }
      else
      {
         POVRot = Rotation;
         
         if( POVRot.Pitch == 0 )
         {
            POVRot.Pitch = RemoteViewPitch << 8;
         }
      }
   }

   return POVRot;
}


defaultproperties
{
   CameraType=CAM_FirstPerson;
   bFollowPlayerRotation = false;
   CamOffsetDistance=384.0
   IsoCamAngle=6420 //35.264 degrees
}

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

state PlayerWalking
{
   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      local UDNPawn P;
      local Rotator tempRot;

          if( (Pawn != None) )
      {
         P = UDNPawn(Pawn);
         if(P != none)
         {
            if(P.CameraType == CAM_SideScroller)
            {
               Pawn.Acceleration.X = -1 * PlayerInput.aStrafe * DeltaTime * 100 * PlayerInput.MoveForwardSpeed;
               Pawn.Acceleration.Y = 0;
               Pawn.Acceleration.Z = 0;
               
               tempRot.Pitch = P.Rotation.Pitch;
               tempRot.Roll = 0;
               if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) > 0)
               {
                  tempRot.Yaw = 0;
                  P.SetRotation(tempRot);
               }
               else if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) < 0)
               {
                  tempRot.Yaw = 32768;
                  P.SetRotation(tempRot);
               }
            }
            else
            {

               if ( (DoubleClickMove == DCLICK_Active) && (Pawn.Physics == PHYS_Falling) )
                  DoubleClickDir = DCLICK_Active;
               else if ( (DoubleClickMove != DCLICK_None) && (DoubleClickMove < DCLICK_Active) )
               {
                  if ( UTPawn(Pawn).Dodge(DoubleClickMove) )
                     DoubleClickDir = DCLICK_Active;
               }
               
               Pawn.Acceleration = newAccel;
            }

            if (Role == ROLE_Authority)
            {
               // Update ViewPitch for remote clients
               Pawn.SetRemoteViewPitch( Rotation.Pitch );
            }
         }

         CheckJumpOrDuck();
      }
   }
}

function UpdateRotation( float DeltaTime )
{
   local UDNPawn P;
   local Rotator   DeltaRot, newRotation, ViewRotation;

   P = UDNPawn(Pawn);

   ViewRotation = Rotation;
   if (p != none && P.CameraType != CAM_SideScroller)
   {
      Pawn.SetDesiredRotation(ViewRotation);
   }

   // Calculate Delta to be applied on ViewRotation
   if( P != none && P.CameraType == CAM_SideScroller )
   {
      DeltaRot.Yaw = Pawn.Rotation.Yaw;
   }
   else
   {
      DeltaRot.Yaw = PlayerInput.aTurn;
   }
   DeltaRot.Pitch = PlayerInput.aLookUp; 

   ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
   SetRotation(ViewRotation);

   ViewShake( deltaTime );

   NewRotation = ViewRotation;
   NewRotation.Roll = Rotation.Roll;

   if (P != None && P.CameraType != CAM_SideScroller )
      Pawn.FaceRotation(NewRotation, deltatime);
}

defaultproperties
{
}

Example - Custom Camera


This example takes a different approach than the previous collection. Those all made use of the CalcCamera() function in the Pawn class to modify the camera. In this example, a custom camera framework will be created that allows for easily plugging in different camera modules to quickly modify the camera behavior. Just as importantly, we have complete access to all of the camera features, such as post process effects, camera animations and effects, etc. In addition, the player controller class will add the ability to use player control modules to override certain aspects of the player movement and aiming.

The setup for this example is a little more involved than the relatively simple overriding of a few functions, as was the case in the other examples. This example requires overriding many of the same functions that were touched in the previous examples, but instead of adding or changing the functionality directly in those functions, those functions will simply look to the custom camera and control system to provide the functionality they usually perform.

The new camera class will essentially act as an interface to the camera modules. It contains a few functions for creating new camera modules and handling the case of the camera having a view target other than the player's Pawn. Its job beyond that is to pass on function calls from various Engine classes to the current camera module to handle. Similarly, the new PlayerController class references a controle module that is relied on to handle certain movement and aiming functions from the PlayerController and Pawn classes. This allows the camera and control type-specific functionality to all be contained in compact, self-contained classes which can be substituted at will to quickly and easily implement new camera types.

Base Camera Module

The base camera module class extends from the Object class and defines all the properties and behavior that will be common to all camera modules. Its one property is a reference to the camera that owns it. Some initialization and deinitialization functions are defined, but the main functionality in this class is performed by the UpdateCamera() function which calculates the new location and rotation of the player's camera and can apply any other desired effects or modifications.

This class is defined using the config(Camera) specifier so that any properties that should be configurable or persistent in the specific camera modules will be found in the *Camera.ini file. It is also defined as abstract so that it cannot actually be used. It is more like a template for the specific camera modules to build from and not a class that would ever actually be used itself.

UDNCameraModule.uc

class UDNCameraModule extends Object
   abstract
   config(Camera);

//owning camera
var transient UDNPlayerCamera   PlayerCamera;

//mode-specific initialization
function Init();

/** Called when the camera becomes active */
function OnBecomeActive( UDNCameraModule OldCamera );
/** Called when the camera becomes inactive */
function OnBecomeInActive( UDNCameraModule NewCamera );

//Calculate new camera location and rotation
function UpdateCamera(Pawn P, UDNPlayerCamera CameraActor, float DeltaTime, out TViewTarget OutVT);

//initialize new view target
simulated function BecomeViewTarget( UDNPlayerController PC );

//handle zooming in
function ZoomIn();

//handle zooming in
function ZoomOut();

defaultproperties
{
}

Custom Camera

The new camera class extends from the base Camera class, overriding some functions and adding new functionality to handle the camera modules. The camera's main job in this system is to act as the middleman for the camera module as it is now handling the majority of the calculations.

UDNPlayerCamera.uc

class UDNPlayerCamera extends Camera
   config(Camera);

var UDNPlayerController PlayerOwner; //player controller owning this camera
var UDNCameraModule CurrentCamera; //Current camera mode in use
var config string DefaultCameraClass; //class for default camera mdoe

function PostBeginPlay()
{
   local class<UDNCameraModule> NewClass;

   Super.PostBeginPlay();

   // Setup camera mode
   if ( (CurrentCamera == None) && (DefaultCameraClass != "") )
   {
      //get the default camera class to use
      NewClass = class<UDNCameraModule>( DynamicLoadObject( DefaultCameraClass, class'Class' ) );
      
      //create default camera
      CurrentCamera = CreateCamera(NewClass);
   }
}

//Initialize the PlayerCamera for the owning PlayerController
function InitializeFor(PlayerController PC)
{
   //do parent initialization
   Super.InitializeFor(PC);

   //set PlayerOwner to player controller
   PlayerOwner = UDNPlayerController(PC);
}

/**
 * Internal. Creates and initializes a new camera of the specified class, returns the object ref.
 */
function UDNCameraModule CreateCamera(class<UDNCameraModule> CameraClass)
{
   local UDNCameraModule NewCam;

   //create new camera and initialize
   NewCam = new(Outer) CameraClass;
   NewCam.PlayerCamera = self;
   NewCam.Init();

   //call active/inactive functions on new/old cameras
   if(CurrentCamera != none)
   {
      CurrentCamera.OnBecomeInactive(NewCam);
      NewCam.OnBecomeActive(CurrentCamera);
   }
   else
   {
      NewCam.OnBecomeActive(None);
   }

   //set new camera as current
   CurrentCamera = NewCam;

   return NewCam;
}

/**
 * Query ViewTarget and outputs Point Of View.
 *
 * @param   OutVT      ViewTarget to use.
 * @param   DeltaTime   Delta Time since last camera update (in seconds).
 */
function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime)
{
   local CameraActor   CamActor;
   local TPOV OrigPOV;
   local Vector Loc, Pos, HitLocation, HitNormal;
   local Rotator Rot;
   local Actor HitActor;

   // Don't update outgoing viewtarget during an interpolation
   if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing )
   {
      return;
   }

   OrigPOV = OutVT.POV;

   // Default FOV on viewtarget
   OutVT.POV.FOV = DefaultFOV;

   // Viewing through a camera actor.
   CamActor = CameraActor(OutVT.Target);
   if( CamActor != None )
   {
      CamActor.GetCameraView(DeltaTime, OutVT.POV);

      // Grab aspect ratio from the CameraActor.
      bConstrainAspectRatio   = bConstrainAspectRatio || CamActor.bConstrainAspectRatio;
      OutVT.AspectRatio      = CamActor.AspectRatio;

      // See if the CameraActor wants to override the PostProcess settings used.
      CamOverridePostProcessAlpha = CamActor.CamOverridePostProcessAlpha;
      CamPostProcessSettings = CamActor.CamOverridePostProcess;
   }
   else
   {
      // Give Pawn Viewtarget a chance to dictate the camera position.
      // If Pawn doesn't override the camera view, then we proceed with our own defaults
      if( Pawn(OutVT.Target) == None ||
         !Pawn(OutVT.Target).CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) )
      {
         //Pawn didn't want control and we have a custom mode
         if(CurrentCamera != none)
         {
            //allow mode to handle camera update
            CurrentCamera.UpdateCamera(Pawn(OutVT.Target), self, DeltaTime, OutVT);
         }
         //no custom mode - use default camera styles
         else
         {
            switch( CameraStyle )
            {
               case 'Fixed'      :   // do not update, keep previous camera position by restoring
                                 // saved POV, in case CalcCamera changes it but still returns false
                                 OutVT.POV = OrigPOV;
                                 break;
   
               case 'ThirdPerson'   : // Simple third person view implementation
               case 'FreeCam'      :
               case 'FreeCam_Default':
                                 Loc = OutVT.Target.Location;
                                 Rot = OutVT.Target.Rotation;
   
                                 //OutVT.Target.GetActorEyesViewPoint(Loc, Rot);
                                 if( CameraStyle == 'FreeCam' || CameraStyle == 'FreeCam_Default' )
                                 {
                                    Rot = PCOwner.Rotation;
                                 }
                                 Loc += FreeCamOffset >> Rot;
   
                                 Pos = Loc - Vector(Rot) * FreeCamDistance;
                                 // @fixme, respect BlockingVolume.bBlockCamera=false
                                 HitActor = Trace(HitLocation, HitNormal, Pos, Loc, FALSE, vect(12,12,12));
                                 OutVT.POV.Location = (HitActor == None) ? Pos : HitLocation;
                                 OutVT.POV.Rotation = Rot;
                                 break;
   
               case 'FirstPerson'   : // Simple first person, view through viewtarget's 'eyes'
               default            :   OutVT.Target.GetActorEyesViewPoint(OutVT.POV.Location, OutVT.POV.Rotation);
                                 break;
   
            }
         }
      }
   }

   ApplyCameraModifiers(DeltaTime, OutVT.POV);

   // set camera's location and rotation, to handle cases where we are not locked to view target
   SetRotation(OutVT.POV.Rotation);
   SetLocation(OutVT.POV.Location);
}

//pass view target initialization through to camera mode
simulated function BecomeViewTarget( PlayerController PC )
{
   CurrentCamera.BecomeViewTarget(UDNPlayerController(PC));
}

//pass zoom in through to camera mode
function ZoomIn()
{
   CurrentCamera.ZoomIn();
}

//pass zoom out through to camera mode
function ZoomOut()
{
   CurrentCamera.ZoomOut();
}

defaultproperties
{
}

Base Control Module

The base control module class extends from the Object class and defines all the properties and behavior that will be common to all control modules. It contains a reference to the controller that owns it as well as holding the current mouse cursor position. Like the base camera module, some initialization and deinitialization functions are defined which allow for any type-specific setup or cleanup that may be necessary. The rest of the class is made up of the functions that will handle the player movement and aiming as well.

This class is defined using the config(Control) specifier so that any properties that should be configurable or persistent in the specific control modules will be found in the *Control.ini file. It is also defined as abstract so that it cannot actually be used. It nothing more than a template for the specific control modules to build from and not a class that would ever actually be used itself.

UDNControlModule.uc

class UDNControlModule extends Object
   abstract
   config(Control);
   
//reference to the owning controller
var UDNPlayerController Controller;

//mode-specific initialization
function Init();

/** Called when the camera becomes active */
function OnBecomeActive( UDNControlModule OldModule );
/** Called when the camera becomes inactive */
function OnBecomeInActive( UDNControlModule NewModule );

//Calculate Pawn aim rotation
simulated singular function Rotator GetBaseAimRotation();

//Handle custom player movement
function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot);

//Calculate controller rotation
function UpdateRotation(float DeltaTime);

defaultproperties
{
}

Engine Class Overrides

Several engine classes need to be extended to be able to interface with the new camera and control system, mainly the PlayerController, Pawn, and HUD classes. A new gametype will aslo be created ot make use of these new classes

PlayerController

The new PlayerController class adds exec functions for changing the type of camera module being used as well as zooming in or out (how the zoom functions work is dependent on how the current camera module implements them). The ProcessMove() function of the PlayerWalking state and the UpdateRotation() functions are overridden to add calls to the control module. Finally, the GetPlayerViewPoint() function is overridden and modified to keep the camera from being destroyed and to force the PlayerController to use the new custom camera if it exists.

UDNPlayerController.uc

class UDNPlayerController extends UTPlayerController;

var UDNControlModule ControlModule; //player control module to use
var config string DefaultControlModuleClass; //default class for player control module

//exec function for switching to a different camera by class
exec function ChangeControls( string ClassName )
{
   local class<UDNControlModule> ControlClass;
   local UDNControlModule NewControlModule;

   ControlClass = class<UDNControlModule>( DynamicLoadObject( DefaultControlModuleClass, class'Class' ) );
   
   if(ControlClass != none)
   {
      // Associate module with PlayerController
      NewControlModule = new(Outer) ControlClass;
      NewControlModule.Controller = self;
      NewControlModule.Init();

      //call active/inactive functions on new/old modules
      if(ControlModule != none)
      {
         ControlModule.OnBecomeInactive(NewControlModule);
         NewControlModule.OnBecomeActive(ControlModule);
      }
      else
      {
         NewControlModule.OnBecomeActive(None);
      }

      ControlModule = NewControlModule;
   }
   else
   {
      `log("Couldn't get control module class!");
      // not having a Control Class is fine.  PlayerController will use default controls.
   }
}

//exec function for switching to a different camera by class
exec function ChangeCamera( string ClassName )
{
   local class<UDNCameraModule> NewClass;

   NewClass = class<UDNCameraModule>( DynamicLoadObject( ClassName, class'Class' ) );

   if(NewClass != none && UDNPlayerCamera(PlayerCamera) != none)
   {
      UDNPlayerCamera(PlayerCamera).CreateCamera(NewClass);
   }
}

//zoom in exec
exec function ZoomIn()
{
   if(UDNPlayerCamera(PlayerCamera) != none)
   {
      UDNPlayerCamera(PlayerCamera).ZoomIn();
   }
}

//zoom out exec
exec function ZoomOut()
{
   if(UDNPlayerCamera(PlayerCamera) != none)
   {
      UDNPlayerCamera(PlayerCamera).ZoomOut();
   }
}

simulated function PostBeginPlay()
{
   local class<UDNControlModule> ControlClass;
   local UDNControlModule NewControlModule;

   Super.PostBeginPlay();

   ControlClass = class<UDNControlModule>( DynamicLoadObject( DefaultControlModuleClass, class'Class' ) );
   
   if(ControlClass != none)
   {
      // Associate module with PlayerController
      NewControlModule = new(Outer) ControlClass;
      NewControlModule.Controller = self;
      NewControlModule.Init();

      //call active/inactive functions on new/old modules
      if(ControlModule != none)
      {
         ControlModule.OnBecomeInactive(NewControlModule);
         NewControlModule.OnBecomeActive(ControlModule);
      }
      else
      {
         NewControlModule.OnBecomeActive(None);
      }

      ControlModule = NewControlModule;
   }
   else
   {
      `log("Couldn't get control module class!");
      // not having a Control Class is fine.  PlayerController will use default controls.
   }
}

state PlayerWalking
{
   function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
   {
      //Controller has a UDNPlayerCamera
      if(ControlModule != none)
      {
         //allow custom camera to override player movement
         ControlModule.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot);
      }
        else
        {
         Super.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot);
        }
      }
}

function UpdateRotation( float DeltaTime )
{
   //Controller has a UDNPlayerCamera
   if(ControlModule != none)
   {
      //allow custom camera to update our rotation
      ControlModule.UpdateRotation(DeltaTime);
   }
    else
    {
         Super.UpdateRotation(DeltaTime);
   }
}


/* GetPlayerViewPoint: Returns Player's Point of View
   For the AI this means the Pawn's Eyes ViewPoint
   For a Human player, this means the Camera's ViewPoint */
simulated event GetPlayerViewPoint( out vector POVLocation, out Rotator POVRotation )
{
   local float DeltaTime;
   local UTPawn P;

   P = IsLocalPlayerController() ? UTPawn(CalcViewActor) : None;

   DeltaTime = WorldInfo.TimeSeconds - LastCameraTimeStamp;
   LastCameraTimeStamp = WorldInfo.TimeSeconds;

   // support for using CameraActor views
   if ( CameraActor(ViewTarget) != None )
   {
      if ( PlayerCamera == None )
      {
         super.ResetCameraMode();
         SpawnCamera();
      }
      super.GetPlayerViewPoint( POVLocation, POVRotation );
   }
   else
   {
      //do not destroy our camera!!!
      /* if ( PlayerCamera != None )
      {
         PlayerCamera.Destroy();
         PlayerCamera = None;
      } */

      //no camera, we have view target - let view target be in control
      if ( PlayerCamera == None && ViewTarget != None )
      {
         POVRotation = Rotation;
         if ( (PlayerReplicationInfo != None) && PlayerReplicationInfo.bOnlySpectator && (UTVehicle(ViewTarget) != None) )
         {
            UTVehicle(ViewTarget).bSpectatedView = true;
            ViewTarget.CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle );
            UTVehicle(ViewTarget).bSpectatedView = false;
         }
         else
         {
            ViewTarget.CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle );
         }

         if ( bFreeCamera )
         {
            POVRotation = Rotation;
         }
      }
      //no camera, no view target - we are in control
      else if(PlayerCamera == None)
      {
         CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle );
         return;
      }
      //we have a camera - let camera be in control
      else
      {
         POVLocation = PlayerCamera.ViewTarget.POV.Location;
         POVRotation = PlayerCamera.ViewTarget.POV.Rotation;
         FOVAngle = PlayerCamera.ViewTarget.POV.FOV;
      }
   }

   // apply view shake
   POVRotation = Normalize(POVRotation + ShakeRot);
   POVLocation += ShakeOffset >> Rotation;

   if( CameraEffect != none )
   {
      CameraEffect.UpdateLocation(POVLocation, POVRotation, GetFOVAngle());
   }


   // cache result
   CalcViewActor = ViewTarget;
   CalcViewActorLocation = ViewTarget.Location;
   CalcViewActorRotation = ViewTarget.Rotation;
   CalcViewLocation = POVLocation;
   CalcViewRotation = POVRotation;

   if ( P != None )
   {
      CalcEyeHeight = P.EyeHeight;
      CalcWalkBob = P.WalkBob;
   }
}

defaultproperties
{
   CameraClass=class'UDNExamples.UDNPlayerCamera'
   MatineeCameraClass=class'UDNExamples.UDNPlayerCamera'
}

Pawn Class

The new Pawn class overrides the CalcCamera() function to simply return false, allowing the new camera system to always control the camera location and position. The BecomeViewTarget() and GetBaseAimRotation() functions are overridden to pass the handling of their functionality on to the camera and control system, respectively.

UDNPawn.uc

class UDNPawn extends UTPawn;

/* BecomeViewTarget
   Called by Camera when this actor becomes its ViewTarget */
simulated event BecomeViewTarget( PlayerController PC )
{
   local UDNPlayerController UDNPC;

   UDNPC = UDNPlayerController(PC);

   //Pawn is controlled by a UDNPlayerController and has a UDNPlayerCamera
      if(UDNPC != none && UDNPlayerCamera(UDNPC.PlayerCamera) != none)
      {
      //allow custom camera to control mesh visibility, etc.
      UDNPlayerCamera(UDNPC.PlayerCamera).BecomeViewTarget(UDNPC);
      }
      else
      {
      Super.BecomeViewTarget(PC);
   }
}

/**
 *   Calculate camera view point, when viewing this pawn.
 *
 * @param   fDeltaTime   delta time seconds since last update
 * @param   out_CamLoc   Camera Location
 * @param   out_CamRot   Camera Rotation
 * @param   out_FOV      Field of View
 *
 * @return   true if Pawn should provide the camera point of view.
 */
simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
   //return false to allow custom camera to control its location and rotation
      return false;
}

/**
 * returns base Aim Rotation without any adjustment (no aim error, no autolock, no adhesion.. just clean initial aim rotation!)
 *
 * @return   base Aim rotation.
 */
simulated singular event Rotator GetBaseAimRotation()
{
      local vector   POVLoc;
      local rotator   POVRot;
      local UDNPlayerController PC;

   PC = UDNPlayerController(Controller);

   //Pawn is controlled by a UDNPlayerController and has a UDNPlayerCamera
      if(PC != none && PC.ControlModule != none)
      {
      //allow custom camera to control aim rotation
      return PC.ControlModule.GetBaseAimRotation();
      }
      else
      {
         if( Controller != None && !InFreeCam() )
         {
            Controller.GetPlayerViewPoint(POVLoc, POVRot);
            return POVRot;
         }
         else
         {
            POVRot = Rotation;

            if( POVRot.Pitch == 0 )
            {
               POVRot.Pitch = RemoteViewPitch << 8;
            }

            return POVRot;
         }
      }
}


defaultproperties
{
}

GameInfo Class

The new gametype class is a basic extension of the UTDeathMatch class which sets the new HUD, Pawn, and PlayerController classes to use. It also sets bUseClassicHUD to True so that the HUD class specified here will be used instead of the UTGFxHUDWrapper, which is hardcoded to be used unless this boolean is set.

UDNGame.uc

class UDNGame extends UTDeathMatch;

defaultproperties
{
   DefaultPawnClass=class'UDNExamples.UDNPawn'
   PlayerControllerClass=class'UDNExamples.UDNPlayerController'
   MapPrefixes[0]="UDN"
}

Example Camera Module

As an example of using the new camera framework, a Top-Down camera will be set up. Making a new camera module is mainly a matter of implementing the functions defined in the base camera module class. Much of this will look familiar if you have gone through the CalcCamera() examples above.

UDNCameraModule_TopDown.uc

class UDNCameraModule_TopDown extends UDNCameraModule;

var float CamAltitude; //actual camera height offset from player
var float DesiredCamAltitude; //new height offset to move camera to
var float MaxCamAltitude; //maximum offset from player camera can be
var float MinCamAltitude; //minimum offset from player camera can be
var float CamZoomIncrement; //how many units to zoom with each click of mousewheel

//Calculate new camera location and rotation
function UpdateCamera(Pawn P, UDNPlayerCamera CameraActor, float DeltaTime, out TViewTarget OutVT)
{
   //interpolate to new camera offest if not there
   if(CamAltitude != DesiredCamAltitude)
   {
      CamAltitude += (DesiredCamAltitude - CamAltitude) * DeltaTime * 3;
   }

   //align camera to player with height (Z) offset
   OutVT.POV.Location = OutVT.Target.Location;
   OutVT.POV.Location.Z += CamAltitude;
   
   //set camera rotation - face down
   OutVT.POV.Rotation.Pitch = -16384;
   OutVT.POV.Rotation.Yaw = 0;
   OutVT.POV.Rotation.Roll = 0;
}

//initialize new view target
simulated function BecomeViewTarget( UDNPlayerController PC )
{
   if (LocalPlayer(PC.Player) != None)
      {
      //Set player mesh visible
        PC.SetBehindView(true);
        UDNPawn(PC.Pawn).SetMeshVisibility(PC.bBehindView);
        PC.bNoCrosshair = true;
      }
}

function ZoomIn()
{
   //decrease camera height
   DesiredCamAltitude -= CamZoomIncrement;
   
   //lock camera height to limits
   DesiredCamAltitude = FMin(MaxCamAltitude, FMax(MinCamAltitude, DesiredCamAltitude));
}

function ZoomOut()
{
   //increase camera height
   DesiredCamAltitude += CamZoomIncrement;

   //lock camera height to limits
   DesiredCamAltitude = FMin(MaxCamAltitude, FMax(MinCamAltitude, DesiredCamAltitude));
}

defaultproperties
{
   CamAltitude=384.0
   DesiredCamAltitude=384.0
   MaxCamAltitude=1024.0
   MinCamAltitude=160.0
   CamZoomIncrement=96.0
}

Example Control Module

UDNControlModule_TopDown.uc

class UDNControlModule_TopDown extends UDNControlModule;

//Calculate Pawn aim rotation
simulated singular function Rotator GetBaseAimRotation()
{
   local rotator   POVRot;

   //aim where Pawn is facing - lock pitch
      POVRot = Controller.Pawn.Rotation;
      POVRot.Pitch = 0;

      return POVRot;
}

//Handle custom player movement
function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
{
   if( Controller.Pawn == None )
    {
       return;
    }

    if (Controller.Role == ROLE_Authority)
    {
       // Update ViewPitch for remote clients
        Controller.Pawn.SetRemoteViewPitch( Controller.Rotation.Pitch );
    }

    Controller.Pawn.Acceleration = NewAccel;


   Controller.CheckJumpOrDuck();
}

//Calculate controller rotation
function UpdateRotation(float DeltaTime)
{
   local Rotator   DeltaRot, NewRotation, ViewRotation;

      ViewRotation = Controller.Rotation;

   //rotate pawn to face cursor
      if (Controller.Pawn!=none)
      Controller.Pawn.SetDesiredRotation(ViewRotation);

      DeltaRot.Yaw   = Controller.PlayerInput.aTurn;
      DeltaRot.Pitch   = 0;

      Controller.ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot );
      Controller.SetRotation(ViewRotation);

      NewRotation = ViewRotation;
      NewRotation.Roll = Controller.Rotation.Roll;

      if ( Controller.Pawn != None )
         Controller.Pawn.FaceRotation(NewRotation, DeltaTime);
}

defaultproperties
{
}

Config Files

All of the files below should be located in the in the UDKgame/Config directory. Since some may be new additions, you will need to create those. Others can simply be modified to include the new configuration settings..

The DefaultCamera.ini file should be populated with the values for the various config variables found in the new camera classes. For this example, this consists of setting a default camera module class only.

DefaultCamera.ini

[UDNExamples.UDNPlayerCamera]
DefaultCameraClass=UDNExamples.UDNCameraModule_TopDown

The DefaultGame.ini file should have the [Engine.GameInfo] section modified to point to the new gametype and it also needs a section added at the bottom to specify the default control module class to use.

DefaultGame.ini


...

[Engine.GameInfo]
DefaultGame=UDNExamples.UDNGame
DefaultServerGame=UDNExamples.UDNGame

...

[UDNExamples.UDNPlayerController]
DefaultControlModuleClass=UDNExamples.UDNControlModule_TopDown

Once these are created and/or populated or modified with the desired configuration settings, a new UDKCamera.ini and UDKGame.ini file will be created the next time the game or editor is run.

Note: In order for the new gametype to be used, you will need to make sure your maps have the correct prefix. We set the prefix to be "UDN" in our gametype so all maps will need to be named starting with "UDN-". It is also possible to quickly test the new gametype with any map in the editor by setting the Game Type PIE property in the World Properties for the map to the new gametype.