| Home Page | Recent Changes | Preferences

Customising The Player View

Customising the Player View

Before we get into the real meat of how to fix the player views we need to understand the code that is already there. Grasping the link between the posessed pawn (stored in the Pawn attribute) and the current view target (held in the ViewTarget attribute) is essential to obtaining the desired effects. Before we go any further lets recap on a few of the attributes of the PlayerController.

Pawn
This is the Pawn that the player has possessed. A possessed pawn is controlled by the player. The pawn class occupied by the player makes callbacks to the PlayerController in use to determine what should happen when the player attempts to make the pawn perform an action (run forwards, change weapon etc).
ViewTarget
The view target is used to determine what the player sees. Nominally the view target is the actor displayed at the center of the current view. The most important thing to remember about the view target is that it does not have to be the same object at the possessed pawn.
Rotation
This is the rotation of the PlayerController class itself, as set by the SetRotation() function. This is invariably set by the PlayerMove() functions of the PlayerController class. This is used by the CalcBehindView() and CalcFirstPersonView() functions in the determination of the view rotation.

In order to set the player view to that of your chosing you will need to perform the following steps.

  1. Create a new game type class and reference your player controller within the PlayerControllerClassName property.
  2. Create a player controller class that will implement your shiny view.
  3. Override the PlayerCalcView() function in the player controller class to set the view you want.

Create a Game Type Class

Assuming that your new game type is going to be based on a deathmatch game, you could use the following code. The important thing to note is the mechanism by which the custom PlayerController class is referenced.

class ViewTestGame extends xDeathMatch;
defaultproperties {
     PlayerControllerClassName="ViewTest.ViewTestPlayerController" // This is the important line
}

Simply save the code as /UT2003/ViewTest/Classes/ViewTestGame.uc and you should be well away. You will also need to create a ViewTest.int file.

Creating the Player Controller Class

This is where the real work is done. Since we have already created our game class and named the player controller class we are going to create we had better keep the same name. The following code will allow us to play around.

class ViewTestPlayerController extends xGame.xPlayer;
var Actor myTarget;
var bool  targetIsMyTarget;

// The player view is determined by this function.
function event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation ) {
  Super.PlayerCalcView( ViewActor, CameraLocation, CameraRotation );
}

// Locates the dummy view target object set up on the map - in my case I used a blue flag decoration.
function PostBeginPlay() {
    local actor newTarget;
    Super.PostBeginPlay();
    foreach AllActors(class'Actor', newTarget, 'newView') {
        myTarget = newTarget;
        SetViewTarget( newTarget );
        break;
    }
}

// The following function allows us to switch between a view target of the player pawn and the dummy view target.
exec function ViewTest() {
    if ( targetIsMyTarget ) {
        SetViewTarget( myTarget );
    } else {
        SetViewTarget( Pawn );
    }
    targetIsMyTarget = !targetIsMyTarget;
}

Overriding the PlayerCalcView() Function

I'm not going to provide any code in here (well, maybe a bit) as setting the rotation and location of the view is as easy as setting the output variables CameraRotation and CameraLocation. I will say that the purpose of the ViewActor output variable is still a bit of a mystery - but it might be there to support setting the view the player sees when the player has died or other similar function. The following guidelines seem to hold true.

  • In order to fix the view rotation and still allow the possessed pawn to be controlled you can set the CameraRotation to a fixed value and set the ViewTarget to the possessed pawn.
  • It's generally better to use the Rotation property of the controller than either the Pawn.Rotation or ViewTarget.Rotation properties as this takes network issues into account.
  • The ViewTarget can be any object of type Actor.
  • The player can only possess Pawns
  • Explicitly set bBehindView to true to remove the floating player weapon (if it appears in thrid person view).

Related Pages

  • PlayerController – A discussion on Player Controllers and how to make them do what you want.

Questions and Discussion

EntropicLqd: I realise that there is some duplication between this page and the PlayerController page but I'll worry about that when I'm happy with the entire piece. The information overload sectino isn't quite complete as I've not finished commenting the code.

Information Overload Section

Here's a quick run down of some of the things I did while working out how it all worked. A lot of it is fairly redundant, but nonetheless was somewhat amusing.

A Quick Experiment

In order to explore the default behaviour of the ViewTarget and Pawn we shall created a small map with a single player start and a static decoration. I gave the static decoration a tag of newView and placed it near the player start, facing it. I then went on to create my own game type (called ViewTestGame), and my own player controller class. The code is fairly self explanatory and shown above.

OK, if you can be bothered to repeat the experiment (or even if you can) here's what you'll find.

Prior to the game starting you'll see that the view is centered on the decoration. This is because the PostBeingPlay() function sets the view target of the controller to be that of the decoration. However, once in the game you'll be playing as normal and you'll be able to run around the map in both first person and third person (using the behindview console command) views.

When viewing the player from the first person, position your player such that the decoration is "looking" at you. Then enter the command ViewTest at the console. You should see the view change and with luck you'll be able to see your player. If you can't see your player use the view test to toggle the view back to the first person perspectivew of your player and reposition yourself so you can be seen. You should notice three things.

  • You still have complete control over your player pawn, you can make him look up and down, run, jump, fire weapons, everything that you would normally be able to do.
  • Your view into the world is completely fixed in the direction the decoration happens to be facing. You cannot change the view at all (although altering the FOV will work).
  • Not only can you see your player pawn, but you can also see the weapon your player currently has selected floating next to the player. As the pawn moves around the weapon also moves around with it – sort of.

Now switch to the third person perspective by entering behindview 1 at the console. You should notice the following things.

  • Your view has pulled back slightly.
  • The floating weapon has vanished into thin air.
  • Your view now rotates as you look around with the mouse.
  • You still have full normal control over the player pawn.

It's all interesting stuff as it gives us a basis for working through the code and figuring out what's going on. The view the player sees is ultimately controlled by the PlayerCalcView() function. Control this and you can make them look anywhere you want to.

The Default Behaviour

So, lets examine the PlayerCalcView() function. I've reproduced it here in its entirety as there are a couple of things worth noting about it. I've taken the liberty of adding some additional comments to the code in order to cover the things I think are important.

event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation ) {
  local Pawn PTarget;

  // If desired, call the possessed pawn's own view calculation function.  If this function
  // calculates the view the player should see then it must return true.  Otherwise the
  // calculated view will be replaced by the usual view.
  if( Pawn != None && Pawn.bSpecialCalcView ) {
    // try the 'special' calcview. This may return false if its not applicable, and we do the usual.
    if( Pawn.SpecialCalcView(ViewActor, CameraLocation, CameraRotation) )
      return; // EXIT point from function.
  }

  // If the controller does not have a view target, or, it's view target is about to be removed from
  // the level then we need to get one.
  if ( (ViewTarget == None) || ViewTarget.bDeleteMe ) {
    if ( bViewBot && (CheatManager != None) )
      CheatManager.ViewBot();
    else if ( (Pawn != None) && !Pawn.bDeleteMe )
      SetViewTarget(Pawn);
    else
      SetViewTarget(self);
  }

  ViewActor = ViewTarget;               // Set the output variable ViewActor with the new view target
  CameraLocation = ViewTarget.Location; // Set the output variable CameraLocation with the view targets location

  // If the view target of the controller is the currently possessed pawn then the
  // calculation of the view the player sees is deferred to the first person and
  // behind view calculation functions.
  if ( ViewTarget == Pawn )
  {
    if( bBehindView ) //up and behind
      CalcBehindView(CameraLocation, CameraRotation, CameraDist * Pawn.Default.CollisionRadius);
    else
      CalcFirstPersonView( CameraLocation, CameraRotation );
    return; // EXIT point from function
  }

  // When all else fails the view target of the controller is itself.  If the camera
  // position is locked then a fixed rotation is used.  If not then the output variable
  // is set to the controller's rotation. 
  if ( ViewTarget == self ) {
    if ( bCameraPositionLocked )
      CameraRotation = CheatManager.LockedRotation;
    else
      CameraRotation = Rotation;
    return; // EXIT point from function
  }

  // If we are watching a projectile and we are in first person view then raise the camera
  // vertically upwards by the projectiles collision height.  The camera rotation is set to
  // the rotation of the controller.
  if ( ViewTarget.IsA('Projectile') && !bBehindView ) {
    CameraLocation += (ViewTarget.CollisionHeight) * vect(0,0,1);
    CameraRotation = Rotation;
    return; // EXIT point from function
  }

  // Set the camera rotation to the rotation of the current view target.
  CameraRotation = ViewTarget.Rotation;

  PTarget = Pawn(ViewTarget);
  if ( PTarget != None ) {
    // If the view target is a pawn, and we are a client of a networked
    // game then we need to do some additional processing
    if ( Level.NetMode == NM_Client ) {
      if ( PTarget.IsPlayerPawn() ) {
        // If we are looking at a player pawn then the view rotation
        // is set to the TargetViewRotation (how the TargetViewRotation
        // is set is a mystery).  The view target's rotation is also set
        // to this value.
        PTarget.SetViewRotation(TargetViewRotation);
        CameraRotation = TargetViewRotation;
      }

      // Adjust the view targets eye height.
      PTarget.EyeHeight = TargetEyeHeight;
      if ( PTarget.Weapon != None )
        PTarget.Weapon.PlayerViewOffset = TargetWeaponViewOffset;
    }
    else if ( PTarget.IsPlayerPawn() )
      CameraRotation = PTarget.GetViewRotation();

    // If we are not in behind view then the position the camera location
    // at the view target's eye height.
    if ( !bBehindView )
      CameraLocation += PTarget.EyePosition();
  }

  // This is the view calculations used to determine the "behind view" when
  // the View Target is not the same as the Possessed pawn.
  if ( bBehindView ) {
    CameraLocation = CameraLocation + (ViewTarget.Default.CollisionHeight - ViewTarget.CollisionHeight) * vect(0,0,1);
    CalcBehindView(CameraLocation, CameraRotation, CameraDist * ViewTarget.Default.CollisionRadius);
  }
}

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

Mapping Topics

Mapping Lessons

UnrealEd Interface

Questions&Answers

Scripting Topics

Scripting Lessons

Making Mods

Class Tree

Questions&Answers

Modeling Topics

Questions&Answers

Log In