> 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/play-actions-and-gestures.md).

# 播放动作和手势

从脚本中让 Convai 角色播放命名动作、锚定动作和指向手势，并读取每次调用返回的句柄。

播放一个命名动作或手势，将角色走到锚点并在那里执行动作，或者指向目标——全部都可通过脚本实现，借助 `ConvaiBodyAnimationController` 以及每次调用返回的句柄。请在 Convai Body Animation 已运行于某个角色且其动画集已编写好你想触发的动作或指向方向后，再使用本页。

***

### 前提条件

* `ConvaiBodyAnimationController` 已添加到角色，并分配了动画集。参见 [构建动画集](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/build-an-animation-set.md) 用于编写动作和指向方向。
* 对控制器的引用，可通过以下方式解析： `GetComponent<ConvaiBodyAnimationController>()` 在角色上。

{% hint style="info" %}
`PlayAction`, `PlayActionAt`以及 `PointAt` 绝不会返回 `null`. 失败时——运行时尚未构建、动作或手势未知，或者请求无法满足——它们都会返回一个已完成且已失败的句柄。请检查 `Failed` 和 `FailureReason` 句柄上的相应状态，而不是检查它是否为 null。
{% endhint %}

***

### 播放一个命名动作或手势

调用 `PlayAction` 使用该动作的名称或别名——匹配不区分大小写，并将空格、连字符和下划线视为等价。

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

public sealed class WaveOnCue : MonoBehaviour
{
    [SerializeField] private ConvaiBodyAnimationController bodyAnimation;

    public async void PlayWave()
    {
        BodyAnimationActionHandle handle = bodyAnimation.PlayAction("wave",
            new ActionPlayOptions { HoldSeconds = 3f });

        if (handle.Failed)
        {
            Debug.Log($"Wave didn't play: {handle.FailureReason}");
            return;
        }

        bool completedNaturally = await handle.Completion; // 被中断时为 false
    }
}
```

`ActionPlayOptions` 字段：

| 字段                 | 类型      | 默认含义              | 说明                                 |
| ------------------ | ------- | ----------------- | ---------------------------------- |
| `SpeedMultiplier`  | `float` | `<= 0` = 条目默认值    | 在该条目作者设定的速度基础上的播放速度倍率。             |
| `HoldSeconds`      | `float` | `<= 0` = 持续到停止为止  | 对于持续到停止为止的动作：在主循环经过这么多秒后自动请求停止。    |
| `FadeInSeconds`    | `float` | `<= 0` = 条目/配置默认值 | 图层淡入覆盖值。                           |
| `FadeOutSeconds`   | `float` | `<= 0` = 条目/配置默认值 | 图层淡出覆盖值。也用于 `StopActionImmediate`. |
| `WeightMultiplier` | `float` | `<= 0` = 现有行为     | 动作图层权重倍率。                          |

`BodyAnimationActionHandle` （由 `PlayAction`):

| 成员                                           | 说明                                                          |
| -------------------------------------------- | ----------------------------------------------------------- |
| `ActionName`                                 | `字符串` 返回）——请求的名称或别名。                                        |
| `Failed` / `FailureReason`                   | 该请求是否从未开始，以及原因。                                             |
| `IsDone`                                     | `是` 在动作完全结束或被中断后。                                           |
| `Completion`                                 | `Task<bool>` ——在 `是` 完整播放完成时解析为 `否` 被中断时解析为                 |
| `Stop()`                                     | 请求平滑停止；若已编排该条目的结尾段，则会播放结尾段。可安全重复调用。                         |
| `StopImmediate(float blendOutSeconds = -1f)` | 立即停止，并在 `blendOutSeconds` (`<= 0` = 动作解析后的淡出时长），跳过剩余链条或结尾段。 |

直接从控制器停止或中断当前正在播放的动作，使用 `StopAction()` （平滑停止，播放结尾段）或 `StopActionImmediate(float blendOutSeconds = -1f)` （立即交叉淡化）。 `CurrentActionName` 读取当前正在播放的动作名称；若没有则为空。

后端触发的手势通过与 `PlayAction`相同的名称/别名匹配来解析，因此名为 `"pick_up"` 在 Convai 响应中的动作会触发一个名为 `"Pick Up"` 或别名 `"pick-up"` ，无需额外连接。

***

### 播放一个带锚点的动作

`PlayActionAt` 将角色走到锚点，根节点对齐到其姿态，然后播放命名动作——即“坐到长椅上”/拾取/使用道具的流程。

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

public sealed class SitOnBench : MonoBehaviour
{
    [SerializeField] private ConvaiBodyAnimationController bodyAnimation;
    [SerializeField] private Transform benchAnchor;

    public async void SitDown()
    {
        PlayActionAtHandle handle = bodyAnimation.PlayActionAt(benchAnchor, "sit");
        bool completedNaturally = await handle.Completion; // 被取消/被拒绝时为 false
    }
}
```

| 方法                                                                                                                                     | 说明                                                             |
| -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `PlayActionAt(Transform anchor, string actionNameOrAlias)`                                                                             | 走到 `锚点`，对齐并播放。                                                 |
| `PlayActionAt(Transform anchor, string actionNameOrAlias, ActionAnchorOptions anchorOptions, ActionPlayOptions playOptions = default)` | 相同，但带有显式的接近/对齐调节以及动作播放微调。显式的 `anchorOptions` 会覆盖该动作条目自身编写的默认值。 |

`ActionAnchorOptions` 没有公开构造函数可从脚本中使用自定义值来创建实例——其字段只能通过 Inspector 在动作条目的 **锚点选项** 字段中设置。仅在重用另一个条目的调节参数时，才向该重载传入一个已存在的编写实例；否则请调用双参数重载，让该条目自身编写的默认值生效。

对齐时会忽略锚点的高度——只考虑其 XZ 位置和偏航，因此请将锚点放在角色预定的站立位置，而不是座位或道具的高度。

`PlayActionAtHandle` （由 `PlayActionAt`):

| 成员                         | 说明                                                                                       |
| -------------------------- | ---------------------------------------------------------------------------------------- |
| `ActionName`               | `字符串` ——请求的动作名称。                                                                         |
| `Phase`                    | `PlayActionAtPhase` ——请求当前阶段。                                                            |
| `Failed` / `FailureReason` | 该请求是否从未开始，以及原因。                                                                          |
| `IsDone`                   | `是` 在请求完成或被取消后。                                                                          |
| `Completion`               | `Task<bool>` ——在 `是` 动作完整播放完成时， `否` 被取消时。                                                |
| `Cancel()`                 | 在请求当前所处的任何阶段取消：在 `Approaching`期间停止移动；在 `Aligning`期间冻结对齐插值，或在 `PlayingAction`期间平滑停止动作。幂等。 |

***

### 指向一个目标或位置

`PointAt` 将手臂抬向世界坐标中的某个位置或一个移动中的 Transform，在最高点保持，然后放下。

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

public sealed class PointAtProp : MonoBehaviour
{
    [SerializeField] private ConvaiBodyAnimationController bodyAnimation;
    [SerializeField] private Transform target;

    public async void Point()
    {
        BodyAnimationPointingHandle handle = bodyAnimation.PointAt(target, holdSeconds: 3f);
        await handle.Completion;
    }
}
```

| 重载                                                          | 说明                                     |
| ----------------------------------------------------------- | -------------------------------------- |
| `PointAt(Vector3 worldPosition, float holdSeconds = -1f)`   | 指向一个固定的世界位置。                           |
| `PointAt(Transform target, float holdSeconds = -1f)`        | 指向一个（移动的）Transform，在保持期间重新瞄准。          |
| `PointAt(Transform target, in PointingPlayOptions options)` | 相同，但带有播放调整：速度、淡入/淡出时长，以及保持时间到期后如何自动释放。 |

`holdSeconds < 0` （或 `PointingPlayOptions.HoldSeconds <= 0`）保持直到 `StopPointing`/`Release()` 被调用。

{% hint style="warning" %}
**`HoldSeconds` 表示的只是指向动作最高点的暂停，而不是整个手势的总时长。** 抬起和放下属于动画片段本身。 `PointingPlayOptions.Speed` 会乘以抬起和放下的速度，而 `ReleaseStyle` 设置为 `混合` 在保持结束时让姿态直接退出，而不是播放下臂收尾段。对于大约一秒的指向，请设置 `Speed = 1.5f` 与 `ReleaseStyle = PointingReleaseStyle.Blend`.
{% endhint %}

`PointingPlayOptions` 字段：

| 字段                 | 类型                     | 默认含义                  | 说明                                                               |
| ------------------ | ---------------------- | --------------------- | ---------------------------------------------------------------- |
| `Speed`            | `float`                | `<= 0` = 原生（`1`)      | 抬起/放下速度倍率。保持本身不受影响。                                              |
| `HoldSeconds`      | `float`                | `<= 0` = 保持直到释放       | 在最高点保持的秒数。                                                       |
| `BlendInSeconds`   | `float`                | `<= 0` = 配置值 `指向淡出秒数` | 图层淡入秒数。                                                          |
| `BlendOutSeconds`  | `float`                | `<= 0` = 配置值 `指向淡出秒数` | 图层淡出秒数。                                                          |
| `ReleaseStyle`     | `PointingReleaseStyle` | `播放尾段`                | 到期后的 `HoldSeconds` 自动释放会做什么：播放下臂收尾段（`播放尾段`，默认）还是立即将姿态交叉淡出（`混合`). |
| `WeightMultiplier` | `float`                | `<= 0` = 现有行为         | 指向图层权重倍率。                                                        |

`BodyAnimationPointingHandle` （由每个 `PointAt` 重载返回）：

| 成员                                              | 说明                           |
| ----------------------------------------------- | ---------------------------- |
| `Failed` / `FailureReason`                      | 该请求是否从未开始，以及原因。              |
| `IsDone`                                        | `是` 在指向手势完全结束后（手臂放下）。        |
| `Completion`                                    | `Task` ——在手势完全结束后解析。         |
| `Release()`                                     | 立即结束保持；完成前会播放下臂收尾段。          |
| `ReleaseImmediate(float blendOutSeconds = -1f)` | 立即停止并将姿态交叉淡出，跳过下臂收尾段。        |
| `SetSpeed(float speed)`                         | 实时调整正在运行手势的抬起/放下速度。在保持期间无效果。 |

直接从控制器停止当前的指向保持，使用 `StopPointing()` （平滑停止，播放收尾段）或 `StopPointingImmediate(float blendOutSeconds = -1f)` （立即交叉淡化）。

***

### 面向一个方向并设置会话锚点

`FaceTowards(Vector3 worldDirection, string reason = "FaceTowards")` 使用原地转身动画族让角色旋转并面向某个方向——无需 `NavMeshAgent` 需要。它会在 `否` 当请求无法满足时返回（功能已禁用、缺少动画片段、移动占用中）。

```csharp
bodyAnimation.FaceTowards(playerTransform.position - character.position, "greeting turn");
```

`SetConversationAnchor(Transform anchor)` 会覆盖 Transform 的社交间距、邻近表现力以及环境抑制，将其视为“此角色正在与之交谈的人”——默认解析链最终止于 `Camera.main`，而这对 XR 设备、第二个本地玩家，或者没有 `MainCamera` 标记。 `ClearConversationAnchor()` 会恢复为默认解析链。

***

### 故障排查

#### 句柄的 `Failed` 是 `是`

**症状：** 该调用会立即返回，并且不会播放任何内容。

**原因：** `FailureReason` 会指出原因——常见原因包括 `"runtime not built"` （图尚未就绪）， `"unknown action"` （动画集中没有匹配的名称或别名）， `"an active non-interruptible action is still playing"`, `"no locomotion"` (`PlayActionAt` ，且角色上没有 `ConvaiNavMeshLocomotion` ），或者 `"set has no pointing clips"`.

**解决方法：** 用于 `"runtime not built"`，请订阅 `ConvaiBodyAnimationController.RuntimeReady` 并从该处理程序中调用，而不是 `Start()`/`Awake()` ——过早调用也会记录到一个延迟槽中并自动重放，但该事件可保证调用生效。对于其他原因，请检查动画集中已编写的动作和指向方向，或者添加 `ConvaiNavMeshLocomotion`.

**验证：** `handle.Failed` 是 `否` 和 `Completion` 在手势播放完成后解析。

***

### 下一步

{% content-ref url="/pages/2f6d4e1228a37a16fdb14e3c1941f0d1c8cf7692" %}
[配置移动](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/embodiment/body-animation/configure-locomotion.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/play-actions-and-gestures.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.
