UDN
Search public documentation:
CameraTechnicalGuideCH
English Translation
日本語訳
한국어
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
日本語訳
한국어
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
相机技术指南
概述
相机
- 通用
- PCOwner – 引用拥有这个相机的 PlayerController。
- CameraStyle – 该相机的当前相机模式。在视角没有被视图目标覆盖时,用于确定相机的类型(例如,第一人称、第三人称、自由等等)。
- ViewTarget – 定义视图目标的当前数据。类型是 TViewTarget。
- TViewTarget
- Target – 相机是“以下”的目标 actor。
- Controller – 如果是 Pawn,Target 的 Controller。
- POV – Target 的理想视角。类型是 TPOV。
- TPOV
- Location – 视角的位置。
- Rotation – 视点的旋转量。
- FOV – 视点的全视角角度。
- AspectRatio – 用于 Target 的屏幕长宽比。
- PRI – 通过 pawn 平移可以用于跟踪同一个玩家的玩家函数复制信息。
- TPOV
- FOV
- DefaultFOV – 相机的默认视角。
- bLockedFOV – 如果为真,相机的视角将会锁定为 LockedFOV 值。
- LockedFOV – 锁定 FOV 时使用的视角。
- 屏幕长宽比
- DefaultAspectRatio – 相机的默认长宽比。
- bConstrainAspectRatio – 如果为真,相机的屏幕长宽比将被限制为 ConstrainedAspectRatio 的值。
- ConstrainedAspectRatio – 在相机的屏幕长宽比受到限制时使用的长宽比。
- 通用
- UpdateCamera [DeltaTime] – 每帧调用一次对相机进行一次更新。
- DeltaTime – 自从上一次更新发生开始的时间长度。
- GetCameraViewPoint [OutCamLoc] [OutCamRot] – 会检索相机的位置和旋转。不可直接调用该函数。相反,应该调用 PCOwner 的 GetPlayerViewPoint() 函数。
- OutCamLoc – 会输出相机的位置。
- OutCamRot – 会输出相机的旋转量。
- ProcessViewRotation [DeltaTime] [OutViewRotation] [OutDeltaRot] – 由 PCOwner 调用,为相机提供一个可以改变这一帧的视图旋转变化量的机会。
- DeltaTime – 自从上一次更新发生开始的时间长度。
- OutViewRotation – 会输出相机的已调整视图旋转量。
- OutDeltaRot – 输出相机的已调整 delta 旋转量。
- UpdateCamera [DeltaTime] – 每帧调用一次对相机进行一次更新。
- FOV
- GetFOVAngle – 会返回相机的当前视角。
- SetFOV [NewFOV] – 会将相机的视角设置为 NewFOV 值。
- NewFOV – 为相机的视角设置的值。
- 视图目标
- SetViewTarget [NewViewTarget] [TransitionParams] – 设置相机的视图目标。
- NewViewTarget – 设置为相机新的视图目标的 Actor。
- TransitionParams – 混合参数以便可以在平移到新的视图目标时使用。
- UpdateViewTarget [OutVT] [DeltaTime] – 根据帧进行调用将视图目标更新为它的新位置、旋转量和 fov。 如果使用自定义相机(与视图目标 actor 中覆盖 CalcCamera() 相对),这是覆盖以便实现您希望的行为的关键函数。
- OutVT – 会输出一个含有相机的视图目标和视点的数据结构。
- DeltaTime – 自从上一次更新发生开始的时间长度。
- SetViewTarget [NewViewTarget] [TransitionParams] – 设置相机的视图目标。
后期特效
后期特效是在向玩家展示之前应用于已渲染的场景的效果。每个相机都可以使用它自己的后期处理设置的集合,可以覆盖世界、音量、或默认后期特效设置。 要了解更多有关后期特效的信息,请参阅 PostProcessEditorUserGuide(后期编辑器用户指南)、PostProcessTechnicalGuide(后期技术指南)以及 PostProcessMaterials(后期材质)。 后期特效属性- CameOverridePostProcessAlpha – 设置与世界、体积或默认后期特效设置相关的相机的后期特效设置的作用范围。值 0.0 表示世界、体积或默认后期特效都具有全部作用。值 1.0 表示相机的后期特效具有全部作用范围。
- CamPostProcessSettings – 当相机覆盖世界、体积或默认后期特效的时候使用的后期特效设置。
- bEnableColorScaling – 如果为真,最终图像中的颜色通道将会使用 ColorScale 值进行调整。
- ColorScale – 在最终图像中各个颜色通道调整向量。
- bEnableColorScaleInterp – 如果为真,相机将会在通过 SetDesiredColorScale() 函数设置的新颜色调整值时插入到颜色调整值之间。
- bEnableFading – 如果为真,相机会将 FadeColor 的 FadeAmount 应用到画面中。
- FadeColor – 相机淡入淡出时画面使用的颜色。
- FadeAmount – 使用的淡入淡出量。实质上就是淡入淡出的滤镜。
- SetDesiredColorScale [NewColorScale] [InterpTime] – 根据 bEnableColorScaleInterp 的值设置新的颜色调整值并有选择地插入。
- NewColorScale – 可以用于颜色调整的新值。
- InterpTime – 进入到新颜色调整值所需要的时间长度。
镜头特效
镜头特效是粒子特效,可以将其用于玩家的相机的镜头。这些镜头特效可以用于创建诸如相机镜头中下的雨滴、血溅、镜头上的污物或灰尘等等这一类的效果。这个 camera 类会包含可以使用这些特效类型的函数。 要了解有关粒子系统和特效的更多信息,请参阅ParticleSystemReference(粒子系统参考)。 镜头特效属性-
- CameraLensEffects – 当前应用于相机的所有粒子特效的数组。
- FindCameraLensEffect [LensEffectEmitterClass] – 会搜索当前应用于相机的镜头特效并返回任何与之匹配的类型。
- LensEffectEmitterType – 要搜索的镜头特效的类。
- AddCameraLensEffect [LensEffectEmitterClass] – 会将指定类型的新镜头特效应用于相机中。
- LensEffectEmitterClass – 要应用到相机中的镜头特效的类。
- RemoveCameraLensEffect [Emitter] – 从相机中删除一个镜头特效。
- Emitter – 要从相机中删除的镜头特效。
- ClearCameraLensEffects – 删除当前应用于相机中的所有镜头特效。
相机动画
相机动画是那些可以在 Matinee 中创建的动画(或者可以选择在一个外部动画编辑器中创建,然后将其导入),它在游戏的过程中使用动画的平移和旋转信息来偏置相机。它们还提供模拟功能,通常可以在 Matinee 中模拟相机的任何其他属性,例如,FOV 或后期特效。它对于创建诸如相机振动、手持式相机的手惯性摆动的特效或者任何其他动画特效都十分有效。 要了解有关创建相机动画的更多信息,请参阅SettingUpCameras(设置相机). 相机动画函数- PlayCameraAnim [CameraAnim] [Rate] [Scale] [BlendInTime] [BlendOutTime] [bLoop] [bRandomStartTime] [Duration] [bSingleInstance] – 在相机上播放一个相机动画。
- CameraAnim – 要播放的相机动画。
- Rate – 可选项。播放相机动画的速度。
- Scale – 可选项。应用于相机动画变换控件的强度加强器。
- BlendInTime – 可选项。淡入相机动画所需的时间长度。
- BlendOutTime – 可选项。淡出相机动画所需的时间长度。
- bLoop – 可选项。如果是真,相机动画将继续循环直到明确要求它停止。
- bRandomStartTime – 可选项。如果为真,相机动画将会在动画的时间轴上任意一个时间点开始播放。
- Duration – 可选项。播放动画的时间长度。如果没有设置。将会播放整个动画。
- bSingleInstance – 可选项。如果是真,同一时间只允许存在一个相机振动实例。
- StopAllCameraAnims [bImmediate] – 停止所有当前播放的相机动画。
- bImmediate – 可选项。如果是真,动画将会立即停止,忽略不计任何混合延时设置。
- StopAllCameraAnimsByType [Anim] [bImmediate] – 会停止相机动画的某个特定类型的所有实例。
- Anim – 要停止的相机动画的类型。
- bImmediate – 可选项。如果是真,动画将会立即停止,忽略不计任何混合延时设置。
- StopCameraAnim [AnimInst] [bImmediate] – 会停止某个相机动画的某个特定实例。
- AnimInst – 要停止的相机动画实例。
- bImmediate – 可选项。如果是真,动画将会立即停止,忽略不计任何混合延时设置。
相机修改器
相机修改器是应用于相机时可以修改相机的属性的物体。CameraModifier 类是这些效果的基类。通过建这个类的子类并覆盖其中的函数,完全自定义可以创建的修改器。CameraModifier_CameraShake 类是一个可以通过使用相机修改器完成的很恰当的示例。 相机修改器属性-
- ModifierList – 当前应用于相机上的所有相机修改器的数列
- CameraShakeModClass – 用于减振型相机振动的类。例如,Kismet 中非相机动画画面振动。
- PlayCameraShake [Shake] [Scale] [PlaySpace] [UserPlaySpaceRot] – 会在相机上播放相机振动效果。
- Shake – 用于相机振动效果的 CamerShake 设置。
- Scale – 可以放大相机振动设置的缩放因素。
- PlaySpace – 可选项。可以用于相机振动的播放空间。
- UserPlaySpaceRot – 可选项。可以用于用户定义的播放空间的旋转量。
- StopCameraShake [Shake] – 会停止在相机上播放相机振动效果。
- Shake – 要停止播放的相机振动效果。
- CalcRadialCameraShake [Cam] [Epicenter] [InnerRadius] [OuterRadius] [Falloff] – 会计算并为一个具有径裂的特定相机返回强度。
- Cam – 要计算密度的相机。
- Epicenter – 产生相机振动的起源位置。
- InnerRadius – 距离衰减开始的 epicenter 的距离。
- OuterRadius – 距离相机振动效果结束的 epicenter 的距离。
- Falloff – 用于计算强度衰减的指数。
- PlayWorldCameraShake [Shake] [ShakeInstigator] [Epicenter] [InnerRadius] [OuterRadius] [Falloff] [bTrForceFeedback] [bOrientShakeTowardEpicenter] – 播放会影响所有附近相机的世界相机振动。
- Shake – 要播放的相机振动。
- ShakeInstigator – 发起相机振动的 Actor。
- Epicenter – 产生相机振动的起源位置。
- InnerRadius – 距离衰减开始的 epicenter 的距离。
- OuterRadius – 距离相机振动效果结束的 epicenter 的距离。
- Falloff – 用于计算强度衰减的指数。
- bTryForceFeedback – 如果是真,强制反馈将会尝试被应用于任何受影响的控制器。
- bOrientShakeTowardEpicenter – 可选项。如果是真,相机振动中的任何偏移都将相对面向 epicenter 进行使用,X 轴正方向朝向 epicenter。
- ClearAllCameraShakes – 会删除当前应用于相机的所有相机振动。
Player Controller(玩家控制器)
- PlayerCamera – 引用玩家的相机。
- CameraClass – 要对玩家使用的相机的类。
- ViewTarget – 玩家相机的当前视图目标。
- RealViewTarget – 玩家的相机的视图目标的玩家函数复制信息。
- FOVangle – 玩家的相机的视角。
- DefaultFOV – 用于玩家的相机的默认视角。
- GetPlayerViewPoint [out_Location] [out_Rotation] – 它会返回 Controller 的 Pawn 的视点。对于人类玩家来说,这是指相机的视角。对于AI-控制的玩家来说,这个函数是Pawn眼中的视角。在这个基本的实现过程中,它只是 Controller 自己的位置和旋转量。
- out_Location – 会输出玩家的视点位置。
- out_Rotation – 会输出玩家的视点旋转量。
- GetActorEyesViewPoint [out_Location] [out_Rotation] – 它会返回 Controller 或者其 Pawn(如果存在一个)的视点。基本上,它会返回玩家从哪个位置和方向观看。
- out_Location – 会输出玩家的眼睛的位置。
- out_Rotation – 会输出玩家的眼睛的旋转量。
- UpdateRotation [DeltaTime] – 它会更新基于玩家的输入的 Controller 及该 Controller 的 Pawn 的旋转量。
- DeltaTime – 自从上一次更新发生开始的时间长度。
- ProcessViewRotation [DeltaTime] [out_ViewRotation] [DeltaRot] – 调用它可以允许对控制器的视图旋转量进行任何修改(例如,固定)。 从 UpdateRotation() 中调用。
- DeltaTime – 自从上一次更新发生开始的时间长度。
- out_ViewRotation – 会输出玩家的视图旋转量。
- DeltaRot – 由于玩家输入使旋转量发生改变。
- PlayerMove [DeltaTime] – 它会为当前的移动计算新的加速度和旋转量值,然后调用 ProcessMove()(针对单个玩家或监听服务器)或 ReplicateMove()(针对网络客户端)。这只是基本 PlayerController 类中的一个存根,但是它在与运动有关的特定状态中被覆盖,例如 PlayerWalking 状态。每个循环都会从 PlayerTick() 函数中调用这个函数。
- ProcessMove [DeltaTime] [newAccel] [DoubleClickMove] [DeltaRot] – 它会处理客户端上的当前移动。该函数被覆盖在运动需要特殊功能才能运动的特定状态内部。
Pawn
- CalcCamera [DeltaTime] [out_CamLoc] [out_CamRot] [out_FOV] – 它会在从 Pawn 角度浏览时计算相机的视点。这是玩家的主要相机计算。
- DeltaTime – 自从上一次更新发生开始的时间长度。
- out_CamLoc – 会输出相机的位置。
- out_CamRot – 会输出相机的旋转量。
- out_FOV – 会输出相机的视角。
- GetDefaultCameraMode [RequestedBy] – 它会返回应该被用于这个 Pawn 的默认相机模式,例如一个名称。通常在具有 Pawn 的时候由控制器进行调用。
- RequestedBy - 控制器请求默认相机模式。
- ProcessViewRotation [deltaTime] [out_ViewRotation] [out_DeltaRot] – 它会为 pawn 提供影响玩家的视图旋转量的机会,然后返回最终视图旋转量作为 out_ViewRotation 参数。可以从 PlayerController 的 UpdateRotation() 函数中调用它。
- deltaTime – 自从上一次更新发生开始的时间长度。
- out_ViewRotation – 会输出 Pawn 的视角的旋转量。
- out_DeltaRot – 会输出 delta 旋转量。
- SetViewRotation [NewRotation] – 如果存在一个Controller则设置该Controller的旋转值,如果不存在Controller ,则设置Pawn本身的旋转值。
- NewRotation – 要为 Pawn 的视图设置的新旋转量。
- GetActorEyesViewPoint [out_Location] [out_Rotation] – 这返回了Pawn的眼睛的位置及方位或者玩家的视角。对于第一人称视角来说,这和相机的位置及朝向一样。它也是会从这里开始执行大多数跟踪的视点。
- out_Location – 会输出 Pawn 的眼睛的位置。
- out_Rotation – 会输出 Pawn 的眼睛的旋转量。
定制相机行为
示例 - CalcCamera
class UDNGame extends UTDeathMatch; defaultproperties { DefaultPawnClass=class'UDNExamples.UDNPawn' PlayerControllerClass=class'UDNExamples.UDNPlayerController' MapPrefixes[0]="UDN" }
[Engine.GameInfo] DefaultGame=UDNExamples.UDNGame DefaultServerGame=UDNExamples.UDNGame
第一人称相机示例
第一人称视角是从 UTPawn 扩展而来的所有 pawn 的默认相机类型。该示例会从每个相关的类中抽取组成相机类型的主要部分,并将其放置在新的子类中,以便可以更好地演示有关创建一个基本的第一人称相机的流程。 UDNPawn.ucclass UDNPawn extends UTPawn; simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { // 计算第一人称相机位置和旋转量 GetActorEyesViewPoint( out_CamLoc, out_CamRot ); return true; } defaultproperties { }
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) { // 为远程客户端更新 ViewPitch 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); } // 计算将会应用在 ViewRotation 上的 Delta 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 { }
第三人称相机示例
第三人称相机设置同时被列为 UTPawn 的所有子类的备选相机类型。该示例会抽取主要部分并将默认相机覆盖为第三人称相机。 UDNPawn.ucclass UDNPawn extends UTPawn; //覆盖使默认情况下的玩家网格物体可见 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //将玩家控制器设置在视图后面,并使网格物体可见 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 ) { // 调整相机位置,确保它没有剪切到世界中 // @todo fixmesteve. 注意:如果 FindSpot 失败,您仍然可以获得剪切(很少发生) 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 { }
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) { // 为远程客户端更新 ViewPitch 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); } // 计算将会应用在 ViewRotation 上的 Delta 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 { }
自上而下相机示例
可以通过进行某些额外的修改创建自上而下相机。它与第三人称相机设置相似,但是它还需要限制 Pawn 的旋转量,特别是不允许它的 pitch 向上或向下瞄准。 UDNPawn.ucclass UDNPawn extends UTPawn; var float CamOffsetDistance; //相机在玩家上方偏移的距离 var bool bFollowPlayerRotation; //如果是真,相机会与玩家一起旋转 //覆盖使默认情况下的玩家网格物体可见 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //将玩家控制器设置在视图后面,并使网格物体可见 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 }
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) { // 为远程客户端更新 ViewPitch 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); } // 计算将会应用在 ViewRotation 上的 Delta 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 { }
等角相机示例
简单的等角型相机与上面介绍的自上而下相机相似。沿着 X 和 Z 两个轴偏移相机,然后向下旋转该 pitch 聚焦于玩家。 UDNPawn.ucclass UDNPawn extends UTPawn; var float CamOffsetDistance; //相机从玩家处偏移的距离 var int IsoCamAngle; //覆盖使默认情况下的玩家网格物体可见 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //将玩家控制器设置在视图后面,并使网格物体可见 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 度 CamOffsetDistance=384.0 }
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) { // 为远程客户端更新 ViewPitch 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); } // 计算将会应用在 ViewRotation 上的 Delta 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 { }
横向卷轴相机示例
一个简单的横版相机不只需要控制相机的视点,还需要修改处理玩家输入数据的方式。只允许玩家在画面上向左和向右移动,并且始终面向他们移动的方向。需要输入数据,这样 A 和 D 按键才可以向前后向后移动玩家。 UDNPawn.ucclass UDNPawn extends UTPawn; var float CamOffsetDistance; //Y-轴上要锁定相机的位置 //覆盖使默认情况下的玩家网格物体可见 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //将玩家控制器设置在视图后面,并使网格物体可见 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 }
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) { // 为远程客户端更新 ViewPitch 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; // 计算将会应用在 ViewRotation 上的 Delta DeltaRot.Yaw = Pawn.Rotation.Yaw; DeltaRot.Pitch = PlayerInput.aLookUp; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); } defaultproperties { }
一体化相机示例
该示例会将所有其他示例合成为一个单独的执行过程,它允许玩家在任何相机类型之间进行切换并通过使用可执行函数调整它们。 UDNPawn.ucclass 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 在这个 actor 变为它的 ViewTarget 时会被 Camera 调用 */ 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); } } } /** * 从这个 pawn 观看时计算相机视点。 * * @param fDeltaTime 自从上次更新开始的 delta 时间(秒) * @param out_CamLoc 计算机位置 * @param out_CamRot 相机旋转量 * @param out_FOV 视角 * * @返回 如果 Pawn 应该提供相机视点,那么返回真。 */ simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { // 处理固定的相机 if (bFixedView) { out_CamLoc = FixedViewLoc; out_CamRot = FixedViewRot; } else { if ( CameraType == CAM_ThirdPerson ) // 处理后视图 { CalcThirdPersonCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else if ( CameraType == CAM_TopDown ) // 处理后视图 { CalcTopDownCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else if ( CameraType == CAM_SideScroller ) // 处理后视图 { CalcSideScrollerCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else if ( CameraType == CAM_Isometric ) // 处理后视图 { CalcIsometricCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else { // 默认情况下,我们通过 Pawn 的眼睛浏览。 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; } /** * 会返回没有经过任何调整的基础“瞄准旋转量”(没有瞄准误差、没有自动锁定、没有附着.. 就是没有用过的初始瞄准旋转量!) * * @返回 基本 Aim 旋转量。 */ 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 度 }
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) { // 为远程客户端更新 ViewPitch 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); } // 计算将会应用在 ViewRotation 上的 Delta 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 { }
示例 - 自定义相机
基础相机模块
基础相机模块类通过 Object 类扩展而来,并且定义了所有相机模块将会共有的属性和运转状态。它的一个属性是对拥有它的相机的一个引用。定义了一些初始化和取消初始化函数,但是这个类中的主要函数功能由可以计算玩家相机的新位置和旋转量的 UpdateCamera() 函数执行,而且可以应用任何其他想要的效果或修改。 使用 config(Camera) 修饰符定义这个类,这样应该可以配置或在特定相机模块中永远存在的所有属性将会在 *Camera.ini 文件中找到。它还会被定义为 abstract ,这样实际上就不能够使用它。它更像是一个可以进行特定相机模块构建的模板,不是实际使用自己本身的类。 UDNCameraModule.ucclass UDNCameraModule extends Object abstract config(Camera); //具有相机控制权 var transient UDNPlayerCamera PlayerCamera; //针对模式的初始化 function Init(); /** 在相机变为 active 的时候调用 */ function OnBecomeActive( UDNCameraModule OldCamera ); /** 在相机变为 inactive 的时候调用 */ function OnBecomeInActive( UDNCameraModule NewCamera ); //计算新的相机位置和旋转量 function UpdateCamera(Pawn P, UDNPlayerCamera CameraActor, float DeltaTime, out TViewTarget OutVT); //初始化新的视图目标 simulated function BecomeViewTarget( UDNPlayerController PC ); //处理镜头推近 function ZoomIn(); //处理镜头推近 function ZoomOut(); defaultproperties { }
自定义相机
这个新的相机类由 Camera 基类扩展而来,覆盖一些函数并添加新的函数功能处理相机模块。在这个系统中相机的主要任务是担任相机模块的中间人,它现在处理大部分计算工作。 UDNPlayerCamera.ucclass UDNPlayerCamera extends Camera config(Camera); var UDNPlayerController PlayerOwner; //玩家控制器具有这个相机控制权 var UDNCameraModule CurrentCamera; //正在使用的当前相机模式 var config string DefaultCameraClass; //默认相机模式��类 function PostBeginPlay() { local class<UDNCameraModule> NewClass; Super.PostBeginPlay(); // 设置相机模式 if ( (CurrentCamera == None) && (DefaultCameraClass != "") ) { //获取要使用的默认相机类 NewClass = class<UDNCameraModule>( DynamicLoadObject( DefaultCameraClass, class'Class' ) ); //创建默认相机 CurrentCamera = CreateCamera(NewClass); } } //初始化 PlayerCamera 使其拥有 PlayerController function InitializeFor(PlayerController PC) { //进行父代初始化 Super.InitializeFor(PC); //将 PlayerOwner 设置为玩家控制器 PlayerOwner = UDNPlayerController(PC); } /** * 内部。创建并初始化一个新的相机指定类,返回这个对象参数。 */ function UDNCameraModule CreateCamera(class<UDNCameraModule> CameraClass) { local UDNCameraModule NewCam; //创建新的相机并进行初始化 NewCam = new(Outer) CameraClass; NewCam.PlayerCamera = self; NewCam.Init(); //在新/旧的相机上调用 active/inactive 函数 if(CurrentCamera != none) { CurrentCamera.OnBecomeInactive(NewCam); NewCam.OnBecomeActive(CurrentCamera); } else { NewCam.OnBecomeActive(None); } //将新相机设置为当前值 CurrentCamera = NewCam; return NewCam; } /** * 查询 ViewTarget(视图目标)并输出 Point Of View(视角)。 * * @param OutVT 要使用的 ViewTarget。 * @param DeltaTime 自从上次相机更新后的 Delta Time(以秒为单位)。 */ 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; // 不要在插值的过程中更新输出观察目标 if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing ) { return; } OrigPOV = OutVT.POV; // 视图目标上的默认 FOV OutVT.POV.FOV = DefaultFOV; // 浏览相机 actor。 CamActor = CameraActor(OutVT.Target); if( CamActor != None ) { CamActor.GetCameraView(DeltaTime, OutVT.POV); // 通过 CameraActor 获取长宽比。 bConstrainAspectRatio = bConstrainAspectRatio || CamActor.bConstrainAspectRatio; OutVT.AspectRatio = CamActor.AspectRatio; // 查看 CameraActor 是否需要覆盖使用的 PostProcess 设置。 CamOverridePostProcessAlpha = CamActor.CamOverridePostProcessAlpha; CamPostProcessSettings = CamActor.CamOverridePostProcess; } else { // 为 Pawn Viewtarget 提供了一个指定相机位置的机会。 // 如果 Pawn 没有覆盖相机视图,那么我们将会继续使用我们自己的默认设置 if( Pawn(OutVT.Target) == None || !Pawn(OutVT.Target).CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) ) { //Pawn 不需要控制,我们有一个自定义模式 if(CurrentCamera != none) { //允许模式处理相机更新 CurrentCamera.UpdateCamera(Pawn(OutVT.Target), self, DeltaTime, OutVT); } //没有自定义模式 - 使用默认相机类型 else { switch( CameraStyle ) { case 'Fixed' : // 没有更新,保留以前的相机位置 // 通过恢复保存的 POV,如果 CalcCamera 更改它,但是仍然会返回 false OutVT.POV = OrigPOV; break; case 'ThirdPerson' : // 简单的第三人称视角实现 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, 各个 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' : // 简单的第一人称,通过视图目标的‘眼睛’观看 default : OutVT.Target.GetActorEyesViewPoint(OutVT.POV.Location, OutVT.POV.Rotation); break; } } } } ApplyCameraModifiers(DeltaTime, OutVT.POV); // 设置相机的位置和旋转量,处理我们没有被锁定到视图目标的情况 SetRotation(OutVT.POV.Rotation); SetLocation(OutVT.POV.Location); } //将视图目标初始化传递到相机模式 simulated function BecomeViewTarget( PlayerController PC ) { CurrentCamera.BecomeViewTarget(UDNPlayerController(PC)); } //将放大传递给相机模式 function ZoomIn() { CurrentCamera.ZoomIn(); } //将缩小传递给相机模式 function ZoomOut() { CurrentCamera.ZoomOut(); } defaultproperties { }
基础控制模块
基础控制模块类通过 Object 类扩展而来,并且定义了所有控制模块将会共有的属性和运转状态。它包含一个对拥有它同时控制当前鼠标光标位置的控制的引用。像基础相机模块一样,定义了初始化和取消初始化函数,可以针对任何类型进行可能需要的设置或清除。这个类的其他部分由将会同时控制玩家运动和瞄准的函数构成。 使用 config(Control) 修饰符定义这个类,这样应该可以配置或在特定控制模块中永远存在的所有属性将会在 *Control.ini 文件中找到。它还会被定义为 abstract ,这样实际上就不能够使用它。它只不过是一个用来进行构建的特定控制模块的模板,而不是一个始终实际会使用自己本身的类。 UDNControlModule.ucclass UDNControlModule extends Object abstract config(Control); //引用具有控制权的控制器 var UDNPlayerController Controller; //针对模式的初始化 function Init(); /** 在相机变为 active 的时候调用 */ function OnBecomeActive( UDNControlModule OldModule ); /** 在相机变为 inactive 的时候调用 */ function OnBecomeInActive( UDNControlModule NewModule ); //计算 Pawn 瞄准目标旋转量 simulated singular function Rotator GetBaseAimRotation(); //控制自定义玩家运动 function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot); //计算控制器旋转 function UpdateRotation(float DeltaTime); defaultproperties { }
Engine 类重载
一些引擎类需要扩展才能与新的相机和控制系统接口,其中主要的是 PlayerController、Pawn 和 HUD 类。还会利用这些新的类创建一个新的游戏类型PlayerController
这个新的 PlayerController 类会添加可以更改同时作为缩小或扩大使用的相机模块类型的可执行函数(缩放函数的工作方式由当前相机模块实现它们的方式决定)。覆盖 PlayerWalking 状态的 ProcessMove() 函数和 UpdateRotation() 函数向控制模块添加调用。最后,覆盖并修改 GetPlayerViewPoint() 函数防止相机被损坏,同时强制 PlayerController 在存在新自定义相机的情况下使用它。 UDNPlayerController.ucclass UDNPlayerController extends UTPlayerController; var UDNControlModule ControlModule; //要使用的玩家控制模块 var config string DefaultControlModuleClass; //玩家控制模块的默认类 //通过类切换到其他相机的可执行函数 exec function ChangeControls( string ClassName ) { local class<UDNControlModule> ControlClass; local UDNControlModule NewControlModule; ControlClass = class<UDNControlModule>( DynamicLoadObject( DefaultControlModuleClass, class'Class' ) ); if(ControlClass != none) { // 将模块与 PlayerController 联系起来 NewControlModule = new(Outer) ControlClass; NewControlModule.Controller = self; NewControlModule.Init(); //在新/旧模块上调用 active/inactive 函数 if(ControlModule != none) { ControlModule.OnBecomeInactive(NewControlModule); NewControlModule.OnBecomeActive(ControlModule); } else { NewControlModule.OnBecomeActive(None); } ControlModule = NewControlModule; } else { `log("Couldn't get control module class!"); // 没有一个 Control Class 的情况很好。 PlayerController 将使用默认控制。 } } //通过类切换到其他相机的可执行函数 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); } } //镜头推近可执行函数 exec function ZoomIn() { if(UDNPlayerCamera(PlayerCamera) != none) { UDNPlayerCamera(PlayerCamera).ZoomIn(); } } //镜头拉远可执行函数 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) { // 将模块与 PlayerController 联系起来 NewControlModule = new(Outer) ControlClass; NewControlModule.Controller = self; NewControlModule.Init(); //在新/旧模块上调用 active/inactive 函数 if(ControlModule != none) { ControlModule.OnBecomeInactive(NewControlModule); NewControlModule.OnBecomeActive(ControlModule); } else { NewControlModule.OnBecomeActive(None); } ControlModule = NewControlModule; } else { `log("Couldn't get control module class!"); // 没有一个 Control Class 的情况很好。 PlayerController 将使用默认控制。 } } state PlayerWalking { function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { //控制器有一个 UDNPlayerCamera if(ControlModule != none) { //允许自定义相机覆盖玩家运动 ControlModule.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot); } else { Super.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot); } } } function UpdateRotation( float DeltaTime ) { //控制器有一个 UDNPlayerCamera if(ControlModule != none) { //允许自定义相机更新我们的旋转量 ControlModule.UpdateRotation(DeltaTime); } else { Super.UpdateRotation(DeltaTime); } } /* GetPlayerViewPoint: 会返回 Player 的 Point of View(视角) 对于 AI,它代表的是 Pawn 的 Eyes ViewPoint(眼睛视角) 对于 Human 玩家,它代表的是 Camera 的 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; // 支持使用 CameraActor 视角 if ( CameraActor(ViewTarget) != None ) { if ( PlayerCamera == None ) { super.ResetCameraMode(); SpawnCamera(); } super.GetPlayerViewPoint( POVLocation, POVRotation ); } else { //没有损坏我们的相机!!! /* if ( PlayerCamera != None ) { PlayerCamera.Destroy(); PlayerCamera = None; } */ //没有相机,我们有视图目标 - 使视图目标在控制中 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; } } //没有相机,没有视图目标 - 由我们负责 else if(PlayerCamera == None) { CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle ); return; } //我们有一个相机 - 让相机在控制中 else { POVLocation = PlayerCamera.ViewTarget.POV.Location; POVRotation = PlayerCamera.ViewTarget.POV.Rotation; FOVAngle = PlayerCamera.ViewTarget.POV.FOV; } } // 应用视图振动 POVRotation = Normalize(POVRotation + ShakeRot); POVLocation += ShakeOffset >> Rotation; if( CameraEffect != none ) { CameraEffect.UpdateLocation(POVLocation, POVRotation, GetFOVAngle()); } // 缓存结果 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 类
这个新的 Pawn 类会重载 CalcCamera() 函数,只返回 false,使新的相机系统始终控制相机方位和位置。重载 BecomeViewTarget() 和 GetBaseAimRotation() 函数将它们的功能处理分别传递给相机和控制系统。 UDNPawn.ucclass UDNPawn extends UTPawn; /* BecomeViewTarget 在这个 actor 变为它的 ViewTarget 时会被 Camera 调用 */ simulated event BecomeViewTarget( PlayerController PC ) { local UDNPlayerController UDNPC; UDNPC = UDNPlayerController(PC); //Pawn 由 UDNPlayerController 进行控制,而且有一个 UDNPlayerCamera if(UDNPC != none && UDNPlayerCamera(UDNPC.PlayerCamera) != none) { //允许相机控制网格物体可视性等等。 UDNPlayerCamera(UDNPC.PlayerCamera).BecomeViewTarget(UDNPC); } else { Super.BecomeViewTarget(PC); } } /** * 从这个 pawn 观看时计算相机视点。 * * @param fDeltaTime 自从上次更新开始的 delta 时间(秒) * @param out_CamLoc 相机方位 * @param out_CamRot 相机旋转量 * @param out_FOV 视角 * * @如果 Pawn 应该提供相机视点,那么返回真。 */ simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { //返回 false,允许自定义相机控制它的方位和旋转量 return false; } /** * 会返回没有经过任何调整的基础“瞄准旋转量”(没有瞄准误差、没有自动锁定、没有附着.. 就是没有用过的初始瞄准旋转量!) * * @返回基本 Aim 旋转量。 */ simulated singular event Rotator GetBaseAimRotation() { local vector POVLoc; local rotator POVRot; local UDNPlayerController PC; PC = UDNPlayerController(Controller); //Pawn 由 UDNPlayerController 进行控制,而且有一个 UDNPlayerCamera if(PC != none && PC.ControlModule != none) { //允许自定义相机控制目标旋转量 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 类
这个新的 gametype 类是 UTDeathMatch 类的一个基础扩展类,UTDeathMatch 类可以设置要使用的新 HUD、Pawn 和 PlayerController 类。它还可以将 bUseClassicHUD 设置为 True,这样将会使用在这里指定的 HUD 类替换 UTGFxHUDWrapper,在没有设置这个布尔变量的情况下将会硬编码使用 UTGFxHUDWrapper。 UDNGame.ucclass UDNGame extends UTDeathMatch; defaultproperties { DefaultPawnClass=class'UDNExamples.UDNPawn' PlayerControllerClass=class'UDNExamples.UDNPlayerController' MapPrefixes[0]="UDN" }
示例相机模块
在使用新相机框架的示例中,将会安装一个 Top-Down(自上而下)相机。生成一个新的相机模块是实现基础相机模块类中定义的函数的主要问题。如果您已经看过上面的 CalcCamera() 示例,将会觉得很多内容非常熟悉。 UDNCameraModule_TopDown.ucclass UDNCameraModule_TopDown extends UDNCameraModule; var float CamAltitude; //相机偏离玩家的实际高度 var float DesiredCamAltitude; //要将相机移动到的新高度偏移量 var float MaxCamAltitude; //相机可以距离玩家的最大偏移量 var float MinCamAltitude; //相机可以距离玩家的最小偏移量 var float CamZoomIncrement; //每次点击鼠标滑缩放单元格的数量 //计算新的相机位置和旋转量 function UpdateCamera(Pawn P, UDNPlayerCamera CameraActor, float DeltaTime, out TViewTarget OutVT) { //如果不是想要得到的高度,那么插入新的相机偏移量 if(CamAltitude != DesiredCamAltitude) { CamAltitude += (DesiredCamAltitude - CamAltitude) * DeltaTime * 3; } //在高度 (Z) 偏移量上将相机与玩家对齐 OutVT.POV.Location = OutVT.Target.Location; OutVT.POV.Location.Z += CamAltitude; //将相机旋转量设置为向下 OutVT.POV.Rotation.Pitch = -16384; OutVT.POV.Rotation.Yaw = 0; OutVT.POV.Rotation.Roll = 0; } //初始化新的视图目标 simulated function BecomeViewTarget( UDNPlayerController PC ) { if (LocalPlayer(PC.Player) != None) { //将玩家网格物体设置为可视 PC.SetBehindView(true); UDNPawn(PC.Pawn).SetMeshVisibility(PC.bBehindView); PC.bNoCrosshair = true; } } function ZoomIn() { //减少相机高度 DesiredCamAltitude -= CamZoomIncrement; //将相机高度锁定在限制范围内 DesiredCamAltitude = FMin(MaxCamAltitude, FMax(MinCamAltitude, DesiredCamAltitude)); } function ZoomOut() { //增加相机高度 DesiredCamAltitude += CamZoomIncrement; //将相机高度锁定在限制范围内 DesiredCamAltitude = FMin(MaxCamAltitude, FMax(MinCamAltitude, DesiredCamAltitude)); } defaultproperties { CamAltitude=384.0 DesiredCamAltitude=384.0 MaxCamAltitude=1024.0 MinCamAltitude=160.0 CamZoomIncrement=96.0 }
示例控制模块
UDNControlModule_TopDown.ucclass UDNControlModule_TopDown extends UDNControlModule; //计算 Pawn 瞄准目标旋转量 simulated singular function Rotator GetBaseAimRotation() { local rotator POVRot; //Pawn 所面向的目标 - 锁定跨度 POVRot = Controller.Pawn.Rotation; POVRot.Pitch = 0; return POVRot; } //控制自定义玩家运动 function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { if( Controller.Pawn == None ) { return; } if (Controller.Role == ROLE_Authority) { // 为远程客户端更新 ViewPitch Controller.Pawn.SetRemoteViewPitch( Controller.Rotation.Pitch ); } Controller.Pawn.Acceleration = NewAccel; Controller.CheckJumpOrDuck(); } //计算控制器旋转 function UpdateRotation(float DeltaTime) { local Rotator DeltaRot, NewRotation, ViewRotation; ViewRotation = Controller.Rotation; //旋转 pawn 面向光标 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 { }
配置文件
所有下面的这些文件都应该防止在 UDKgame/Config 目录中。由于某些可能是新添加的目录,所以您需要自己进行创建。其他可以简单地修改使它们可以包括这些新的配置设置。 DefaultCamera.ini 文件应该含有在新相机类中出现的各种配置变量的值。在这个示例中,它只包含设置一个默认的相机模块类。 DefaultCamera.ini[UDNExamples.UDNPlayerCamera] DefaultCameraClass=UDNExamples.UDNCameraModule_TopDown
... [Engine.GameInfo] DefaultGame=UDNExamples.UDNGame DefaultServerGame=UDNExamples.UDNGame ... [UDNExamples.UDNPlayerController] DefaultControlModuleClass=UDNExamples.UDNControlModule_TopDown