
ue-character-movement
Use when writing or debugging UCharacterMovementComponent code: custom movement modes, floor detection, jumping, crouching, root motion, moving platforms, or client-side movement prediction. Also use when the user mentions 'CharacterMovementComponent', 'CMC', 'PhysCustom', 'PhysWalking', 'MOVECustom', 'FSavedMoveCharacter', 'GetCompressedFlags', 'FCharacterNetworkMoveData', 'MovementBase', 'LaunchCharacter', 'WalkableFloorAngle', 'SetGravityDirection', 'NavWalking', or 'FRootMotionSource'. For the Mover plugin, see ue-mover; for replication mechanics, see ue-networking-replication.
- Installs
- 1.2K
- Last 24h
- +14
- Trending rank
- #7112
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/quodsoler/unreal-engine-skills --skill ue-character-movement- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads ue-character-movement automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
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 -> UCharacterMovementComponent
UNavMovementComponent 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); // CMC
Build 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, WithValidation
For 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
FAQ
What does the ue-character-movement skill do?
Use when writing or debugging UCharacterMovementComponent code: custom movement modes, floor detection, jumping, crouching, root motion, moving platforms, or client-side movement prediction. Also use when the user mentions 'CharacterMovementComponent', 'CMC', 'PhysCustom', 'PhysWalking', 'MOVECustom', 'FSavedMoveCharacter', 'GetCompressedFlags', 'FCharacterNetworkMoveData', 'MovementBase', 'LaunchCharacter', 'WalkableFloorAngle', 'SetGravityDirection', 'NavWalking', or 'FRootMotionSource'. For the Mover plugin, see ue-mover; for replication mechanics, see ue-networking-replication.
How do I install ue-character-movement?
Run npx skills add https://github.com/quodsoler/unreal-engine-skills --skill ue-character-movement in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/ue-character-movement/ (personal) or .claude/skills/ue-character-movement/ (project).
Which AI agents can use ue-character-movement?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is ue-character-movement safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains ue-character-movement?
It is published by quodsoler in quodsoler/unreal-engine-skills. It has 1,174 recorded installs.
More from quodsoler/unreal-engine-skills
ue-cpp-foundations
quodsoler/unreal-engine-skills
Use when writing Unreal Engine C++ that touches the reflection system or core types: UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM, GENERATEDBODY, TObjectPtr, TWeakObjectPtr, TSharedPtr, FGCObject, TArray, TMap, TSet, TOptional, TVariant, DECLAREDYNAMICMULTICASTDELEGATE, AddDynamic, FName, FString, FText, UGameInstanceSubsystem, UWorldSubsystem, GetDefault, TNotNull. Also use when the user mentions 'UE C++', 'garbage collection', 'CDO', 'smart pointers' or 'specifiers'. For Actor and component lifecycle, see ue-actor-component-architecture; for Build.cs and modules, see ue-module-build-system; for Blueprint-facing functions and latent actions, see ue-blueprint-cpp-interop.
ue-ui-umg-slate
quodsoler/unreal-engine-skills
Use when writing or fixing Unreal Engine UI in C++ — UMG user widgets, Slate widgets, Common UI screens, or MVVM view models. Also use when the user mentions 'UUserWidget', 'NativeConstruct', 'BindWidget', 'CreateWidget', 'AddToViewport', 'ESlateVisibility', 'OnClicked', 'UListView', 'widget animation', 'SNew', 'SLATEBEGINARGS', 'SCompoundWidget', 'UCommonActivatableWidget', 'activatable widget stack', 'UCommonButtonBase', 'gamepad focus', 'FUIInputConfig', 'UMVVMViewModelBase', 'FieldNotify', or 'WidgetComponent'. For input modes and Enhanced Input, see ue-input-system; for Slate in editor tooling, see ue-editor-tools.
ue-editor-tools
quodsoler/unreal-engine-skills
Use when extending the Unreal Editor from C++ with detail customizations, editor utility widgets, menus, editor modes, asset definitions or editor subsystems. Also use when the user mentions 'IDetailCustomization', 'IPropertyTypeCustomization', 'CustomizeDetails', 'RegisterCustomClassLayout', 'IDetailLayoutBuilder', 'AddCustomRow', 'IPropertyHandle', 'UToolMenus', 'ExtendMenu', 'FToolMenuEntry', 'UEditorUtilityWidget', 'Blutility', 'UAssetActionUtility', 'UEditorSubsystem', 'UEdMode', 'UAssetDefinition', 'UFactory', 'FScopedTransaction', or 'editor module'. For Slate fundamentals, see ue-ui-umg-slate; for Build.cs wiring, see ue-module-build-system.
ue-project-context
quodsoler/unreal-engine-skills
Use when creating, refreshing or repairing .agents/ue-project-context.md, the project document every other UE skill reads first. Also use when the user says 'project context', 'set up context', 'UE context', 'scan my project', 'onboard the agent to my project', 'update project context', 'configure project', 'what engine version is this project on', 'which plugins do we have enabled', or complains that UE advice keeps coming out generic. For Build.cs and Target.cs mechanics, see ue-module-build-system; for naming and API-macro conventions, see ue-cpp-foundations; for GAS specifics, see ue-gameplay-abilities.
ue-testing-debugging
quodsoler/unreal-engine-skills
Use when writing or running Unreal Engine tests, adding logging, or profiling a build. Also use when the user mentions 'UELOG', 'log category', 'DEFINELOGCATEGORY', 'UELOGFMT', 'verbosity', 'check', 'ensure', 'ensureMsgf', 'verify', 'IMPLEMENTSIMPLEAUTOMATIONTEST', 'automation test', 'TESTCLASS', 'CQTest', 'AFunctionalTest', 'latent command', 'Unreal Insights', 'TRACECPUPROFILEREVENTSCOPE', 'TRACEBOOKMARK', 'stat unit', 'SCOPECYCLECOUNTER', 'memreport', 'DrawDebugLine', 'UEVLOG', 'gameplay debugger', 'ShowDebug', 'console command', 'CVar', or 'why is my frame time bad'. For module and target wiring, see ue-module-build-system; for editor tooling, see ue-editor-tools.
ue-module-build-system
quodsoler/unreal-engine-skills
Use when writing or fixing Build.cs, Target.cs, .uproject or .uplugin files, creating a module or plugin, or resolving an Unreal build error. Also use when the user mentions 'unresolved external symbol', 'LNK2019', 'cannot open include file', 'C1083', 'MODULENAMEAPI', 'PublicDependencyModuleNames', 'IWYU', 'PCHUsage', 'IMPLEMENTMODULE', 'IMPLEMENTPRIMARYGAMEMODULE', 'LoadingPhase', 'circular dependency', 'Missing precompiled manifest', 'EngineAssociation', 'IncludeOrderVersion', 'Live Coding'. For UCLASS/UPROPERTY rules see ue-cpp-foundations; for editor modules see ue-editor-tools; for Game Feature plugins see ue-game-features.
Popular code quality & workflow skills
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
improve-codebase-architecture
mattpocock/skills
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
handoff
mattpocock/skills
Compact the current conversation into a handoff document for another agent to pick up.
domain-modeling
mattpocock/skills
Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.
codebase-design
mattpocock/skills
Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
diagnosing-bugs
mattpocock/skills
Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.