UE 角色移动与 Locomotion 系统设计完全指南

UE 角色移动与 Locomotion 系统设计完全指南

前言

Locomotion(运动系统)是角色动画的灵魂。再华丽的外观,如果走路僵硬、转向迟钝、跳跃违和,整个角色就会失去「生命感」。UE 的 Character Movement Component 与动画系统深度耦合,提供了从移动速度到地面检测的一切基础设施——但如何把这些底层数据转化为流畅、自然的角色动作,需要开发者对两者的衔接机制有清晰理解。

本文聚焦 Walk / Jog / Sprint 步态设计根骨骼运动转向动画匹配跳跃落地系统、以及 IK 足部 placement 五大核心主题。


一、速度体系与步态设计

1.1 UE 的速度档位

UE Character Movement Component 内置了清晰的速度层级:

速度参数 默认值 用途
MaxWalkSpeed 175 cm/s 行走速度上限
MaxJogSpeed 335 cm/s 慢跑/冲刺
MaxSprintSpeed 600 cm/s 全力冲刺
MaxCrouchSpeed 160 cm/s 蹲行
MaxSwimSpeed 256 cm/s 水中移动

Project Settings → Character Controller 中可全局调整,也可运行时通过蓝图或 C++ 动态修改。

1.2 三档步态实现

典型的三档步态(Walk → Jog → Sprint)通过 Blend Space 1D + AnimBP 变量 实现:

AnimBP EventGraph:
  Character.GetMovementComponent → Is Sprinting?
                                    ↓
                        Set SprintSpeed / WalkSpeed
                                    ↓
AnimBP AnimGraph:
  Speed Variable (0~600) → BlendSpace1D (Walk/Jog/Sprint)
                                ↓
                          Output Pose

1.3 地面 / 空中 / 水下检测

// C++ 获取当前运动状态
UCharacterMovementComponent* MoveComp = Character->GetCharacterMovement();

if (MoveComp->IsFalling()) {
    // 空中状态
} else if (MoveComp->IsSwimming()) {
    // 水中状态
} else if (MoveComp->IsWalking()) {
    // 地面状态(会自动处理斜坡)
}

// 获取当前地面法线(用于斜坡适配)
FVector GroundNormal = MoveComp->CurrentFloor.HitResult.ImpactNormal;

1.4 动态速度调整

// 蓝图等效:在 Character Blueprint 中
Event Tick
  → Branch (IsExhausted == true)
      → Set MaxWalkSpeed (100)  // 疲惫状态减速
      → Set MaxJogSpeed (200)
  → Branch (IsSprinting == true && Stamina > 0)
      → Set MaxJogSpeed (600)   // 冲刺状态

二、根骨骼运动 vs 完全复制运动

2.1 两种运动模式

UE 中角色的实际位置移动有两种模式:

模式一:完全复制运动(Root Motion from Montages OFF)
– 动画只负责「看起来在动」,实际位移由 Character Movement Component 计算
– 优点:网络同步简单、碰撞可靠
– 缺点:动画与位移可能不完全匹配

模式二:根骨骼运动(Root Motion from Montages / Anim Drives Motion)
– 动画的根骨骼位移直接驱动角色实际移动
– 优点:动画与位移完全匹配,无滑步
– 缺点:需要精确动画,网络同步复杂

2.2 何时用哪种

场景 推荐模式
普通走/跑/跳 复制运动
翻滚(Roll)/ 起身 根骨骼
攀爬(Climb) 根骨骼
攻击位移(如突进) 根骨骼 Montage
受击后仰 根骨骼

2.3 配置方式

在 Character Movement Component 中:

CharacterMovementComponent:
  bRequestedMoveUseAcceleration: True        # 是否使用加速度
  NavAgentProps:
    bRequestedMoveUseAcceleration: False    # AI 路径移动不用加速度
  RootMotion:
    bHasRootMotionSources: False             # 是否允许根骨骼驱动

在 Animation Blueprint 中:

AnimGraph:
  UseNormalizedBlendSpaceAssetPlayerTime → 支持时间驱动
  RootMotionMode → "Root Motion from Montages Only"

2.4 bUseControllerRotationYaw

这个布尔值决定了角色是否「跟随控制器朝向旋转」:

// 玩家角色通常设为 false,由动画系统处理转身
Character->bUseControllerRotationYaw = false;

// AI 角色通常设为 true,由 AIController 驱动朝向
 AIController->bActivateAINavSystem = true;

配合 Orient Rotation to Movement 使用时,角色会自动朝运动方向旋转,转身动画由 Blend Space 或 Turn In Place 驱动。


三、转向与动画匹配

3.1 转向的三种模式

模式 适用场景 实现难度
Directional(方向步态) 原地转向时播放转身动画 简单
Turn In Place 超过角度阈值时原地转身 中等
Blend Turn In Place 小角度快速转身用原地 Blend 复杂

3.2 Directional 步态

Directional 步态下,Blend Space 2D 的 X 轴是 Speed,Y 轴是 Direction(相对角色的朝向角度)

                    角色朝向(0°)
                        ↑
              90° ←  |  → -90°
         向左跑    |      向右跑
                   |
              180° ←      → -180°
                   |
                背面跑

这种模式下,角色奔跑动画包含「左倾 / 右倾 / 正面 / 背面」四个方向的变体,Blend Space 自动混合。

3.3 Turn In Place(原地转身)

当转向角度超过阈值(通常是 90°)时,播放原地转身动画:

转向角度检测:
  DeltaAngle = abs(ActorRotation.Yaw - LastRotation.Yaw)
  if (DeltaAngle > 90 && GroundSpeed < 50)
      → 播放 Turn_90 或 Turn_180 动画

3.4 转身角度阈值设置

// 在 AnimBP EventGraph 中计算转身角度
FVector Velocity2D = GetWorld()->GetFirstPlayerController()->GetPawn()->GetVelocity();
Velocity2D.Z = 0;
FRotator VelocityAngle = Velocity2D.Rotation();

float TurnAngle = FMath::FindDeltaAngleDegrees(
    GetOwningActor()->GetActorRotation().Yaw,
    VelocityAngle.Yaw
);

// 设置到 AnimBP 变量,供 Blend Space 或 Turn 状态机使用
TurnAngleRaw = TurnAngle;
TurnAngle = FMath::GridSnap(TurnAngle, 45.0f); // 吸附到 45° 步进

四、跳跃、落地与受击

4.1 跳跃状态时序

完整的跳跃时序:

[Walk/Jog/Running]
      ↓ (按下 Jump)
[Jump Start] (起跳蓄力/起跳动画, ~0.1s)
      ↓
[InAir] (空中浮空/下落动画, 持续时间)
      ↓ (检测地面)
[Land] (落地动画, ~0.2s)
      ↓
[Land Recovery] (恢复平衡, ~0.3s)
      ↓
[Idle/Run] (回到 Locomotion)

4.2 落地预判(Land Anticipation)

高质量的 Locomotion 系统会在落地前根据 下落高度 播放不同强度的落地动画:

// 根据下落高度选择落地强度
float FallHeight = LastAirLocation.Z - CurrentLocation.Z;
float LandScale = FMath::GetMappedRangeValueClamped(
    FVector2D(200.0f, 1000.0f),  // 下落高度范围
    FVector2D(1.0f, 3.0f),       // 落地动画强度
    FallHeight
);
AnimInstance->SetLandRecoveryScale(LandScale);

4.3 受击反应(Hit React)

受击反应需要游戏逻辑通知动画系统:

// 游戏逻辑(Character 或 Gameplay Ability)
void AMyCharacter::OnTakeHit(FHitResult Hit, FVector ImpulseDir) {
    // 计算击打方向(正面/侧面/背面)
    FVector Forward = GetActorForwardVector();
    float Dot = FVector::DotProduct(ImpulseDir, Forward);

    if (Dot > 0.3f) {
        // 正面击打 → Hit_Forward
    } else if (Dot < -0.3f) {
        // 背面击打 → Hit_Backward
    } else {
        // 侧面击打 → Hit_Left / Hit_Right
    }

    // 播放受击 Montage
    UAnimMontage* HitMontage = HitReactMontage.LoadSynchronous();
    AnimInstance->Montage_Play(HitMontage);
}

受击后自动过渡回 Locomotion 状态,通常通过 Montage 的 Blend Out 时间控制。

4.4 Ragdoll 状态切换

Ragdoll(布娃娃物理)通常用于死亡或严重受击:

Locomotion State Machine:
  Any State → [Ragdoll]
      ↑           ↓
      ← (Blend Out, ~0.3s)
// C++ 启用 Ragdoll
void AMyCharacter::EnableRagdoll() {
    GetMesh()->SetAllBodiesSimulatePhysics(true);
    GetMesh()->SetSimulatePhysics(true);
    GetMesh()->WakeAllRigidBodies();

    // 关闭 Character Movement
    GetCharacterMovement()->DisableMovement();
    GetCharacterMovement()->StopMovementImmediately();
}

五、IK 足部 Placement

5.1 什么是 Foot IK

Foot IK 是通过 程序化方式 调整角色足部位置,使其自然贴合地面的技术。即使角色站在斜坡或台阶上,足部也能「贴」在地面上,而不是悬空或穿模。

5.2 Two Bone IK 节点

UE 提供 Two Bone IK 节点用于处理四肢 IK:

AnimGraph:
  Foot IK
    ├── Effector Location (从地面检测获取)
    ├── Joint Target Location (膝部 IK 目标)
    └── Bone Name (Foot_L / Foot_R)

5.3 地面检测实现

// 在 Character Blueprint 的 EventGraph 中
// 每帧 Raycast 检测地面高度
FVector Foot_L_Location = GetMesh()->GetSocketLocation("foot_l");
FVector Foot_R_Location = GetMesh()->GetSocketLocation("foot_r");

FCollisionQueryParams Params;
Params.AddIgnoredActor(this);

FHitResult Hit_L, Hit_R;
GetWorld()->LineTraceSingleByChannel(Hit_L, Foot_L_Location,
    Foot_L_Location + FVector::DownVector * 100.0f,
    ECC_Visibility, Params);
GetWorld()->LineTraceSingleByChannel(Hit_R, Foot_R_Location,
    Foot_R_Location + FVector::DownVector * 100.0f,
    ECC_Visibility, Params);

// 计算 Pelvis(盆骨)偏移
float PelvisOffset = FMath::Min(Hit_L.ImpactPoint.Z, Hit_R.ImpactPoint.Z)
    - GetMesh()->GetSocketLocation("pelvis").Z;

// 设置到 AnimBP
AnimBP->PelvisOffset = PelvisOffset;
AnimBP->FootIK_L_Effector = Hit_L.ImpactPoint;
AnimBP->FootIK_R_Effector = Hit_R.ImpactPoint;

5.4 Pelvis 深度补偿

Pelvis(盆骨)需要根据两脚落地高度的差异做补偿:

// 盆骨高度 = 两脚最低点高度 - 预设偏移
float PelvisHeight = FMath::Min(Hit_L.ImpactPoint.Z, Hit_R.ImpactPoint.Z)
    - BasePelvisOffset;

AnimBP->SetPelvisTargetHeight(PelvisHeight);

// 膝盖 IK 目标位置(防止膝盖穿模)
FVector KneeTarget_L = Foot_L_Location + FVector::UpVector * 50.0f
    + ForwardVector * 20.0f;
AnimBP->SetKneeTarget_L(KneeTarget_L);

5.5 斜坡适应性

CurrentFloor.HitResult.ImpactNormal 与垂直方向有夹角时,说明角色站在斜坡上:

FVector FloorNormal = MoveComp->CurrentFloor.HitResult.ImpactNormal;
float SlopeAngle = FMath::RadiansToDegrees(
    FMath::Acos(FVector::DotProduct(FloorNormal, FVector::UpVector))
);

if (SlopeAngle > 5.0f) {
    // 应用斜坡旋转到角色基底
    FRotator SlopeRotation = FRotator(
        -SlopeAngle * ForwardVector.Y,
        0,
        SlopeAngle * ForwardVector.X
    );
    AddActorLocalRotation(SlopeRotation);
}

六、总结:Locomotion 调试清单

在完成 Locomotion 系统后,按以下清单逐项检查:

  • [ ] 滑步检查:角色实际移动距离是否与动画位移匹配
  • [ ] 转身检查:快速连续转身是否有动画断裂
  • [ ] 斜坡检查:站在 30° 斜坡上,足部是否穿模
  • [ ] 跳跃检查:长短跳跃高度 / 落地区间是否符合预期
  • [ ] 落地检查:不同高度落地是否有不同强度的落地动画
  • [ ] 网络检查:P2P / Server 模式下,同步延迟是否导致动画抖动
  • [ ] 性能检查IsInGameThreadCheck 确保每帧动画计算量合理

推荐参考资料:
ALS 社区版ShadowfallStudios/ALS-Community)——最完整的 UE5 Locomotion 参考实现
UE 官方 Gameplay Framework 文档——Character Movement Component 详细参数
「Animation Boot Camp」GDC 演讲——AAA 游戏 Locomotion 设计思路


本文收录于 Matrix4x4 AI 编程与游戏开发资源库

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部