UE Character Movement
Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated β do not use".
This skill covers UCharacterMovementComponent (CMC) and ACharacter: the movement-mode pipeline, floor detection and step-up, custom movement modes, client-side prediction and correction, root motion, custom gravity, and the 5.8 movement-base rework. Everything here ships in the Engine module β no plugin and no extra Build.cs entry beyond "Engine". Public headers: GameFramework/CharacterMovementComponent.h (which already pulls in GameFramework/RootMotionSource.h, GameFramework/CharacterMovementReplication.h, CharacterMovementComponentAsync.h and Interfaces/MovementBaseInterface.h), GameFramework/Character.h.
Context
Read .agents/ue-project-context.md if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.
Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.
| Request is about⦠| Go to |
|---|---|
| Choosing between CMC and the Mover plugin | CMC vs Mover |
| Class hierarchy, movement modes, mode transitions | CMC Architecture |
| Standing on a lift, rotating platform, vehicle, physics body | Movement Base |
Where a tick goes, PhysWalking/PhysFalling, sweeps, sliding |
Movement Pipeline |
| Falling through floors, step-up, walkable angle, perching | Floor Detection |
| Wall-run, climb, dash, grapple, any new movement type | Custom Movement Modes |
| Multiplayer desync, rubber-banding, saved moves, compressed flags | Network Prediction |
| Knockback, dashes, animation-driven motion | Root Motion |
| Walking on walls/ceilings, planet gravity | Custom Gravity |
| AI agents, navmesh-driven walking, RVO avoidance | NavMesh Walking |
| Jump/crouch/launch from gameplay code | ACharacter API |
| Speeds, friction, braking, rotation flags | Key Properties |
Deep templates live in cmc-extension-patterns.md; per-mode internals and flow diagrams live in movement-pipeline.md.
CMC vs Mover
Mover (Experimental in 5.8) is a separate plugin (Mover, not enabled by default) built around UMoverComponent, UBaseMovementMode, FLayeredMoveBase and UCharacterMoverComponent. It is not a drop-in CMC replacement.
| Situation | Use |
|---|---|
| Shipping title, any engine-supported platform | CMC |
Existing ACharacter hierarchy, animation and AI already wired to CMC |
CMC |
| Networked gameplay that must be stable today | CMC |
| Non-capsule, non-humanoid movers (vehicles, drones) that still want rollback networking | Mover |
Movement composed from data-driven modes/modifiers instead of a Phys* subclass |
Mover |
| Movement simulated on the physics thread with Chaos | Mover (ChaosMover) |
| Prototype that can absorb Experimental API churn | Mover |
Mover details belong to ue-mover. Do not mix the two components on one pawn.
CMC Architecture
UActorComponent -> UMovementComponent -> UNavMovementComponent -> UPawnMovementComponent -> UCharacterMovementComponentUNavMovementComponent also implements INavMovementInterface; UCharacterMovementComponent additionally implements IRVOAvoidanceInterface and INetworkPredictionInterface, and is declared UCLASS(MinimalAPI).
CMC is a default subobject on ACharacter (ACharacter::CharacterMovementComponentName). ACharacter owns the capsule, mesh and high-level verbs (Jump, Crouch, LaunchCharacter); CMC owns simulation, floor detection and prediction.
Movement Modes
EMovementMode (Engine/EngineTypes.h):
| Mode | Meaning |
|---|---|
MOVE_None |
No movement processing |
MOVE_Walking |
Ground movement with floor detection and step-up |
MOVE_NavWalking |
Walking driven by navmesh projection |
MOVE_Falling |
Airborne: gravity, air control, landing |
MOVE_Swimming |
Fluid movement with buoyancy |
MOVE_Flying |
Free 3D movement, no gravity |
MOVE_Custom |
User-defined; dispatches to PhysCustom with a uint8 sub-mode |
// UCharacterMovementComponent. SetMovementMode is BlueprintCallable;
// OnMovementModeChanged is protected and not a UFUNCTION (override it in C++)
virtual void SetMovementMode(EMovementMode NewMovementMode, uint8 NewCustomMode = 0);
virtual void OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode);ACharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode = 0) fires after the CMC hook and broadcasts MovementModeChangedDelegate (FMovementModeChangedSignature). Put enter/exit logic for custom modes in the CMC override; the current sub-mode is the uint8 CustomMovementMode member.
Movement Base
The movement base is the object a character stands on and moves with. In 5.8 it is no longer a UPrimitiveComponent*: it is FMovementBaseInterfaceData (Interfaces/MovementBaseInterface.h), which holds a TWeakObjectPtr<UObject> PhysicsObjectOwner plus the IPhysicsBodyInstanceOwner that supplies the base behaviour. This lets non-component physics objects act as bases.
Reading the base
// UCharacterMovementComponent
UObject* GetMovementBaseObject() const; // BlueprintCallable
const FMovementBaseInterfaceData* GetMovementBaseInterfaceData() const;
FMovementBaseInterfaceData* GetMovementBaseInterfaceData_Mutable();
const FMovementBaseInterfaceData* GetLastServerMovementBaseInterfaceData() const;APawn declares GetMovementBaseObject(), GetMovementBaseInterfaceData() and GetMovementBaseInterfaceData_Mutable(); ACharacter overrides all three as final. FMovementBaseInterfaceData itself exposes IsValid(), Clear(), Set(), GetBodyInstanceOwner(), GetMovementBaseObject() and GetMovementBaseObjectOwner().
static void MyLogCurrentBase(const UCharacterMovementComponent& Movement)
{
const FMovementBaseInterfaceData* BaseData = Movement.GetMovementBaseInterfaceData();
if (!MovementBaseUtility::IsMovementBaseDataValid(BaseData))
{
return;
}
const FName BoneName = Movement.GetCharacterOwner()->GetBasedMovement().BoneName;
const FVector BaseVelocity = MovementBaseUtility::GetMovementBaseVelocity(BaseData, BoneName);
// Component-specific work now needs an explicit cast.
if (const UPrimitiveComponent* BaseComponent = Cast<UPrimitiveComponent>(BaseData->GetMovementBaseObject()))
{
UE_LOG(LogMyGame, Verbose, TEXT("Based on %s at %s"), *BaseComponent->GetName(), *BaseVelocity.ToString());
}
}Setting the base
virtual void SetBase(FMovementBaseInterfaceData* MovementBaseInterfaceData, const FName BoneName = NAME_None, bool bNotifyActor = true); // CMC and ACharacter
void SetBaseFromFloor(const FFindFloorResult& FloorResult); // CMCBuild the struct from what you already have:
FMovementBaseInterfaceData NewBase = MovementBaseUtility::GetMovementBaseDataFromHitResult(&Hit);
SetBase(&NewBase, Hit.BoneName);MovementBaseUtility::GetMovementBaseDataFromPhysicsOwner(UObject*) does the same from a replicated PhysicsObjectOwner.
Utility namespace
Every MovementBaseUtility helper now takes FMovementBaseInterfaceData* instead of UPrimitiveComponent*: IsDynamicBase, IsSimulatedBase, UseRelativeLocation, AddTickDependency, RemoveTickDependency, GetMovementBaseVelocity, GetMovementBaseTangentialVelocity, GetMovementBaseTransform, TransformLocationToWorld, TransformLocationToLocal, TransformDirectionToWorld, TransformDirectionToLocal. New in 5.8: DoesMovementBaseDataMatch, IsMovementBaseDataValid, GetMovementBaseDataFromPhysicsOwner, GetMovementBaseDataFromHitResult.
Stored and replicated base state
| Old member | 5.8 member | Holder |
|---|---|---|
MovementBase |
MovementBaseInterfaceData |
FBasedMovementInfo |
MovementBase |
PhysicsObjectOwner |
FRepRootMotionMontage |
MovementBase |
MovementBasePhysicsObjectOwner |
FCharacterNetworkMoveData |
StartBase / EndBase |
StartMovementBaseInterfaceData / EndMovementBaseInterfaceData |
FSavedMove_Character |
LastServerMovementBase |
LastMovementBaseInterfaceData |
UCharacterMovementComponent |
Every server-authority and correction virtual that used to take a UPrimitiveComponent* ClientMovementBase now has an FMovementBaseInterfaceData* overload: ServerMoveHandleClientError, ServerCheckClientError, ServerExceedsAllowablePositionError, ServerShouldUseAuthoritativePosition, ClientAdjustPosition_Implementation, ClientVeryShortAdjustPosition_Implementation, ClientAdjustRootMotionPosition_Implementation, ClientAdjustRootMotionSourcePosition_Implementation, OnClientCorrectionReceived (plus the non-virtual RevertMove). Override the new overload β overriding the deprecated one silently stops being called.
ACharacter::BaseChange() and ACharacter::OnRep_ReplicatedBasedMovement() are the notification hooks; ACharacter::GetBasedMovement() returns the FBasedMovementInfo.
Movement Pipeline
PerformMovement(float DeltaTime) (protected) is the autonomous-path entry point; simulated proxies go through SimulatedTick, which calls PerformMovement only while root motion is active and otherwise SimulateMovement (a MoveSmooth extrapolation that skips the Phys* functions). PerformMovement reaches StartNewPhysics(float deltaTime, int32 Iterations), which dispatches on MovementMode. All Phys* functions are virtual; PhysFalling is public (CharacterMovementComponent.h:1663), the other five are protected (:1995-2007):
virtual void PhysWalking(float deltaTime, int32 Iterations);
virtual void PhysNavWalking(float deltaTime, int32 Iterations);
virtual void PhysFalling(float deltaTime, int32 Iterations);
virtual void PhysSwimming(float deltaTime, int32 Iterations);
virtual void PhysFlying(float deltaTime, int32 Iterations);
virtual void PhysCustom(float deltaTime, int32 Iterations);Inside a Phys* function:
// BlueprintCallable
virtual void CalcVelocity(float DeltaTime, float Friction, bool bFluid, float BrakingDeceleration);
// UMovementComponent; also has an FRotator overload
virtual bool SafeMoveUpdatedComponent(const FVector& Delta, const FQuat& NewRotation, bool bSweep,
FHitResult& OutHit, ETeleportType Teleport = ETeleportType::None);SafeMoveUpdatedComponent wraps MoveUpdatedComponent and calls ResolvePenetration on initial overlap β always prefer it inside custom Phys* code. When overriding it, add using UMovementComponent::SafeMoveUpdatedComponent; next to the declaration or the FRotator overload is hidden.
On a blocking hit, CMC's override of SlideAlongSurface(const FVector& Delta, float Time, const FVector& Normal, FHitResult& Hit, bool bHandleImpact) projects the remaining delta along the surface (and refuses to slide up unwalkable slopes while walking). Corner hits go through TwoWallAdjust(FVector& WorldSpaceDelta, const FHitResult& Hit, const FVector& OldHitNormal) const. During walking, ComputeGroundMovementDelta(const FVector& Delta, const FHitResult& RampHit, const bool bHitFromLineTrace) const reorients the delta along the ramp, and MaintainHorizontalGroundVelocity() flattens velocity afterwards (rescaling to preserve magnitude when bMaintainHorizontalGroundVelocity is false).
SetUpdatedComponent(USceneComponent* NewUpdatedComponent) is overridden on CMC; it is what binds the component being moved (the capsule) and re-registers ticking. Sub-stepping is bounded by MaxSimulationTimeStep and MaxSimulationIterations; MIN_TICK_TIME is the floor below which a slice is discarded. CMC can also run the whole simulation on the physics thread behind the CVar p.AsyncCharacterMovement (default 0; the engine marks it not fully developed) through FCharacterMovementComponentAsyncInput / FCharacterMovementComponentAsyncOutput / FCharacterMovementComponentAsyncCallback β custom Phys* state is not mirrored into that path automatically. Full per-mode breakdowns and the async plumbing: movement-pipeline.md.
Floor Detection
FFindFloorResult lives in CharacterMovementComponentAsync.h (USTRUCT(BlueprintType)), not in the CMC header:
| Member | Meaning |
|---|---|
bBlockingHit |
Sweep hit something, not in initial penetration |
bWalkableFloor |
Hit surface passes the walkability test |
bLineTrace |
Result came from the line-trace fallback |
FloorDist |
Distance from the swept capsule |
LineDist |
Distance from the line trace (valid only if bLineTrace) |
HitResult |
Full FHitResult |
Helpers: IsWalkableFloor() (bBlockingHit && bWalkableFloor), GetDistanceToFloor(), Clear(), SetFromSweep(), SetFromLineTrace().
// Both are const on UCharacterMovementComponent
virtual void FindFloor(const FVector& CapsuleLocation, FFindFloorResult& OutFloorResult,
bool bCanUseCachedLocation, const FHitResult* DownwardSweepResult = NULL) const;
virtual void ComputeFloorDist(const FVector& CapsuleLocation, float LineDistance, float SweepDistance,
FFindFloorResult& OutFloorResult, float SweepRadius,
const FHitResult* DownwardSweepResult = NULL) const;FindFloor delegates to ComputeFloorDist (downward capsule sweep, then a line trace to validate the contact point). The cached result is CurrentFloor. AdjustFloorHeight() snaps the capsule between MIN_FLOOR_DIST and MAX_FLOOR_DIST.
Walkability: WalkableFloorAngle (degrees, clamped 0β90) and the derived WalkableFloorZ (default 0.71, about 44.8 degrees). Set the angle through SetWalkableFloorAngle(float) β writing WalkableFloorZ directly desynchronises the pair. virtual bool IsWalkable(const FHitResult& Hit) const is the override point for per-surface rules.
Step-up runs through virtual bool StepUp(const FVector& GravDir, const FVector& Delta, const FHitResult& Hit, FStepDownResult* OutStepDownResult = NULL), bounded by MaxStepHeight, with PerchRadiusThreshold and PerchAdditionalHeight controlling ledge perching.
Custom Movement Modes
MOVE_Custom plus the uint8 CustomMovementMode gives 256 sub-modes that reuse CMC's prediction pipeline.
// MyCharacterMovement.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "MyCharacterMovement.generated.h"
UENUM(BlueprintType)
enum class EMyMovementMode : uint8
{
None = 0,
Climbing = 1,
Dash = 2
};
UCLASS()
class MYGAME_API UMyCharacterMovement : public UCharacterMovementComponent
{
GENERATED_BODY()
public:
UMyCharacterMovement();
UPROPERTY(Transient)
uint8 bWantsToClimb : 1;
UPROPERTY(Transient)
FVector ClimbSurfaceNormal;
virtual void PhysCustom(float deltaTime, int32 Iterations) override;
virtual void OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) override;
virtual float GetMaxSpeed() const override;
protected:
void PhysClimb(float deltaTime, int32 Iterations);
};void UMyCharacterMovement::PhysCustom(float deltaTime, int32 Iterations)
{
Super::PhysCustom(deltaTime, Iterations); // dispatches ACharacter::K2_UpdateCustomMovement
if (CustomMovementMode == static_cast<uint8>(EMyMovementMode::Climbing))
{
PhysClimb(deltaTime, Iterations);
}
}Call Super::PhysCustom(deltaTime, Iterations) first: the base implementation forwards to ACharacter::K2_UpdateCustomMovement(float DeltaTime), so skipping it or calling it last breaks the Blueprint hook and any base bookkeeping added later. This skill is the authority on that ordering.
Enter and leave a mode with SetMovementMode(MOVE_Custom, static_cast<uint8>(EMyMovementMode::Climbing)) and SetMovementMode(MOVE_Falling). Cap speed by overriding virtual float GetMaxSpeed() const override (the base returns MaxCustomMovementSpeed for MOVE_Custom). A full climbing mode with prediction is in cmc-extension-patterns.md.
Network Prediction
The autonomous client simulates locally, records each frame as an FSavedMove_Character, sends it to the server, and replays unacknowledged moves when the server sends a correction. Custom movement state that is not saved and restored will be lost on replay. The transport itself (RPC rules, roles, relevancy, push model) belongs to ue-networking-replication.
FSavedMove_Character
Useful fields: bPressedJump, bWantsToCrouch, bForceMaxAccel, bForceNoCombine, TimeStamp, DeltaTime, StartLocation, StartVelocity, StartFloor, StartRotation, StartControlRotation, StartMovementBaseInterfaceData, SavedLocation, SavedVelocity, EndMovementBaseInterfaceData, Acceleration, MaxSpeed, SavedRootMotion.
Virtuals to override, verbatim:
virtual void Clear();
virtual void SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, class FNetworkPredictionData_Client_Character& ClientData);
virtual void PrepMoveFor(ACharacter* C);
virtual uint8 GetCompressedFlags() const;
virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const;
virtual void PostUpdate(ACharacter* C, EPostUpdateMode PostUpdateMode);
virtual bool IsImportantMove(const FSavedMovePtr& LastAckedMove) const;
virtual void CombineWith(const FSavedMove_Character* OldMove, ACharacter* InCharacter, APlayerController* PC, const FVector& OldStartLocation);Compressed flags
FSavedMove_Character::CompressedFlags is a nested enum:
| Flag | Value | Purpose |
|---|---|---|
FLAG_JumpPressed |
0x01 |
Jump input |
FLAG_WantsToCrouch |
0x02 |
Crouch input |
FLAG_Reserved_1 |
0x04 |
Engine reserved |
FLAG_Reserved_2 |
0x08 |
Engine reserved |
FLAG_Custom_0 |
0x10 |
Yours |
FLAG_Custom_1 |
0x20 |
Yours |
FLAG_Custom_2 |
0x40 |
Yours |
FLAG_Custom_3 |
0x80 |
Yours |
Pack them in GetCompressedFlags(), unpack them in virtual void UpdateFromCompressedFlags(uint8 Flags) on the CMC. Four bits total; anything larger goes through custom move data.
Prediction data
FNetworkPredictionData_Client_Character owns SavedMoves, FreeMoves, PendingMove, LastAckedMove, MaxSavedMoveCount and the mesh-smoothing offsets. Override virtual FSavedMovePtr AllocateNewMove() to hand back your subclass, and lazily create the data in virtual class FNetworkPredictionData_Client* GetPredictionData_Client() const override (the CMC member is ClientPredictionData; GetPredictionData_Client_Character() is the typed accessor).
Packed move RPCs
Movement RPCs live on ACharacter:
void ServerMovePacked(const FCharacterServerMovePackedBits& PackedBits); // Server, unreliable, WithValidation
void ClientMoveResponsePacked(const FCharacterMoveResponsePackedBits& PackedBits); // Client, unreliable, WithValidationFor state that does not fit in the four custom flags, derive FCharacterNetworkMoveData (override ClientFillNetworkMoveData(const FSavedMove_Character& ClientMove, ENetworkMoveType MoveType) and Serialize(UCharacterMovementComponent&, FArchive&, UPackageMap*, ENetworkMoveType)), derive FCharacterNetworkMoveDataContainer and repoint NewMoveData / PendingMoveData / OldMoveData, then call SetNetworkMoveDataContainer(...) in the CMC constructor with a container that outlives the component. GetCurrentNetworkMoveData() reaches the active data from inside MoveAutonomous or UpdateFromCompressedFlags. The response side mirrors this with FCharacterMoveResponseDataContainer and SetMoveResponseDataContainer(...). Templates: cmc-extension-patterns.md.
Root Motion
FRootMotionSource (GameFramework/RootMotionSource.h) drives movement from gameplay code. Base fields: Priority (uint16, higher wins), LocalID (uint16), InstanceName, Duration (negative means it runs until removed), AccumulateMode (ERootMotionAccumulateMode::Override or Additive), FinishVelocityParams (ERootMotionFinishVelocityMode).
| Struct | Key fields | Use |
|---|---|---|
FRootMotionSource_ConstantForce |
Force, StrengthOverTime |
Knockback, wind |
FRootMotionSource_RadialForce |
Location, LocationActor, Radius, Strength, bIsPush, bNoZForce |
Explosions, vortex |
FRootMotionSource_MoveToForce |
StartLocation, TargetLocation, PathOffsetCurve |
Dash to a fixed point |
FRootMotionSource_MoveToDynamicForce |
StartLocation, SetTargetLocation() |
Homing dash |
FRootMotionSource_JumpForce |
Rotation, Distance, Height, TimeMappingCurve |
Targeted jump arc |
// UCharacterMovementComponent
uint16 ApplyRootMotionSource(TSharedPtr<FRootMotionSource> SourcePtr);
TSharedPtr<FRootMotionSource> GetRootMotionSource(FName InstanceName);
TSharedPtr<FRootMotionSource> GetRootMotionSourceByID(uint16 RootMotionSourceID);
void RemoveRootMotionSource(FName InstanceName);
void RemoveRootMotionSourceByID(uint16 RootMotionSourceID);
bool HasRootMotionSources() const;TSharedPtr<FRootMotionSource_ConstantForce> Knockback = MakeShared<FRootMotionSource_ConstantForce>();
Knockback->InstanceName = TEXT("Knockback");
Knockback->Duration = 0.3f;
Knockback->Force = KnockbackDirection * KnockbackStrength;
Knockback->AccumulateMode = ERootMotionAccumulateMode::Override;
Knockback->Priority = 5;
GetCharacterMovement()->ApplyRootMotionSource(Knockback);Active sources live in CurrentRootMotion (FRootMotionSourceGroup) and are folded into velocity by virtual void ApplyRootMotionToVelocity(float deltaTime). Animation root motion is a separate path: RootMotionParams (FRootMotionMovementParams) filled from the anim graph, queried with HasAnimRootMotion(), and replicated through FRepRootMotionMontage. Montage authoring and UAnimInstance::SetRootMotionMode belong to ue-animation-system.
Custom Gravity
virtual void SetGravityDirection(const FVector& GravityDir);
bool HasCustomGravity() const;
FVector GetGravityDirection() const;
FQuat GetWorldToGravityTransform() const;
FQuat GetGravityToWorldTransform() const;
FVector RotateGravityToWorld(const FVector& World) const;
FVector RotateWorldToGravity(const FVector& Gravity) const;
FVector ProjectToGravityFloor(const FVector& Vector) const;
FVector::FReal GetGravitySpaceZ(const FVector& Vector) const;
void SetGravitySpaceZ(FVector& Vector, const FVector::FReal Z) const;GravityDirection, WorldToGravityTransform, GravityToWorldTransform and bHasCustomGravity are protected β always go through the accessors, and always set the direction with SetGravityDirection so the cached quaternions are rebuilt. The default is UCharacterMovementComponent::DefaultGravityDirection. Walking, falling and floor detection all operate in gravity space when HasCustomGravity() is true; in custom Phys* code use ProjectToGravityFloor / GetGravitySpaceZ instead of hard-coded Z arithmetic.
NavMesh Walking
MOVE_NavWalking projects the capsule onto the navmesh instead of sweeping against world geometry β far cheaper for crowds, and the reason AI characters can stand on nothing. FindNavFloor(const FVector& TestLocation, FNavLocation& NavFloorLocation) const performs the projection.
| Property | Effect |
|---|---|
bProjectNavMeshWalking |
Raycast to underlying geometry to conform height |
bProjectNavMeshOnBothWorldChannels |
Use WorldStatic and WorldDynamic for that raycast |
bSlideAlongNavMeshEdge |
Slide along the navmesh edge instead of projecting a point and moving to it |
Avoidance: bUseRVOAvoidance, AvoidanceWeight, and AvoidanceGroup (FNavAvoidanceMask) set through SetAvoidanceGroupMask(const FNavAvoidanceMask&). Nav data, queries and pathfinding belong to ue-ai-navigation.
Key Properties
| Property | Default | Notes |
|---|---|---|
MaxWalkSpeed |
600 | Ground speed cap |
MaxWalkSpeedCrouched |
300 | Half of MaxWalkSpeed by default |
MaxSwimSpeed |
300 | |
MaxFlySpeed |
600 | |
MaxCustomMovementSpeed |
600 | Cap returned by GetMaxSpeed() in MOVE_Custom |
MaxAcceleration |
2048 | |
BrakingDecelerationWalking |
2048 | Matches MaxAcceleration by default |
GroundFriction |
8 | |
GravityScale |
1 | Multiplier on world gravity |
JumpZVelocity |
420 | |
AirControl |
0.05 | Lateral control while falling |
MaxStepHeight |
45 | |
WalkableFloorZ |
0.71 | About 44.8 degrees; set via SetWalkableFloorAngle |
MaxSimulationTimeStep |
0.05 | Sub-step cap |
MaxSimulationIterations |
8 | Sub-steps per frame |
NetworkSmoothingMode |
Exponential (CharacterMovementComponent.cpp:718) |
ENetworkSmoothingMode::Disabled, Linear, Exponential |
bOrientRotationToMovement |
false | Rotate toward velocity |
bUseControllerDesiredRotation |
false | Rotate toward controller rotation |
bOrientRotationToMovement and bUseControllerDesiredRotation fight each other each frame β pick one.
ACharacter API
virtual void Jump(); // BlueprintCallable, sets bPressedJump
virtual void StopJumping(); // BlueprintCallable
bool CanJumpInternal() const; // BlueprintNativeEvent, DisplayName "CanJump"
virtual bool CanJumpInternal_Implementation() const; // override this
virtual void LaunchCharacter(FVector LaunchVelocity, bool bXYOverride, bool bZOverride);
virtual void Landed(const FHitResult& Hit);
virtual void Crouch(bool bClientSimulation = false);
virtual void UnCrouch(bool bClientSimulation = false);Jump state: bPressedJump, JumpMaxHoldTime, JumpMaxCount, JumpCurrentCount, JumpCurrentCountPreJump. The CMC side is virtual bool DoJump(bool bReplayingMoves, float DeltaTime) and virtual bool CanAttemptJump() const. Blueprint events: OnLanded, OnLaunched, OnWalkingOffLedge, OnReachedJumpApex.
bXYOverride/bZOverride replace the corresponding velocity components instead of adding to them; LaunchCharacter routes through UCharacterMovementComponent::Launch(FVector const&) and HandlePendingLaunch().
Crouching: bIsCrouched is the replicated flag; the capsule half-height is SetCrouchedHalfHeight(const float) / GetCrouchedHalfHeight() on the CMC.
Accessors: GetCharacterMovement(), GetCapsuleComponent() (UCapsuleComponent), GetMesh() (USkeletalMeshComponent).
Deprecated β do not use
| Do not emit | Use in 5.8 | Source |
|---|---|---|
ACharacter::GetMovementBase() |
GetMovementBaseObject() / GetMovementBaseInterfaceData() |
UE_DEPRECATED(5.8) in GameFramework/Character.h |
UCharacterMovementComponent::GetMovementBase() |
GetMovementBaseObject() |
UE_DEPRECATED(5.8) in GameFramework/CharacterMovementComponent.h |
SetBase(UPrimitiveComponent*, FName, bool) |
SetBase(FMovementBaseInterfaceData*, FName, bool) |
UE_DEPRECATED(5.8) in Character.h, CharacterMovementComponent.h |
Any MovementBaseUtility helper taking const UPrimitiveComponent* |
the same name taking const FMovementBaseInterfaceData* (AddTickDependency/RemoveTickDependency take a non-const UPrimitiveComponent* in the old form) |
UE_DEPRECATED(5.8) in Character.h |
FBasedMovementInfo::MovementBase |
FBasedMovementInfo::MovementBaseInterfaceData |
UE_DEPRECATED(5.8) in Character.h |
FRepRootMotionMontage::MovementBase |
FRepRootMotionMontage::PhysicsObjectOwner |
UE_DEPRECATED(5.8) in Character.h |
FCharacterNetworkMoveData::MovementBase |
MovementBasePhysicsObjectOwner |
UE_DEPRECATED(5.8) in GameFramework/CharacterMovementReplication.h |
FSavedMove_Character::StartBase / EndBase |
StartMovementBaseInterfaceData / EndMovementBaseInterfaceData |
UE_DEPRECATED(5.8) in CharacterMovementComponent.h |
GetLastServerMovementBase() / LastServerMovementBase |
GetLastServerMovementBaseInterfaceData() / LastMovementBaseInterfaceData |
UE_DEPRECATED(5.8) in CharacterMovementComponent.h |
ServerMoveHandleClientError, ServerCheckClientError, ClientAdjustPosition_Implementation, RevertMove and the other UPrimitiveComponent* correction virtuals |
the FMovementBaseInterfaceData* overloads |
UE_DEPRECATED(5.8) in CharacterMovementComponent.h |
DoJump(bool bReplayingMoves) |
DoJump(bool bReplayingMoves, float DeltaTime) |
UE_DEPRECATED_FORGAME(5.5) in CharacterMovementComponent.h |
Public CrouchedHalfHeight |
SetCrouchedHalfHeight() / GetCrouchedHalfHeight() |
UE_DEPRECATED_FORGAME(5.0) in CharacterMovementComponent.h |
SetAvoidanceGroup(int32) |
SetAvoidanceGroupMask(const FNavAvoidanceMask&) |
meta=(DeprecatedFunction) in CharacterMovementComponent.h |
UNavMovementComponent::bUseAccelerationForPaths, FixedPathBrakingDistance, bUpdateNavAgentWithOwnersCollision, bUseFixedBrakingDistanceForPaths, bStopMovementAbortPaths |
the matching NavMovementProperties.* field |
UE_DEPRECATED(5.5) in GameFramework/NavMovementComponent.h |
ServerMove, ServerMoveDual, ServerMoveOld, ClientAdjustPosition, ClientAckGoodMove RPCs |
ServerMovePacked / ClientMoveResponsePacked |
DEPRECATED_CHARACTER_MOVEMENT_RPC in CharacterMovementReplication.h (SUPPORT_DEPRECATED_CHARACTER_MOVEMENT_RPCS defaults to 0) |
Common Mistakes
Treating the movement base as a component: GetMovementBase() is deprecated and FBasedMovementInfo::MovementBase no longer carries the base. Read GetMovementBaseInterfaceData() and cast GetMovementBaseObject() only when you genuinely need a UPrimitiveComponent.
Overriding the UPrimitiveComponent* correction virtuals: a subclass that overrides the deprecated ServerCheckClientError / ClientAdjustPosition_Implementation overload compiles but is never called, so corrections silently fall back to base behaviour. Override the FMovementBaseInterfaceData* overloads.
Calling Super::PhysCustom last, or not at all: the base implementation forwards to ACharacter::K2_UpdateCustomMovement. Call it first, then run your mode.
Writing Velocity directly instead of CalcVelocity: bypasses friction, braking and acceleration clamping.
// WRONG
Velocity = GetLastInputVector() * MaxWalkSpeed;
// RIGHT
CalcVelocity(DeltaTime, GroundFriction, false, BrakingDecelerationWalking);Using MoveUpdatedComponent in custom Phys* code: it does not resolve initial penetration, so the capsule gets stuck in geometry. Use SafeMoveUpdatedComponent.
Not saving custom state in FSavedMove_Character: anything not captured in SetMoveFor and restored in PrepMoveFor is lost during correction replay, which shows up as rubber-banding only under latency.
Reusing a stack-local network move data container: SetNetworkMoveDataContainer stores a pointer. A container that goes out of scope leaves a dangling pointer on the CMC.
Setting WalkableFloorZ directly: it is computed from WalkableFloorAngle. Call SetWalkableFloorAngle() so both stay consistent.
Hard-coding Z while custom gravity is active: use ProjectToGravityFloor, GetGravitySpaceZ and RotateWorldToGravity instead of Velocity.Z.
Related Skills
ue-moverβ the Mover plugin (Experimental in 5.8):UMoverComponent, movement modes as data, layered moves, ChaosMoverue-networking-replicationβ RPC rules, net roles,DOREPLIFETIME, push model, relevancy; the transport CMC prediction rides onue-animation-systemβ montages, anim graph,SetRootMotionMode, animation-driven locomotionue-gameplay-frameworkβACharacterin the GameMode/Controller/Pawn hierarchy, possession, input routingue-physics-collisionβ sweeps, collision channels and responses used by floor detection and step-upue-gameplay-abilitiesβ abilities and ability tasks that trigger movement modes or root motion sourcesue-ai-navigationβ navmesh generation and queries behindMOVE_NavWalking, avoidance configurationue-actor-component-architectureβ actor and component lifecycle, attachment, spawning and tick configurationue-gameplay-camerasβ spring arms, view targets, camera modifiers, shakes and the Gameplay Camera Systemue-input-systemβ Enhanced Input actions, mapping contexts, triggers, modifiers and user settings