ue-character-movement — Claude Code skill for UE 5.8 character
A skill for UCharacterMovementComponent in Unreal Engine 5.8: movement modes, floor detection, client-side prediction, root motion.
npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a claude-codeWhat ue-character-movement does
The skill covers UCharacterMovementComponent 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 ships in the Engine module, so no plugin and no extra Build.cs entry beyond "Engine". Public headers are GameFramework/CharacterMovementComponent.h and GameFramework/Character.h.
Inside you get a table for picking between CMC and the Mover plugin, the class hierarchy and mode transitions, a reference for FMovementBaseInterfaceData (in 5.8 a base no longer has to be a component), plus links to extension templates and pipeline internals. The target engine is UE 5.8 and the APIs are verified against its headers. Older forms sit in a separate "Deprecated — do not use" section, so the skill won't hand you a stale snippet from an old tutorial.
If .agents/ue-project-context.md exists, the skill reads it for module names, conventions and enabled plugins. Missing file? It keeps going. Mover and replication mechanics live in the neighbouring ue-mover and ue-networking-replication skills, so don't expect this one to explain replication end to end.
What it helps with
- write a custom movement mode with
PhysCustomandMOVE_Custom - fix a character falling through the floor and tune
WalkableFloorAngle - debug desync and rubber-banding in multiplayer
- make a character ride a lift or a rotating platform
- set gravity along a non-vertical axis with
SetGravityDirection
Example prompts for the agent
- Help me write a custom movement mode on
MOVE_CustomandPhysCustomfor a dash - The character falls through the floor on steps — check the floor detection settings in CMC
- Explain why the character jitters in multiplayer:
FSavedMove_CharacterandGetCompressedFlags
How to install ue-character-movement
1Skills CLI (Vercel)
npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a claude-code2Manually: put the files in place
~/.claude/skills/ue-character-movement/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/ue-character-movement/
1Skills CLI (Vercel)
npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a codex2Manually: put the files in place
.agents/skills/ue-character-movement/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a cursor2Manually: put the files in place
.cursor/skills/ue-character-movement/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install quodsoler/unreal-engine-skills ue-character-movement2Manually: put the files in place
.github/skills/ue-character-movement/SKILL.md1Skills CLI (Vercel)
npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a gemini-cli2Platform CLI
gemini skills install https://github.com/quodsoler/unreal-engine-skills --path skills/ue-character-movementManually: put the files in place
.devin/skills/ue-character-movement/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about ue-character-movement
How do I install ue-character-movement in Claude?
Run npx skills add quodsoler/unreal-engine-skills --skill ue-character-movement -a claude-code. The skill lands in ~/.claude/skills/ue-character-movement/SKILL.md.
What does ue-character-movement do?
It helps you write and debug UCharacterMovementComponent code: custom movement modes, floor detection, jumping, crouching, root motion, moving platforms and client-side prediction.
Which Unreal Engine version does this skill target?
UE 5.8. The APIs are verified against its headers, and deprecated calls are listed separately as not to be used.
Source text
SKILL.md · MIT
---
name: ue-character-movement
description: "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', 'MOVE_Custom', 'FSavedMove_Character', 'GetCompressedFlags', 'FCharacterNetworkMoveData', 'MovementBase', 'LaunchCharacter', 'WalkableFloorAngle', 'SetGravityDirection', 'NavWalking', or 'FRootMotionSource'. For the Mover plugin, see ue-mover; for replication mechanics, see ue-networking-replication."
metadata:
version: "2.0.0"
engine: "5.8"
---
# 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](#cmc-vs-mover) |
| Class hierarchy, movement modes, mode transitions | [CMC Architecture](#cmc-architecture) |
| Standing on a lift, rotating platform, vehicle, physics body | [Movement Base](#movement-base) |
| Where a tick goes, `PhysWalking`/`PhysFalling`, sweeps, sliding | [Movement Pipeline](#movement-pipeline) |
| Falling through floors, step-up, walkable angle, perching | [Floor Detection](#floor-detection) |
| Wall-run, climb, dash, grapple, any new movement type | [Custom Movement Modes](#custom-movement-modes) |
| Multiplayer desync, rubber-banding, saved moves, compressed flags | [Network Prediction](#network-prediction) |
| Knockback, dashes, animation-driven motion | [Root Motion](#root-motion) |
| Walking on walls/ceilings, planet gravity | [Custom Gravity](#custom-gravity) |
| AI agents, navmesh-driven walking, RVO avoidance | [NavMesh Walking](#navmesh-walking) |
| Jump/crouch/launch from gameplay code | [ACharacter API](#acharacter-api) |
| Speeds, friction, braking, rotation flags | [Key Properties](#key-properties) |
Deep templates live in [cmc-extension-patterns.md](references/cmc-extension-patterns.md); per-mode internals and flow diagrams live in [movement-pipeline.md](references/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 |
```cpp
// 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
```cpp
// 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()`.
```cpp
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
```cpp
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:
```cpp
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`):
```cpp
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:
```cpp
// 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](references/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()`.
```cpp
// 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.
```cpp
// 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);
};
```
```cpp
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](references/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:
```cpp
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`:
```cpp
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](references/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 |
```cpp
// 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;
```
```cpp
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
```cpp
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
```cpp
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(...
© quodsoler · MIT · quodsoler/unreal-engine-skills