[UE学习笔记][基于源码] 控制器、Pawn、相机的控制关系

基于 UE 5.7 引擎源码

理清 Controller / PlayerController / Pawn / Character / CharacterMovementComponent / SpringArm / Camera / PlayerCameraManager 之间"谁持有什么、谁改谁"。

先盘点状态变量,再讲过程怎么读写它们——从"数据"到"流程"建立心智模型。


1. 核心状态变量

只列与"控制 / 移动 / 视点"直接相关的字段。

1.1 AController(基类)

抽象的"控制者",只有一个 Pawn 引用、一个朝向意图。

字段类型含义默认
PawnTObjectPtr<APawn>当前控制的 Pawn(通过 Possess/UnPossess 改)nullptr
CharacterTObjectPtr<ACharacter>Pawn 是 Character 时的别名nullptr
PlayerStateTObjectPtr<APlayerState>玩家信息(昵称、分数等),可复制nullptr
ControlRotationFRotator核心:玩家"想看哪儿"的朝向,权威源(0,0,0)

1.2 APlayerController(在 AController 之上加了什么)

人类玩家专用,新增"输入"和"相机管理"两组字段。

字段类型含义默认
PlayerInputTObjectPtr<UPlayerInput>按键 / 轴的"原始账本",输入流入口由 OverridePlayerInputClass 或 InputSettings.DefaultPlayerInputClass Spawn
RotationInputFRotator本帧鼠标 / 手柄累积的"旋转增量",每帧消费后清零(0,0,0)
PlayerCameraManagerTObjectPtr<APlayerCameraManager>视点管理器,每个 PlayerController 一个PostInitializeComponents 中 Spawn
PlayerCameraManagerClassTSubclassOf<APlayerCameraManager>Spawn 时用哪个类APlayerCameraManager::StaticClass()
bAutoManageActiveCameraTargetboolPossess 时是否自动把新 Pawn 设为 ViewTargettrue
MyHUDTObjectPtr<AHUD>UI 入口nullptr

1.3 APawn

被控制对象的基类,承担"被谁控制"和"要往哪儿走"两件事的存储。

字段类型含义默认
ControllerTObjectPtr<AController>反向指针:谁在控制我(由 PossessedBy 设置)nullptr
PlayerStateTObjectPtr<APlayerState>与 Controller.PlayerState 同步的镜像nullptr
bUseControllerRotationPitchuint32:1Pawn.Rotation.Pitch 是否每帧同步成 Controller.ControlRotation.Pitchfalse
bUseControllerRotationYawuint32:1同上,Yawfalse
bUseControllerRotationRolluint32:1同上,Rollfalse
BaseEyeHeightfloat计算视点位置时的眼高偏移64
ControlInputVectorFVector本帧累积的"移动输入向量"(世界空间),Movement Tick 后清零(0,0,0)
LastControlInputVectorFVector上一帧的副本,给查询用(0,0,0)

Pawn 自己没有 ControlRotation。Pawn.GetViewRotation() 默认实现 = Controller ? Controller->GetControlRotation() : ActorRotation。

1.4 ACharacter(在 APawn 之上加了什么)

把"角色三件套"持有起来,并加了跳跃状态。

字段类型含义默认
CapsuleComponentTObjectPtr<UCapsuleComponent>碰撞胶囊(RootComponent)构造时创建
MeshTObjectPtr<USkeletalMeshComponent>角色骨骼网格构造时创建
CharacterMovementTObjectPtr<UCharacterMovementComponent>位移引擎构造时创建
bPressedJumpuint8:1玩家是否按住跳键false
JumpKeyHoldTimefloat按住时长0
JumpMaxHoldTimefloat最大允许按住时长0
JumpMaxCountint32最多连跳次数1
JumpCurrentCountint32已跳次数0

ACharacter 构造里会把 bUseControllerRotationYaw 改回 true——所以默认 Character 的身体跟着鼠标转。

1.5 UCharacterMovementComponent

真正"动起来"的地方,持有速度 / 加速度 / 移动模式 / 朝向开关。

字段类型含义默认
CharacterOwnerTObjectPtr<ACharacter>反向指针自动绑定
UpdatedComponent(基类)USceneComponent*实际被挪动的组件(= Character 的 Capsule)Capsule
Velocity(基类)FVector当前速度(0,0,0)
AccelerationFVector当前加速度(来自 ControlInputVector 归一化方向 × MaxAcceleration)(0,0,0)
MovementModeEMovementModeWalking / Falling / Flying / Swimming / CustomMOVE_None(Possess 后由 SetDefaultMovementMode 切到 DefaultLandMovementMode,默认 MOVE_Walking)
bOrientRotationToMovementuint8:1角色朝向移动方向(覆盖 bUseControllerDesiredRotation)false
bUseControllerDesiredRotationuint8:1角色平滑转向 ControlRotationfalse
RotationRateFRotator上述两种转向的速度上限(度/秒)(0, 360, 0)
JumpZVelocityfloat跳跃初速420
MaxWalkSpeedfloat行走最大速600
GravityScalefloat重力倍率1.0

1.6 USpringArmComponent

只决定"相机末端的位置和朝向",自身不渲染。

字段类型含义默认
TargetArmLengthfloat吊臂自然长度300
SocketOffsetFVector末端 socket 偏移(最常用,决定相机位置)(0,0,0)
TargetOffsetFVector起点的世界空间偏移(0,0,0)
bUsePawnControlRotationuint32:1吊臂朝向 = Pawn.GetViewRotation()(即 ControlRotation)false
bInheritPitchuint32:1是否继承父组件 Pitchtrue
bInheritYawuint32:1是否继承父组件 Yawtrue
bInheritRolluint32:1是否继承父组件 Rolltrue
bDoCollisionTestuint32:1被遮挡时是否回弹true
bEnableCameraLaguint32:1位置是否平滑跟随false
bEnableCameraRotationLaguint32:1旋转是否平滑跟随false

1.7 UCameraComponent

只决定镜头参数(FOV / 后处理等),位置由父级(通常是 SpringArm 末端)决定。

字段类型含义默认
FieldOfViewfloat透视 FOV(度)90
AspectRatiofloat宽高比1.777778
bConstrainAspectRatiouint8:1是否强制宽高比(不匹配时加黑边)false
PostProcessSettingsFPostProcessSettings后处理默认结构
bUsePawnControlRotationuint8:1没有 SpringArm 时,自己跟控制器旋转false

1.8 APlayerCameraManager

视点的"出口":选定 ViewTarget、调它的 CalcCamera、缓存 POV、跑修饰器栈。

字段类型含义默认
PCOwnerTObjectPtr<APlayerController>反向指针Spawn 时绑定
ViewTargetFTViewTarget当前观察对象(一般 = 当前 Pawn)+ 缓存的 POV空
PendingViewTargetFTViewTarget切换中的目标(用于混合)空
CameraStyleFName“Default” / “Fixed” / “FreeCam” 等模式"Default"
DefaultFOVfloat没有 CameraComponent 时的兜底 FOV90
CameraCachePrivateFCameraCacheEntry上一帧算出的 POV 缓存空
ModifierListTArray<UCameraModifier*>后处理混合栈(震屏等)空

2. 重要动作和开关——发生了什么

按"动作 / 开关"逐节展开,每节回答"它到底改了什么状态、玩家看到什么差异"。

时序图说明:🔓 表示虚函数(C++ 可重写)。

2.1 Add Yaw Input 发生了什么

这一步只做累加——把鼠标增量加到 APlayerController.RotationInput,不立即生效。

APlayerControllerAPawn蓝图APlayerControllerAPawn蓝图RotationInput.Yaw += ValAdd Controller Yaw Input(Val)1AddYawInput(Val)2

调用本身就到这里。真正"消费 RotationInput 写入 ControlRotation、并把朝向同步给 Pawn / SpringArm" 的过程在后续小节展开——这里只是生产端。

2.2 启用 Pawn 的 bUseControllerRotationPitch / Yaw / Roll 会发生什么

让 Pawn 的身体跟着 ControlRotation 转。这一节顺便把 2.1 累积起来的 RotationInput 是怎么、什么时候被消费也画出来——UpdateRotation 内部一气呵成。

APawnAPlayerCameraManagerAPlayerControllerAPawnAPlayerCameraManagerAPlayerControllerTG_PrePhysics局部 ViewRot = GetControlRotation()局部 DeltaRot = RotationInput跑 ModifierList 链OutViewRot += OutDeltaRot;OutDeltaRot = 0LimitViewPitch / Yaw / Roll直接 return,不写 ActorRotation关掉的轴回退到 ActorRotation 当前值alt[三个 bUseControllerRotation* 全为false][任一为 true]TickActor 末尾:RotationInput = ZeroRotator🔓 TickActor(DeltaTime)1🔓 PlayerTick(DeltaTime)2🔓 UpdateRotation(DeltaTime)3🔓 ProcessViewRotation(DeltaTime, ViewRot&, DeltaRot&)4返回(ViewRot 已按引用更新)5SetControlRotation(ViewRot)6🔓 FaceRotation(ViewRot)7SetActorRotation(ViewRot)8

2.3 启用 SpringArm 的 bUsePawnControlRotation / bInheritPitch / Yaw / Roll 会发生什么

AControllerAPawnUSpringArmComponentAControllerAPawnUSpringArmComponentTG_PostPhysicsDesiredRot = GetComponentRotation()(默认基底)DesiredRot = ControlRotationalt[bUsePawnControlRotation == true]DesiredRot.Pitch = RelativeRotation.Pitchalt[!bInheritPitch]DesiredRot.Yaw = RelativeRotation.Yawalt[!bInheritYaw]DesiredRot.Roll = RelativeRotation.Rollalt[!bInheritRoll]alt[!IsUsingAbsoluteRotation()]用 DesiredRot 计算 SocketTransform,末端 Camera 跟随🔓 TickComponent(DeltaTime)1🔓 UpdateDesiredArmLocation(...)2GetTargetRotation()3🔓 GetViewRotation()4GetControlRotation()5ControlRotation6返回 ControlRotation7返回 DesiredRot8

典型用法:

配置表现
关掉 bInheritPitch相机水平面跟玩家转,但俯仰恒定(俯视 / 仰视固定相机)
关掉 bInheritYaw相机朝固定方向,玩家在画面内自由转身(横版固定视角)
全部关掉相机姿态完全由 SpringArm 自身 RelativeRotation 决定,跟谁都不跟

2.4 Add Movement Input 发生了什么

UCharacterMovementComponentAPawn蓝图UCharacterMovementComponentAPawn蓝图ControlInputVector += WorldAccelTG_PrePhysicsLastControlInputVector = ControlInputVectorControlInputVector = ZeroVector用 InputVector 算 Acceleration / Velocity / 写 Capsule 位置🔓 Add Movement Input(WorldDir, Scale)1🔓 AddInputVector(WorldDir * Scale)2Internal_AddMovementInput(WorldAccel)3🔓 TickComponent(DeltaTime)4ConsumeInputVector()5Internal_ConsumeMovementInputVector()6返回 InputVector7🔓 PerformMovement(DeltaSeconds)8

关键:AddYawInput 是 PlayerController 的成员(人类玩家专属),AddMovementInput 是 Pawn 的成员(AI 也用同一个)——这就是为什么 AI 和玩家的"走路"汇合在 ControlInputVector 这一层,而"看哪儿"对 AI 而言走的是另一套(直接给 Controller SetControlRotation)。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐