All skills
quodsoler avatar

/ue-character-movement

@f3742d7
by Quod Solerquodsoler/unreal-engine-skills352 stars
59

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.

Use this Skill: https://skilld.dev/gh/quodsoler/unreal-engine-skills/ue-character-movement

This session only. Nothing lands on disk.

SKILL.md

β‰ˆ153 tokens always: the name and description. β‰ˆ8.3k when used: this file. β‰ˆ8k more on demand in 2 files.

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, ChaosMover
  • ue-networking-replication β€” RPC rules, net roles, DOREPLIFETIME, push model, relevancy; the transport CMC prediction rides on
  • ue-animation-system β€” montages, anim graph, SetRootMotionMode, animation-driven locomotion
  • ue-gameplay-framework β€” ACharacter in the GameMode/Controller/Pawn hierarchy, possession, input routing
  • ue-physics-collision β€” sweeps, collision channels and responses used by floor detection and step-up
  • ue-gameplay-abilities β€” abilities and ability tasks that trigger movement modes or root motion sources
  • ue-ai-navigation β€” navmesh generation and queries behind MOVE_NavWalking, avoidance configuration
  • ue-actor-component-architecture β€” actor and component lifecycle, attachment, spawning and tick configuration
  • ue-gameplay-cameras β€” spring arms, view targets, camera modifiers, shakes and the Gameplay Camera System
  • ue-input-system β€” Enhanced Input actions, mapping contexts, triggers, modifiers and user settings

Source: SKILL.md on GitHub

No alerts1d5 checks Β· Risk SAFE
  • Gen Agent Trust Hub1d

    The skill is a comprehensive technical reference for Unreal Engine 5.8 character movement development. It provides documentation and code templates for extending the UCharacterMovementComponent and contains no security risks.

  • Socket1d

    No alerts

  • Snyk1d

    Risk: LOW Β· No issues

  • Runlayer6mo

    3 files scanned Β· No issues

  • ZeroLeaks5mo

    Score: 93/100 Β· 2 sections analyzed

Signed by skilld at f3742d7. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 3 days ago
metadata
{
  "version": "2.0.0",
  "engine": "5.8"
}

README badge

README badge for quodsoler/unreal-engine-skills/ue-character-movement