> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/configure-locomotion.md).

# 配置移动

使用 Unity 烘焙的导航网格为 Convai 角色添加行走和慢跑移动，并提供可调速度和手动移动选项。

添加 `ConvaiNavMeshLocomotion` 将其添加到 Convai 角色，以便它可以在已烘焙的 NavMesh 上行走和慢跑，调整其速度和转向行为，并通过 `IConvaiLocomotionSource` 或 `ConvaiTravelIntent`。当 Convai Body Animation 已在一个也需要移动的角色上运行时，请使用此页。

***

### 前提条件

* `ConvaiBodyAnimationController` 添加到角色上。对于 Body Animation 来说，移动是可选的——没有运动组件的角色会原地待机、说话、做手势和指示。
* 场景中已烘焙的 NavMesh，如果你使用 `ConvaiNavMeshLocomotion`. `ConvaiNavMeshLocomotion` 驱动内置的 Unity `NavMeshAgent`，但 NavMesh 的制作—— `窗口 > AI > Navigation` 以及 `NavMeshSurface` 组件——来自 `com.unity.ai.navigation`，SDK 不会安装它。如果你的项目会烘焙 NavMesh，请安装 `com.unity.ai.navigation` 通过包管理器自行安装。

***

### 为角色添加 NavMesh 运动

{% stepper %}
{% step %}

#### 为地面烘焙 NavMesh

打开 `窗口 > AI > Navigation`，将可行走几何体标记为静态（或添加一个 `NavMeshSurface` 组件），然后烘焙。角色必须站在已烘焙的网格上。
{% endstep %}

{% step %}

#### 添加该组件

选择角色并使用 **Add Component > Convai > Embodiment > NavMesh Locomotion**。这会添加 `ConvaiNavMeshLocomotion`.

`ConvaiNavMeshLocomotion` 需要一个 `NavMeshAgent`。如果角色没有， `Awake` 会使用默认设置添加一个。
{% endstep %}

{% step %}

#### 验证代理位于网格上

进入 Play 模式并调用 `MoveTo` 从脚本中（参见 **从脚本移动角色** 下方），或者查看 Scene 视图中的 gizmo，它会在对象被选中时绘制当前路径。
{% endstep %}
{% endstepper %}

***

### 配置移动速度和转向

`ConvaiNavMeshLocomotion` 公开以下 Inspector 字段：

| 字段       | 类型                       | 默认          | 范围               | 说明                                                   |
| -------- | ------------------------ | ----------- | ---------------- | ---------------------------------------------------- |
| 速度配置     | `LocomotionSpeedProfile` | `自动`        | `步行`, `慢跑`, `自动` | 移动是步行、慢跑，还是根据每个目的地选择步态。 `自动` 慢跑超过 **自动慢跑距离**，更近时则步行。 |
| 自动慢跑距离   | `float`                  | `6` (米)     | `>= 0.5`         | `自动` 配置阈值：比此更远的目的地会以慢跑前往。                            |
| 最小慢跑距离   | `float`                  | `4.5` (米)   | `>= 0`           | 短于此距离的路径始终步行，即使在 `慢跑` 配置下也是如此——慢跑需要空间来加速、巡航并稳稳停下。    |
| 加速度      | `float`                  | `4` (米/秒²)  | `>= 0.5`         | 代理加速度。Unity 自身的默认值 `8` 读起来像是突然猛冲。                    |
| 步行速度     | `float`                  | `1.2` (米/秒) | `>= 0.1`         | 指令步行速度。                                              |
| 慢跑速度     | `float`                  | `2.6` (米/秒) | `>= 0.1`         | 指令慢跑速度。                                              |
| 每秒旋转角度   | `float`                  | `360`       | `>= 10`          | 沿路径跟随时的转向速率。原地转身本身由动画驱动，而不是此值。                       |
| 绘制 Gizmo | `布尔值`                    | `是`         | —                | 在对象被选中时，在 Scene 视图中绘制当前 NavMesh 路径。仅限编辑器。            |

{% hint style="info" %}
**步行速度** 和 **慢跑速度** 会被覆盖 `ConvaiBodyAnimationController`其测得的动画片段速度会在角色上存在控制器时覆盖这些字段。只有在不使用身体动画控制器、仅单独使用 NavMesh 时，才直接设置这些字段。
{% endhint %}

在运行时使用以下方式更改配置： `SetSpeedProfile(LocomotionSpeedProfile)`。更改会立即应用于当前移动，包括步行↔慢跑的步态切换，并应用于之后的每一次 `MoveTo`.

***

### 从脚本移动角色

```csharp
using Convai.Modules.BodyAnimation.Components;
using UnityEngine;

public sealed class WalkToButton : MonoBehaviour
{
    [SerializeField] private Transform character;
    [SerializeField] private Transform destination;

    public void OnWalkHereClicked()
    {
        ConvaiNavMeshLocomotion locomotion = character.GetComponent<ConvaiNavMeshLocomotion>();
        bool started = locomotion.MoveTo(destination.position);
        if (!started)
            Debug.Log("无法开始移动——请查看控制台中的原因。");
    }
}
```

`MoveTo` 会将目的地采样到 NavMesh 上，并返回 `否` 当其周围 2 米内没有可行走地面，或者代理不在已烘焙的 NavMesh 上时返回。 `IsMoving`, `Speed`, `RemainingDistance`以及 `Destination` 读取当前移动。

`Stop()` 和 `StopGracefully()` 以不同方式结束一次移动：

|                    | 在以下情况使用它：                    | 角色的行为                                                                          |
| ------------------ | ---------------------------- | ------------------------------------------------------------------------------ |
| `Stop()`           | 行走被打断——新的指令到来、组件被禁用，或其他系统接管。 | 在当前帧原地停止。路径被清除，速度同时归零。                                                         |
| `StopGracefully()` | 角色决定停下——等待某人跟上，或接近跟随距离。      | 继续沿当前路径走到前方的制动点，减速，并完成稳稳的停步。返回 `否` 仅当根本没有任何办法继续跑出去时才会返回，在这种情况下，移动会通过 `Stop()`. |

`StopGracefully()` 仍会报告 `MoveEnded(false)` ——角色停下了，但没有到达被送往的位置。使用 `Warp(Vector3)` 可传送代理，并清除任何路径。

***

### 使用自定义运动源

`ConvaiBodyAnimationController` 公开一个 Inspector 字段， **运动提供器覆盖**，序列化为 `_locomotionProviderOverride`。分配一个 `MonoBehaviour` 实现了 `IConvaiLocomotionSource` 以从不同于 `ConvaiNavMeshLocomotion` ——一个 `CharacterController`、根运动、补间动画或第三方导航。

```csharp
using Convai.Modules.BodyAnimation.Core.Locomotion;
using System;
using UnityEngine;

public sealed class CustomMover : MonoBehaviour, IConvaiLocomotionSource
{
    public bool IsMoving { get; private set; }
    public bool PathPending => false;
    public float Speed { get; private set; }
    public float DesiredSpeed { get; private set; }
    public float RemainingDistance { get; private set; }
    public float SignedAngleToSteering { get; private set; }
    public Vector3 Destination { get; private set; }
    public event Action<bool> MoveEnded;

    // 使用你自己的移动代码驱动这些字段，并调用
    // 在到达时调用 MoveEnded(true)，在取消时调用 MoveEnded(false)。
}
```

`IConvaiLocomotionSource` 是最基本的契约—— `IsMoving`, `PathPending`, `Speed`, `DesiredSpeed`, `RemainingDistance`, `SignedAngleToSteering`, `Destination`，以及 `MoveEnded` 事件。同一组件上还会发现三个额外接口，作为可选能力；缺少其中一个只会禁用需要它的功能：

| 接口                          | 增加                                                                                                                                                                                                                                   |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `IConvaiLocomotionCommands` | `MoveTo(Vector3)` / `Stop()` ——允许 `ConvaiBodyAnimationController` 以及 Convai 自己的动作执行器通过你的提供器发出移动指令。                                                                                                                                   |
| `IConvaiManagedLocomotion`  | 与动画片段同步的启动、停止和转向（`FreezeAgent`, `BeginManagedMotion`, `SetManagedSpeed`, `EndManagedMotion`, `SetAnimationStartGate`, `ReleaseAnimationStartGate`, `CompleteMoveFromAnimation`, `ConfigureSpeeds`）。没有它，运动仍会与你的源报告的速度和方向同步，只是混合方式更简单。 |
| `IConvaiAnchorAlignment`    | `BeginRootAlignment()` / `EndRootAlignment(Vector3)` ——对……的根写入权限 `PlayActionAt`的对齐插值。没有它， `PlayActionAt` 会退化为以未对齐方式播放动作。                                                                                                             |

`ConvaiNavMeshLocomotion` 实现了全部四个接口，这就是它无需配置覆盖字段也能工作的原因。

***

### 在没有 ConvaiNavMeshLocomotion 的情况下报告行进

`ConvaiTravelIntent` 是角色级别的接缝，用来表明角色正在前往某处，因此同伴——例如 Convai Gaze 在行走时观察路径——可以在其旅行过程中表现得不同。 `ConvaiNavMeshLocomotion` 会自动为其提供并向其报告；由一个 `CharacterController`、根运动、补间动画或不含 Convai 组件的导航代码移动的角色，仍会获得基本覆盖，因为 `ConvaiTravelIntent` 它还会观察角色自身的 Transform 位移。

对于脚本控制的移动器，请调用 `ReportTravel` 在移动持续的每一帧都进行报告——停止重复的报告会自行过期：

```csharp
using Convai.Runtime.Embodiment;
using UnityEngine;

public sealed class ScriptedMover : MonoBehaviour
{
    [SerializeField] private Transform character;
    private ConvaiTravelIntent _travelIntent;

    private void Awake()
    {
        _travelIntent = character.GetComponent<ConvaiTravelIntent>();
    }

    private void MoveTowards(Vector3 destination, float speed01)
    {
        // 以一次调用报告方向、速度（0..1）和剩余距离，
        // 并将目的地声明为这段行程的主题。
        _travelIntent.ReportTravelTo(destination, speed01);
    }

    private void StopMoving()
    {
        _travelIntent.ClearTravel();
    }
}
```

| 方法                                                                             | 用途                                                 |
| ------------------------------------------------------------------------------ | -------------------------------------------------- |
| `ReportTravel(Vector3 worldDirection, float speed01)`                          | 报告本帧的方向和归一化速度。不包含剩余距离详情。                           |
| `ReportTravel(Vector3 worldDirection, float speed01, float remainingDistance)` | 相同，但包含已知的剩余距离。                                     |
| `ReportTravelTo(Vector3 destination, float speed01)`                           | 便捷方法：报告朝向 `destination` 并在一次调用中将其设为主题。             |
| `ClearTravel()`                                                                | 立即结束已报告的行进，不必等待其过期。                                |
| `SetSubject(Transform subject)`                                                | 声明这段行进是关于 `subject` （被跟随的人）——这会从 Gaze 等同伴那里获得定期注视。 |
| `SetSubject(Vector3 worldPosition)`                                            | 声明这段行进是关于一个固定地点。                                   |
| `ClearSubject()`                                                               | 忘记这段行进的主题。角色会继续看着前方道路。                             |

`ConvaiTravelIntent` 会在角色实际移动的瞬间自动配置——除非你想更改其检测阈值或关闭自动检测，否则无需手动添加：

| 字段     | 类型      | 默认           | 范围        | 说明                                              |
| ------ | ------- | ------------ | --------- | ----------------------------------------------- |
| 自动检测移动 | `布尔值`   | `是`          | —         | 通过角色自身的 Transform 检测移动，而无需任何 Convai 运动组件或代码报告它。 |
| 移动速度阈值 | `float` | `0.35` (米/秒) | `>= 0.01` | 低于该速度时，移动会被视为停稳、抖动或原地转身。                        |
| 移动持续秒数 | `float` | `0.25`       | `>= 0`    | 移动需要持续多长时间才会被视为一次行进。                            |
| 报告超时秒数 | `float` | `0.5`        | `>= 0.05` | 已报告的行进在不重复报告的情况下保持有效的时长。                        |
| 参考行进速度 | `float` | `3.6` (米/秒)  | `>= 0.1`  | 在归一化报告速度时，被视为“全力”的速度。仅在没有其他来源提供速度时使用。           |

***

### 故障排查

#### 角色不移动

**症状：** `MoveTo` 返回 `否`，或者控制台记录一条带有角色名称的警告。

**原因：** 代理没有站在已烘焙的 NavMesh 上，或者目的地 2 米内不存在可行走地面。

**解决方法：** 在地面上烘焙一个 NavMesh（`窗口 > AI > Navigation`）并确认角色从其上开始。

**验证：** `MoveTo` 返回 `是` 和 `IsMoving` 变为 `是`.

#### 角色在……之后会滑行 `Stop()`

**症状：** 角色的脚停下了，但身体还会滑行一小会儿。

**原因：** `Stop()` 会在动画被告知移动结束的同一帧清除路径并将速度归零，因此一个惯性滑行的代理（慢跑速度下大约 0.8 米）可能会跑在已稳定的动画前面。

**解决方法：** 使用 `StopGracefully()` 用于明确的停止，而不是 `Stop()`；把 `Stop()` 留给真正的中断。

**验证：** 角色的稳停动画会在它实际停下的位置落地。

***

### 下一步

{% content-ref url="/pages/13dbbe592147612edba60a45974dc24d1b9f3e61" %}
[播放动作和手势](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/play-actions-and-gestures.md)
{% endcontent-ref %}

{% content-ref url="/pages/5e8d8a3b4fe5ee06381a3ed5c6ba13e6f297f706" %}
[身体动画配置参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/config-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/bfa444bd8429ef5ecce69dad819e8a8afcb74544" %}
[身体动画脚本参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/scripting-reference.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/configure-locomotion.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
