Version: 1.8
语言 : 中文
Mirror 节点
CodeBase 节点介绍

FacingWarp 节点

1. 概述 (Overview)

Facing Warp Node 是一个用于 AnimGraph 的动画节点。它的主要作用是在保持角色下半身运动方向(Locomotion)的同时,动态调整上半身的朝向。

通过在配置好的脊柱骨骼链(Spine Chain)上分布旋转扭曲,该节点能够实现自然的扫射(Strafing)转身以及锁定目标移动的效果,避免了角色在非直线移动时出现“滑步”或姿态僵硬的问题。

2. 核心概念 (Core Concepts)

在使用本节点前,理解以下三个核心概念至关重要:

  • 运动与朝向解耦 (Decoupling): 传统的移动往往要求角色面朝移动方向。Facing Warp 允许角色向指定方向移动(Locomotion Direction),但身体面朝前方,通过扭曲脊柱来补偿这两者之间的角度差。

  • 脊柱扭曲分布 (Distributed Twist): 为了避免仅旋转腰部导致的模型变形,该节点将所需的旋转角度按照脊柱骨骼(如 Spine, Chest, UpperChest)在Avatar的层级结构从父到子按权重递增分配旋转角度。

    例如旋转角度为90°,设置脊柱骨骼Spine, Chest和UpperChest,权重均为1.0. 计算它们的归一化权重均为1.0/3.0,则在层级结构中位于父级的Spine,其旋转角度为 90°*1.0/3.0 = 30°. 次父级的Chest,其旋转角度为 30° + 90°*1.0/3.0 = 60°. 而位于子级的UpperChest,其旋转角度为 60° + 90°*1.0/3.0 = 90°. 确保角色最终面部朝向为原根运动方向。

  • 输入模式 (Input Modes): 节点支持两种驱动方式:

    • Direction (方向模式):输入一个目标向量,节点自动计算当前朝向与目标向量的夹角。

    • Angle (角度模式):直接输入一个具体的角度值(度数)。

3. 快速入门 (Quick Start)

以下是在 AnimGraph 中创建并配置 Facing Warp 节点的标准流程:

3.1 构建 Graph 连接 (Graph Setup)

  • 创建动画输入 (Source) :

    • AnimationNode 分类下找到 AnimationClip 节点并创建。

    • 设置动画:将 Clip 设置为 Ellen_Walk_Forward。

    • 设置循环:确保该动画是循环模式,保证角色持续行走。

创建AnimationClip节点
创建AnimationClip节点
设置动画
设置动画
  • 创建变量输入 (Input Variable) :

    • 在 Parameters 区域创建一个 Vector3 类型的变量,命名为 MotionDirection

    • 将其拖入 Graph 视图中,作为一个输入节点。

创建Vector3类型变量
创建Vector3类型变量
创建MotionDirection节点
创建MotionDirection节点
  • 创建 FacingWarp Node:

    • 在 AnimationNode 分类下找到 AnimationFacingWarpNode 节点并创建。

    • 将 Ellen_Walk_Forward 的 Output (Pose) 连接到 FacingWarp Node 的 Input 端口。

    • 将 MotionDirection 变量节点的 Output 连接到 FacingWarp Node 的 Motion Direction (黄色) 端口。

创建FacingWarp节点
创建FacingWarp节点
连接FacingWarp节点
连接FacingWarp节点
  • 输出:将 FacingWarp Node 的 Output连接到最终 Output 节点的 Pose 端口。

3.2 Inspector 参数设置 (Inspector Parameter Setup)

  1. 设置根骨骼 (Setup Root Bone) :指定用于引导根运动的骨骼(通常是 HipsPelvis)。

  2. 配置脊柱骨骼 (Setup Spine Bones) :定义哪些脊柱骨骼参与旋转以及它们的权重。

3.3 代码驱动

  • 将下述脚本添加至被相机跟随的游戏对象上。

  • 该脚本获取用户当帧输入的期望 MotionDirection 并传递给 AnimGraph。

  using UnityEngine;
  using AnimGraph;

  public class FacingWarpCharacterController : MonoBehaviour
  {
      [Header("References")]
      public Transform cameraTransform;

      [Header("Graph Settings")]
      public string paramName = "MotionDirection";

      [Header("Rotation Settings")]
      public bool lockRotationToCamera = true;
      public float turnSpeed = 15f;

      public AnimGraphManager graphManager;

      void Start()
      {
          graphManager = GetComponent<AnimGraphManager>();

          if (cameraTransform == null)
          {
              cameraTransform = Camera.main.transform;
          }
      }

      void Update()
      {
          if (lockRotationToCamera)
          {
              HandleCharacterRotation();
          }

          float h = Input.GetAxis("Horizontal");
          float v = Input.GetAxis("Vertical");

          Vector3 moveDir = CalculateCameraRelativeDirection(h, v);

          SetGraphParameter(moveDir);

          //if (moveDir.magnitude > 0.1f)
          //    Debug.DrawRay(transform.position + Vector3.up, moveDir * 2, Color.green);
      }

      void HandleCharacterRotation()
      {
          Vector3 targetForward = cameraTransform.forward;

          targetForward.y = 0;
          targetForward.Normalize();

          if (targetForward != Vector3.zero)
          {
              Quaternion targetRotation = Quaternion.LookRotation(targetForward);
              transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * turnSpeed);
          }
      }

      Vector3 CalculateCameraRelativeDirection(float h, float v)
      {
          if (Mathf.Abs(h) < 0.01f && Mathf.Abs(v) < 0.01f)
              return Vector3.zero;

          Vector3 camForward = cameraTransform.forward;
          Vector3 camRight = cameraTransform.right;

          camForward.y = 0;
          camRight.y = 0;
          camForward.Normalize();
          camRight.Normalize();

          Vector3 targetDirection = (camForward * v + camRight * h);

          if (targetDirection.sqrMagnitude > 0.01f)
          {
              targetDirection.Normalize();
          }

          return targetDirection;
      }

      void SetGraphParameter(Vector3 direction)
      {
          if (graphManager != null)
          {
              graphManager.SetVector("MotionDirection", direction);
          }
      }
  }

3.4 最终效果展示

  • 角色初始播放Walk_Forward动画,向正前方前进

  • 键盘输入期望运动方向(Motion Direction),角色下半身平滑地转向期望运动方向并前进。

  • 脊柱骨骼根据权重,呈现出自然的扭转曲线,使角色上半身仍朝向正前方,达成运动和朝向解耦的效果。

4. 详细功能参考 (Feature Reference)

本节将对编辑器面板(Inspector)中的参数进行详细说明:

开放为端口:如果希望在运行时修改下列参数,可以在Inspector面板的对应属性上单击右键,会弹出“Show As Port”的右键菜单,点击即可开放为端口。连接对应类型的参数可在运行时修改。

4.1 基础设置 (Basic Settings)

参数名称 (Inspector) 描述
Node Weight 节点总权重 (0.0 - 1.0)。控制整个 Warp 效果的混合程度。1 表示完全应用扭曲,0 表示不生效(直接输出上游动画)。

4.2 输入控制 (Input Control)

参数名称 (Inspector) 描述
Input Mode 选择输入模式:
Direction: 通过向量计算角度。
Angle: 直接输入角度值。
Use Global Space (Direction模式) 勾选表示 Motion Direction 使用世界坐标系;不勾选则使用角色局部坐标系。
Motion Direction (Direction模式) 期望移动方向向量。例如 (1, 0, 0) 代表向右移动。
Locomotion Angle (Angle模式) 期望的扭曲角度,相对于角色未应用Warp时的根运动方向。
Use Root Motion 是否启用根运动重定向。勾选后,脊柱的扭曲会影响角色的实际位移方向。
Keep Current Warp 是否保持当前的扭曲状态。用于在特定状态下(如停止移动瞬间或LocomotionAngle输入为0时)锁定当前的扭曲角度。

4.3 扭曲参数 (Warp Settings)

参数名称 (Inspector) 描述
Warp Offset Angle 偏移角度:用于调整上半身的朝向,0表示上半身保持原始的根运动方向。
Rotation Axis 旋转轴:通常设为 Y,表示围绕垂直轴进行水平面上的转身。
Use Smooth Damp 使用平滑阻尼:勾选后,使用 SmoothDamp 来平滑处理旋转变化避免突兀的转向;未勾选时,使用指数衰减插值平滑处理旋转变化。
Smooth Time 平滑时间 (秒):控制扭曲响应的滞后感。通常战斗移动建议设为 0.1 - 0.3。
Max Rotation Speed 最大旋转速度 (度/秒):限制脊柱旋转的角速度上限。
Interp Rotation Speed 插值旋转速度 (度/秒):控制旋转插值的速度。
Max Rotation Angle 最大旋转角度 (度):限制脊柱总扭曲角度的绝对值。

4.4 骨骼配置 (Bone Configuration)

参数名称 (Inspector) 描述
Root Bone 根骨骼引用:通常指定为角色的 Hips 或 Pelvis。这是下半身和根运动转向的基础。
Spine Bones 脊柱骨骼列表:点击 + 号添加。
Transform: 骨骼的 Transform 引用 (如 Spine, Chest, UpperChest)。
Weight: 该骨骼旋转的权重系数。骨骼在列表中的顺序不影响最后的结果,内部会按照骨骼在Avatar中的层级结构从父到子按权重递增分配旋转角度。

5. 最佳实践 (Best Practices)

  1. 骨骼权重分配: 建议脊柱骨骼的权重总和接近 1.0,或者根据美术效果递增。

    • 推荐配置:Spine (0.2), Chest (0.3), UpperChest (0.5)。越靠近头部的骨骼通常承担更多的旋转量,看起来更自然。
    • 另外,如果角色有 Head 或 Neck 骨骼,也可以加入列表中,以增强头部朝向的自然度。
  2. 执行顺序: 在 AnimGraph 中,Facing Warp 节点应当连接在之后,但在 IK Node 之前。

    • 先让脊柱扭曲到位,再进行脚部 IK 计算,可以防止脚部滑步。
  3. Direction 模式 vs Angle 模式

    • 如果你的角色控制器是基于速度向量驱动的,使用 Direction 模式最方便。

    • 如果你已经计算好了“移动方向”与“瞄准方向”的 Delta Yaw,使用 Angle 模式更直接。

6. 常见问题 (FAQ)

Q: 为什么设置了参数但角色没有任何反应?

A: 请检查以下几点:

  1. Node Weight 是否为 1?

  2. Spine Bones 列表是否为空?

  3. Root Bone 是否正确设置?


Q: 启用 UseRootMotion 后,角色移动方向变乱了?

A: 确保 Root Bone 设置正确(通常是 “Hips” 或 “Pelvis”)。此外,检查 Motion Direction 和 Use Global Space 的组合是否正确表达了你的移动意图。


Q: 角色上半身旋转过度,像麻花一样?

A: 使用 Max Rotation Angle 来硬性限制最大扭曲度。


Q: 能否配合Animator使用?

A: 提供 AnimationFacingWarpPlayable 配合 Animator 使用。


Q: 如何实现快速的转身效果?

A: 调整 Smooth Time 参数至较小值(如 0.001 秒),并确保 Max Rotation Speed 足够高(如 1000 度/秒)即可实现快速转身。通过减小平滑时间并提高最大旋转速度,可以显著提升角色对方向变化的响应速度,从而达到快速转身的效果。这种设置特别适用于需要快速改变方向的战斗或动作场景中。


Q: SmoothDamp与指数衰减插值的区别?

A: SmoothDamp 平滑处理旋转变化时,旋转速度会有缓入缓出的加速减速的过渡,适合模拟物理上的惯性效果;而指数衰减插值则在初始时刻就达到最大旋转速度,响应更快但可能显得生硬。选择哪种方式取决于你希望达到的动画风格和流畅度要求。在实际应用中,SmoothDamp 更常用于需要自然过渡的场景,例如角色行走或跑步时的自然转身;而指数衰减插值则更适合需要即时响应的交互操作,比如快速转向或紧急避让等动作。

Mirror 节点
CodeBase 节点介绍