NOTE: This all assumes you already have a PlayerController and a Pawn with a skeletal mesh already set-up.
According to the UDK documentation here - http://udn.epicgames.com/Three/CameraTechnicalGuide.html, you can implement a camera in one of 2 ways.
The first and also quickest is to override your Pawn’s CalcCamera function and calculate the views rotation and location.
The second is by overriding the camera class which is said to be more flexible and allows you to have fancy things like camera animations and effects. This is what I did by sub-classing the Camera class.
Lets start by creating this sub-class. We need to figure out the camera's location.
1. Create a vector that points to the right of the characters position, this is basically a vector pointing down Y in world coordinates.
2. Offset this unit vector by multiplying it by the number of units we want. This moves the camera's position to the right of the character by said units.
3. Add any other offsets we want in the X and Z directions, in my case I want my camera slightly moved up.
This is achieved by defining a vector with these offsets - FollowCamDist which is initialised as (X=0.f,Y=400.f,Z=100.f)
Once we have this position, we need to rotate the camera to look at the character. This is done by getting the direction from the camera's position to the character's position like so:
CamDir = (OutVT.Target.Location - CamLocation);
Then converting this to a Rotator and assigning it as the camera's rotation.
And here is the complete camera class. The stuff discussed above is what is within the highlighted block. The rest of the function is picked from the base Camera class.
class DecadePlayerCamera extends Camera; var Vector FollowCamDist; /** * 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 vector HitLocation, HitNormal, CamDir, CamLocation; local Actor HitActor; local CameraActor CamActor; local TPOV OrigPOV; local Pawn TPawn; // Don't update outgoing viewtarget during an interpolation if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing ) { return; } // store previous POV, in case we need it later 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 { TPawn = Pawn(OutVT.Target); // 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( TPawn == None || !TPawn.CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) ) { /// make a unit vector that points down the Y/left axis seems that for the camrea, X is forward - perpediculr to pawn rotation CamDir = vect(0.f, 1.f, 0.f); /// displace vector by Z offset CamDir *= FollowCamDist.Y; /// offset direction by follow distance CamLocation = CamDir + OutVT.Target.Location; CamLocation.X += FollowCamDist.X; CamLocation.Z += FollowCamDist.Z; /// handle collisions @todo: fix player mesh intersection HitActor = Trace(HitLocation, HitNormal, CamLocation, OutVT.Target.Location, false, vect(12,12,12)); //if (HitActor != none) // CamLocation = HitLocation; /// set view position and location OutVT.POV.Location = CamLocation; /// get the new direction CamDir = (OutVT.Target.Location - CamLocation); OutVT.POV.Rotation = Rotator(CamDir); } } ApplyCameraModifiers(DeltaTime, OutVT.POV); //`log( WorldInfo.TimeSeconds @ GetFuncName() @ OutVT.Target @ OutVT.POV.Location @ OutVT.POV.Rotation @ OutVT.POV.FOV ); } defaultproperties { FollowCamDist=(X=0.f,Y=400.f,Z=100.f) }
Now lets look at how to make the character only walk along the X axis. Input is translated into movement in the PlayerWalking state's PlayerMove function.
... // Update acceleration. NewAccel = PlayerInput.aForward*X + PlayerInput.aStrafe*Y; NewAccel.Z = 0; NewAccel = Pawn.AccelRate * Normal(NewAccel); ...
What we want is to use the strafe input to move the player. We'll take the player's X direction and multiply it by the strafe input. Then we'll zero out the Y movement (Z is already zeroed out)
... // Update acceleration. NewAccel = PlayerInput.aForward*X; NewAccel.Z = 0; NewAccel.Y = 0; NewAccel = Pawn.AccelRate * Normal(NewAccel); ...
We also need to prevent the player from turning around the Z axis, this we do in the PlayerController's UpdateRotation function by removing the line that uses the aTurn input to update the Yaw rotation.
... DeltaRot.Yaw = PlayerInput.aTurn; // turning is not allowed ...
And here is the full code listing for the PlaeyrController class
NOTE: the character runs backwards but likely what we want is to have the player change direction when the A key or equivalent is used. I'm still working on this and will post an update when I can.
As always questions, comments and suggestions are welcome.
class DecadePlayerController extends AcventurePlayerController; 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 = 0; // turning is not allowed DeltaRot.Pitch = PlayerInput.aLookUp; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); ViewShake( deltaTime ); NewRotation = ViewRotation; NewRotation.Roll = Rotation.Roll; if ( Pawn != None ) Pawn.FaceRotation(NewRotation, deltatime); } // Player movement. // Player Standing, walking, running, falling. state PlayerWalking { ignores SeePlayer, HearNoise, Bump; function PlayerMove( float DeltaTime ) { local vector X,Y,Z, NewAccel; local eDoubleClickDir DoubleClickMove; local rotator OldRotation; local bool bSaveJump; if( Pawn == None ) { GotoState('Dead'); } else { GetAxes(Pawn.Rotation,X,Y,Z); // Update acceleration. //@TODO: use aStrafe * X and figure out how to change pawns direction NewAccel = PlayerInput.aStrafe * X; NewAccel.Z = 0; NewAccel.Y = 0; NewAccel = Pawn.AccelRate * Normal(NewAccel); if (IsLocalPlayerController()) { AdjustPlayerWalkingMoveAccel(NewAccel); } DoubleClickMove = PlayerInput.CheckForDoubleClickMove( DeltaTime/WorldInfo.TimeDilation ); // Update rotation. OldRotation = Rotation; UpdateRotation( DeltaTime ); bDoubleJump = false; if( bPressedJump && Pawn.CannotJumpNow() ) { bSaveJump = true; bPressedJump = false; } else { bSaveJump = false; } if( Role < ROLE_Authority ) // then save this move and replicate it { ReplicateMove(DeltaTime, NewAccel, DoubleClickMove, OldRotation - Rotation); } else { ProcessMove(DeltaTime, NewAccel, DoubleClickMove, OldRotation - Rotation); } bPressedJump = bSaveJump; } } Begin: } defaultproperties { CameraClass=class'SideScrollerCamera' }
No comments:
Post a Comment