【UEGamePlay】- 3C篇 : Character(一)

基本組件(繼承鏈):
UActorCompoent - 負責組件的生命周期管理、激活/停用、與Actor的綁定等
USceneCompoent - 具有變換并支持附件(組件依附),但沒有渲染或碰撞功能。
UPrimitiveCompoent - 具有渲染和物理信息,可以實現(xiàn)Overlap
UMovementComponent - 具備基本移動功能和接口
UProjectileMovementComponent - 拋射物移動
UNavMovementComponent - 路徑尋找和導航功能,可實現(xiàn)代理AI移動
UPawnMovementComponent - 提供輸入累積,Owner和移動相關接口
UCharacterMovementComponent - 提供了豐富的角色移動功能
UFloatingPawnMovement - 運動組件,為Pawn提供簡單運動
基礎移動組件(泛用型):
USceneComponent
+FTransform ComponentToWorld 組件相對世界的變換/基本位置信息 //關鍵能力
-----------------------------------
MoveComponent() //核心移動入口,負責位置/旋轉(zhuǎn)更新 可選擇sweep。
MoveComponentImpl() //虛函數(shù)分發(fā)
UpdateComponentToWorldWithParent() //更新 +ComponentToWorld
UMovementComponent
+USceneComponent UpdatedComponent //
+UPrimitiveComponent UpdatedPrimitive;
+FVector Velocity //速度,表示角色當前的移動速度和方向。
----------------------------------------------
SafeMoveUpdatedComponent() //安全移動更新的組件,嘗試移動組件并處理碰撞和阻擋,確保不會進入不可通過的區(qū)域。
MoveUpdatedComponent() //移動更新的組件,直接移動組件到目標位置,不處理碰撞。
MoveUpdatedComponentImpl() //虛函數(shù)分發(fā)
GetPenetrationAdjustment() //計算移動調(diào)整,嘗試擺脫失敗移動帶來的滲透/基于FHitResult,返回能解決穿透的Delta增量
ResolvePenetration() //移動失敗后,多次退回嘗試移出物體的穿透范圍。遵循平面約束
HandleImpact() // 碰撞響應
ComputeSlideVector() //計算滑動下的位移
SlideAlongSurface() //嘗試沿表面滑動
GetMaxSpeed() //獲取最大速度,返回角色在當前狀態(tài)下的最大移動速度。
UpdateComponentVelocity() //更新組件速度
GetPhysicsVolume() //獲取物理體積,返回角色當前所在的物理體積,如水、空氣等。
UNavMovementComponent
+FNavAgentProperties NavAgentProps //導航代理屬性,包含角色在導航網(wǎng)格中的相關信息,如大小、能否通過特定區(qū)域等。
+FMovementProperties MovementState //運動狀態(tài)標簽,用于標識角色的運動能力和狀態(tài)。
uint8 bCanCrouch
uint8 bCanJump
uint8 bCanWalk
uint8 bCanSwim
uint8 bCanFly
基于GamePlay視角下的底層核心移動函數(shù)(此函數(shù)為不同移動組件衍生中包裹的核心):
USceneComponent::MoveComponent()
virtual MoveComponentImpl() //虛函數(shù)轉(zhuǎn)發(fā)
ConditionalUpdateComponentToWorld()
UpdateComponentToWorld()
UpdateComponentToWorldWithParent()
更新變量:+ComponentToWorld
InternalSetWorldLocationAndRotation
UpdateComponentToWorldWithParent()
更新變量:+ComponentToWorld
移動組件處理移動的核心調(diào)用鏈(主要用于處理不同的物理運動狀態(tài)下的移動)
UMovementComponent::SafeMoveUpdatedComponent()
UMovementComponent::MoveUpdatedComponent()
UMovementComponent::MoveUpdatedComponentImpl()
+UpdatedComponent -> USceneComponent::MoveComponent();
tips:移動組件必須控制一個能夠存在于世界上的組件。移動組件屬于UActorCompoent的子類。本身不具備坐標,其主要功能是用于控制成員變量USceneComponent UpdatedComponent進行移動
UMovementComponent::SafeMoveUpdatedComponent(const FVector& Delta, const FRotator& NewRotation, bool bSweep, FHitResult& OutHit, ETeleportType Teleport)
USceneComponent::MoveComponent(const FVector& Delta, const FQuat& NewRotation, bool bSweep, FHitResult* Hit=NULL, EMoveComponentFlags MoveFlags = MOVECOMP_NoFlags, ETeleportType Teleport = ETeleportType::None);
衍生移動組件(角色特化型)
//角色特化型移動組件一般用于包含“物理”特性的可操控主體。例如Pawn,Character
Pawn && PawnMovementComponent
PawnMovementComponent的設計上用于為 Pawn 提供更新移動的能力(接口/輔助),主要目的還是為了子組件鋪墊所提供一些Pawn其關聯(lián)的移動的基本功能。提供了一種通用的方式來累積和讀取方向輸入(并不實現(xiàn)具體的移動行為):
UPawnMovementComponent
+TObjectPtr<class APawn> PawnOwner;
-----------------------------------
AddInputVector() //接受輸入方向 自身不做處理,內(nèi)部轉(zhuǎn)發(fā)回Owner處理
ConsumeInputVector() //消耗輸入方向 自身不做處理,內(nèi)部轉(zhuǎn)發(fā)回Owner處理
APawn
+FVector ControlInputVector //待處理輸入
+FVector LastControlInputVector //正在處理輸入
+bUseControllerRotationPitch //使用控制器旋轉(zhuǎn)
+bUseControllerRotationRoll //使用控制器旋轉(zhuǎn)
+bUseControllerRotationYaw //使用控制器旋轉(zhuǎn)
----------------------------------------
SetupPlayerInputComponent() //設置玩家輸入組件
CreatePlayerInputComponent()//創(chuàng)建玩家輸入組件
AddMovementInput()
//內(nèi)部最終調(diào)用為 APawn::Internal_AddMovementInput();
//內(nèi)部包含擁有PawnMovementComponent的轉(zhuǎn)發(fā),但是實際最終調(diào)用相同(轉(zhuǎn)發(fā)回溯)
ConsumeMovementInputVector()
//內(nèi)部最終調(diào)用為APawn::Internal_ConsumeMovementInputVector();
//內(nèi)部包含擁有PawnMovementComponent的轉(zhuǎn)發(fā),但是實際最終調(diào)用相同(轉(zhuǎn)發(fā)回溯)
AddControllerPitchInput()
//內(nèi)部轉(zhuǎn)發(fā)至AController::AddPitchInput();
AddControllerYawInput()
//內(nèi)部轉(zhuǎn)發(fā)至AController::AddYawInput();
AddControllerRollInput()
//內(nèi)部轉(zhuǎn)發(fā)至AController::AddRollInput();
GetLastMovementInputVector()//返回LastControlInputVector
GetPendingMovementInputVector()//返回ControlInputVector
FaceRotation()//將Pawn的旋轉(zhuǎn)更新為指定的旋轉(zhuǎn),假定為ControlRotation,遵循控制器旋轉(zhuǎn)設置
GetViewRotation()// 返回視圖旋轉(zhuǎn) 通常是控制器旋轉(zhuǎn)
UpdateNavAgent()//更新導航代理,確保導航代理屬性與角色狀態(tài)同步。
MoveIgnoreActorAdd()//添加忽略的移動演員,將指定的演員添加到移動忽略列表中,避免與之碰撞
GetMovementComponent()//此函數(shù)為了通用性用了消耗量比較大的查找類去搜尋,可以進行重載變得更快捷
AddMovementInput()用于收集和存儲移動輸入。主要用于改變Pawn中的ControlInputVector以及LastControlInputVector。本身不會直接導致Pawn移動,而是將輸入量存儲起來供移動組件在每幀更新時處理。ControlInputVector,LastControlInputVector保存了玩家輸入的移動方向和大小,這些輸入量通常在對應的移動組件中進行處理,不會應用到Pawn的移動上。- 基礎Pawn類中并沒有直接附加
UPawnMovementComponent組件。需要自行添加 UPawnMovementComponent組件并不實現(xiàn)具體的移動行為,其組件含義為提供輸入累積,Owner 關聯(lián)和一些移動相關的工具函數(shù)ControlInputVector->LastControlInputVector(目的是防止在幀之間控制輸入的累積)
輸入-移動/旋轉(zhuǎn)流程
Pawn中不實現(xiàn)具體的移動邏輯,但是存在已經(jīng)實現(xiàn)的旋轉(zhuǎn)邏輯
旋轉(zhuǎn)流程:
APlayerController::TickActor
PlayerTick
UpdateRotation
APawn::FaceRotation
如果啟用以下設置中任意一項
+bUseControllerRotationPitch //使用控制器旋轉(zhuǎn)
+bUseControllerRotationRoll //使用控制器旋轉(zhuǎn)
+bUseControllerRotationYaw //使用控制器旋轉(zhuǎn)
在APawn::FaceRotation中Pawn的旋轉(zhuǎn)會被啟用的對應控制器旋轉(zhuǎn)控制(直接SetActorRotation())
如果不是自由相機,盡可能不要使用這個方式,尤其是在Character這種精細化的角色子類。
DefaultPawn(Pawn衍生/模板)
Character 和 DefaultPawn 等子類會自動處理此輸入并移動。
以DefaultPawn舉例,由輸入導致的最終更新位于TickComponent中SafeMoveUpdatedComponent();
DefaultPawn作為基本實現(xiàn)應用類的簡易角色類,是其余角色類的基本模板
UFloatingPawnMovement //運動組件,提供速度,加速度限制,沒有實現(xiàn)重力
ApplyControlInputToVelocity()//負責將存儲的輸入量轉(zhuǎn)換為加速度或速度。這一步將輸入量應用到Pawn的速度上,決定Pawn的移動方向和速度。
ADefaultPawn
+UFloatingPawnMovement MovementComponent //移動組件
+UStaticMeshComponent MeshComponent //
輸入-移動旋轉(zhuǎn)流程
- 旋轉(zhuǎn)由父類Pawn的FaceRotation提供,默認啟用三項控制器旋轉(zhuǎn)設置
UFloatingPawnMovement::TickComponentApplyControlInputToVelocity()消費輸入轉(zhuǎn)換為Velocity;Velocity轉(zhuǎn)換為移動差量DeltaDelta用于驅(qū)動SafeMoveUpdatedComponent()實現(xiàn)實際的移動HandleImpact()和SlideAlongSurface()處理滑動以及物理阻擋UpdateComponentVelocity()更新組件速度
Character (包含基礎運動學的網(wǎng)絡同步角色)
UCharacterMovementComponent
+FVector Acceleration //加速度 由輸入向量更新
+FFindFloorResult CurrentFloor //當前地面信息
+enum EMovementMode MovementMode //當前移動模式
+float Mass //質(zhì)量,用于計算加速度以及受力
+bEnableScopedMovementUpdates //是否啟動范圍內(nèi)更新優(yōu)化性能
+FNetworkPredictionData_Client_Character* ClientPredictionData //客戶端角色移動預測數(shù)據(jù)
+FNetworkPredictionData_Server_Character* ServerPredictionData //服務器角色移動預測數(shù)據(jù)
--------------------------------------------------
MoveAutonomous() //處理自主移動,即在不受其他外力干擾的情況下,角色根據(jù)輸入指令進行移動的邏輯。
MoveAlongFloor() //在地面上移動的邏輯,確保角色沿著地面正常行走,并處理地形變化。
UpdateBasedRotation() //隨著底座的旋轉(zhuǎn)更新控制器的視圖旋轉(zhuǎn)
StartNewPhysics() //啟動新的物理處理周期,根據(jù)角色當前的移動模式和狀態(tài),調(diào)用相應的物理處理函數(shù)。
PhysWalking() //處理角色在步行模式下的物理計算,包括地面摩擦力、重力等因素。
PhysNavWalking() //處理角色在導航網(wǎng)格上步行的物理計算,通常用于AI角色的路徑導航。
PhysFalling() //處理角色在下落模式下的物理計算,包括重力加速度和落地檢測。
PhysFlying() //處理角色在飛行模式下的物理計算,包括飛行速度和方向控制。
PhysSwimming() //處理角色在游泳模式下的物理計算,包括水的浮力和阻力。
PhysCustom() //處理自定義模式下的物理計算,允許開發(fā)者實現(xiàn)自定義的移動邏輯。
CallServerMove() //在客戶端上調(diào)用,向服務器發(fā)送移動請求,確保客戶端和服務器上的角色位置同步。
ReplicateMoveToServer() //復制移動數(shù)據(jù)到服務器,確保服務器上的角色位置和客戶端的一致。
ClientUpdatePositionAfterServerUpdate() //在服務器更新后,客戶端更新角色位置,處理可能的位移校正。
SimulatedTick() //在模擬角色(非本地控制的角色)上調(diào)用,用于處理模擬的移動和物理更新。
TickCharacterPose() //每幀更新角色的姿態(tài),確保角色動畫和物理狀態(tài)的同步
ACharacter
+FBasedMovementInfo BasedMovement //保存角色當前所站立的“基礎”對象的信息,用于處理角色在移動平臺上或其他移動基礎上的相對位置和旋轉(zhuǎn)。
+FBasedMovementInfo ReplicatedBasedMovement //復制的基于移動的信息,用于網(wǎng)絡同步,確保客戶端和服務器上的基于移動狀態(tài)一致。
+uint8 ReplicatedMovementMode //復制的移動模式,用于網(wǎng)絡同步,確保客戶端和服務器上的移動模式一致。
+float ReplicatedServerLastTransformUpdateTimeStamp //復制的服務器最后變換更新時間戳,用于網(wǎng)絡同步,記錄服務器上最后一次變換更新的時間。
+TObjectPtr<UCharacterMovementComponent> CharacterMovement
+TObjectPtr<USkeletalMeshComponent> Mesh
+TObjectPtr<UCapsuleComponent> CapsuleComponent
FBasedMovementinfo //保存有關角色所站立的“基礎”對象的信息的結構。
+TObjectPtr<UPrimitiveComponent> MovementBase //指向角色所站立的基礎對象的指針,通常是一個移動平臺或其他物理對象
+FName BoneName //基礎對象上的骨骼名稱,如果角色站立在一個骨骼網(wǎng)格的特定骨骼上,則記錄該骨骼的名稱。
+FVector_NetQuantize100 Location //角色相對于基礎對象的位置,使用網(wǎng)絡量化格式確保高效同步。
+FRotator Rotation //角色相對于基礎對象的旋轉(zhuǎn)。
+bool bServerHasBaseComponent //表示服務器是否擁有基礎組件。
+bool bRelativeRotation //表示角色是否相對于基礎對象進行旋轉(zhuǎn)。
+bool bServerHasVelocity //表示服務器是否擁有基礎對象的速度信息。
輸入-移動旋轉(zhuǎn)流程(玩家)
//以下是接受控制器輸入狀態(tài)的角色,也就是玩家角色的流程
常規(guī)的第三人稱/第一人稱 角色移動邏輯:
AddControllerPitch/Yaw/RollInput //添加對應的控制器旋轉(zhuǎn)
更新ControlRotation
AddMovementInput //添加移動向量
移動輸入向量被轉(zhuǎn)換為加速度向量,后續(xù)在各類動力學模式中被處理為速度進行移動
通常根據(jù)ControlRotation轉(zhuǎn)換移動向量 //例如攀爬等方向為特殊需要單獨處理
UCharacterMovementComponent::TickComponent()
InputVector = ConsumeInputVector(){return APawn::LastControlInputVector}
UCharacterMovementComponent::ControlledCharacterMove(InputVector, DeltaTime)
Acceleration = ScaleInputAcceleration(ConstrainInputAcceleration(InputVector));
PerformMovement() //完整的移動處理流程
FScopedMovementUpdate // 延遲更新
StartNewPhysics() //在這里處理不同狀態(tài)下的移動邏輯
switch (MovementMode){ PhysWalking PhysNavWalking PhysFalling PhyFlying PhysSwimming PhysCustom }
CalcVelocity() //根據(jù)加速度計算速度
SafeMoveUpdatedComponent() //核心移動邏輯(每個分支均會包括)
PhysicsRotation()//核心旋轉(zhuǎn)邏輯。
ComputeOrientToMovementRotation()
// 根據(jù)當前運動計算目標旋轉(zhuǎn),當使用bOrientRotationToMovement時,基于加速度計算旋轉(zhuǎn)
正常在地面上的流程棧幀:
![[6bbc850572a1f714064411937e50411e.png]]
PerformMovement
處理絕大部分情況下的每幀運動,是CharacterMovementComponent的核心,其中包含范圍更新移動代碼塊。此代碼塊為PerformMovement的核心
tips:以目的做流程,子列表為核心函數(shù)
處理流程如下
- 計時/前置驗證
SCOPE_CYCLE_COUNTER(性能統(tǒng)計)HasValidData()/GetWorld()前置有效性檢查
- 瞬移檢測
- 更新 bTeleportedSinceLastUpdate 用于在地面上瞬移后強制檢測地面
- 早退(不能移動的情況)
- 若
MovementMode == MOVE_None或UpdatedComponent不可動或在物理仿真中: - 處理 root motion 的消耗(若不是客戶端更新且不忽略根運動),清空/消費 root motion,清除累計力,然后返回。
- 若
- 基于移動的地面檢查準備
bForceNextFloorCheck |= (IsMovingOnGround() && bTeleportedSinceLastUpdate);如果我們在地上且發(fā)生瞬移,強制下一幀做地面檢查。
- root motion 的增量調(diào)整
CurrentRootMotion.LastPreAdditiveVelocity += Adjustment;
- 保存舊狀態(tài)(調(diào)試用)
- 開始范圍移動更新(性能/一致性保障)
FScopedCapsuleMovementUpdate ScopedMovementUpdate(UpdatedComponent, bEnableScopedMovementUpdates);減少重復性碰撞檢測
- 根據(jù)基座運動更新或者延遲更新位置
MaybeUpdateBasedMovement(DeltaSeconds)UpdateBasedMovement(DeltaSeconds)UpdateBasedRotation(FinalRotation, PawnDeltaRotation.Rotator());
- 清理無效的 RootMotion Source
CurrentRootMotion.CleanUpInvalidRootMotion(DeltaSeconds, *CharacterOwner, *this);
- 應用累計外力
ApplyAccumulatedForces(DeltaSeconds):把外部施加到 character 的力(launch、external forces)轉(zhuǎn)換為速度/影響。
- 更新角色狀態(tài)(移動前)
UpdateCharacterStateBeforeMovement(DeltaSeconds);移動前修正狀態(tài) //源碼中為蹲伏狀態(tài)的切換
- 檢查MOVE_NavWalking
TryToLeaveNavWalking();查詢是否需要做離開導航步行狀態(tài)的轉(zhuǎn)換
- 處理延遲的 Launch
HandlePendingLaunch() Character::LaunchCharacter()被延遲執(zhí)行時在此生效ClearAccumulatedForces清除累計力
- 準備/收集 Root Motion(在物理前)
- 存在根運動且不由客戶端更新:
- `IsPlayingRootMotion()
TickCharacterPose(),并轉(zhuǎn)換動畫 local->world。
CurrentRootMotion.PrepareRootMotion(...):準備合成/累積來自非動畫來源的 root motion。
- `IsPlayingRootMotion()
- 存在根運動且不由客戶端更新:
- 應用 Root Motion 到 Velocity
HasAnimRootMotion()animation root motion 轉(zhuǎn)成世界空間速度,覆蓋Velocity- `RootMotionParams = ConvertLocalRootMotionToWorld()
AnimRootMotionVelocity = CalcAnimRootMotionVelocity()Velocity = ConstrainAnimRootMotionVelocity()
else- 若
HasOverrideVelocity,合成 override velocity 到Velocity。
- 若
- NaN 校驗
- 宏
ensureMsgf(!Velocity.ContainsNaN()確保速度合法,避免災難性崩潰
- 宏
- 清除跳躍輸入與計數(shù)
CharacterOwner->ClearJumpInput(DeltaSeconds);NumJumpApexAttempts = 0;
- 主要物理移動調(diào)用
StartNewPhysics(DeltaSeconds, 0)真正移動角色地方,存在基本的動力學
- 再次驗證有效性
- 移動后更新角色
UpdateCharacterStateAfterMovement(DeltaSeconds);移動后修正狀態(tài) //源碼中為蹲伏狀態(tài)的切換
- 旋轉(zhuǎn)處理
PhysicsRotation(DeltaSeconds);
- 應用 Root Motion 的旋轉(zhuǎn)
- `HasAnimRootMotion()
MoveUpdatedComponent(FVector::ZeroVector, NewActorRotationQuat, true)}
CurrentRootMotion.HasActiveRootMotionSources()MoveUpdatedComponent(FVector::ZeroVector, NewActorRotationQuat, true);
- `HasAnimRootMotion()
- 消費路徑跟隨請求速度
- `LastUpdateRequestedVelocity = bHasRequestedVelocity ? RequestedVelocity : FVector::ZeroVector;
bHasRequestedVelocity = false;
- 觸發(fā)移動更新回調(diào)
OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);
- 結束范圍移動更新
- 退出
FScopedCapsuleMovementUpdate,讓變換/碰撞狀態(tài)最終生效。
- 退出
- 外部事件回調(diào)
CallMovementUpdateDelegate(DeltaSeconds, OldLocation, OldVelocity):廣播外部監(jiān)聽者(在 scoped update 之外,以便事件能看到最終 Overlaps 等)。
- 保存/更新基座位置選項
- 根據(jù) CVar 選擇
SaveBaseLocation()或MaybeSaveBaseLocation()(修復/優(yōu)化相關行為)。
- 根據(jù) CVar 選擇
- 更新組件速度緩存
UpdateComponentVelocity():把 movement component 的Velocity同步到 UpdatedComponent 的 velocity 等。
- 網(wǎng)絡相關:盡早取消自適應頻率 Throttle
- 在服務器權威下,如果 actor 移動了并且網(wǎng)絡更新在被節(jié)流,可能會調(diào)用
NetDriver->CancelAdaptiveReplication(CharacterOwner)來確保快速同步。
- 在服務器權威下,如果 actor 移動了并且網(wǎng)絡更新在被節(jié)流,可能會調(diào)用
- 計算/保存本幀最終位置/旋轉(zhuǎn)并在服務端處理 timestamp
NewLocation = UpdatedComponent->GetComponentLocation(),NewRotation = UpdatedComponent->GetComponentQuat()。若 server 且 transform 改變,保存ServerLastTransformUpdateTimeStamp(有條件使用客戶端時間戳)。
- 保存 LastUpdateLocation/Rotation/Velocity
LastUpdateLocation = NewLocation; LastUpdateRotation = NewRotation; LastUpdateVelocity = Velocity;—— 用于下一幀比較/差分和網(wǎng)絡預測。
工程要點:
在CharacterMovementComponent中處理旋轉(zhuǎn)的是PhysicsRotation:其核心變量為DesiredRotation
由Orient Rotation to movement和bUseControllerDesiredRotation決定DesiredRotation計算方式
ComputeOrientToMovementRotation();//根據(jù)當前運動計算目標旋轉(zhuǎn),當使用bOrientRotationToMovement時,基于加速度計算旋轉(zhuǎn)
UpdateBasedRotation :當角色所依附的 Base(比如旋轉(zhuǎn)的平臺/載具/重力基座)發(fā)生旋轉(zhuǎn) 時,同步調(diào)整角色相關的朝向(主要是 Controller 的 ControlRotation / 視角)并根據(jù)設置決定是否把基座的 roll (翻滾)應用到角色/攝像機上。它在 PerformMovement 中計算出 pawn 因基座旋轉(zhuǎn)產(chǎn)生的增量旋轉(zhuǎn)(delta) 后被調(diào)用,允許移動組件修正最終的角色朝向或?qū)刂破饕暯亲鲱~外處理。
旋轉(zhuǎn)基座處理的調(diào)用棧
PerformMovement
MaybeUpdateBasedMovement
UpdateBasedMovement
UpdateBasedRotation //前置有移動基座的判斷,只有進入移動基座才會在此進入這個
關于添加推動力 兩個一個描述為一次性推動 一個為每幀推動,實際上完全相同。且每幀推動還會被*幀 使其大量縮減

浙公網(wǎng)安備 33010602011771號