---
title: 使用动画事件
slug: world-sdk-guide-ch/x8JH_eN8F0Q6tRc07v99U
docTags: 
createdAt: 2024-09-02T08:34:13.135Z
---

动画事件提供了一种在动画剪辑的特定时间调用自定义函数或执行操作的方法。

这允许动画与脚本之间的交互，并在动画的确切时机执行操作时非常有用。



### 向对象动画添加事件

以下示例向一个动画对象添加了一个动画事件。

该结构在每次执行事件时将信息输出到控制台日志，通过向上下重复的动画添加一个向上事件和一个向下事件。

::Image[]{src="https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-xO_1nL7IRhCFVsPcmwFGM-20240904-102422.gif" size="74" width="1310" height="864" position="center" showCaption="false"}



1\) 选择 GameObject > 3D 对象 > 立方体，以在场景中创建一个立方体。

- 选择创建的立方体，并通过单击检查器面板中的添加组件按钮添加 Animator 组件。
- 将立方体 Animator Controller 应用到添加的 Animator。
- 将立方体动画剪辑应用到立方体 Animator Controller 的入口，以便在播放场景时可以播放立方体动画。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-wMEQaupztsnprYFC5J74G-20240904-102422.png)



请参考以下指南以进行 Animator 注册和动画剪辑编辑。

:::hint{type="info"}
**📘 Unity 动画事件指南**
[https://docs.unity3d.com/kr/current/Manual/script-AnimationWindowEvent.html](https://docs.unity3d.com/kr/current/Manual/script-AnimationWindowEvent.html)
:::

:::hint{type="info"}
**📘 Unity 学习动画教程**
[https://learn.unity.com/tutorial/may-18-animation?language=en#](https://learn.unity.com/tutorial/may-18-animation?language=en#)
:::



2\) 将以下属性添加到立方体动画剪辑中。

- 在立方体动画剪辑的0秒和2秒部分有一个位置关键帧（x: 0, y:0, z:0）。
- 在立方体动画剪辑的1秒部分有一个位置关键帧（x: 0, y:1, z:0）。

(1) 在立方体动画剪辑的1秒部分注册一个动画事件。

- 函数：调用
- 字符串：向上

(2) 在立方体动画剪辑的2秒部分注册一个动画事件。

- 函数：调用
- 字符串：向下

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-Yzu5ZqoYDS9lZKn-vpM9K-20240904-102422.png)



:::hint{type="success"}
- 在Unity中通过C#编写动画事件时，函数名称写在Function中。
- 然而，为了在Typescript中调用动画事件，Invoke必须写在Function中，函数名称必须写在String中，参数值。
:::



3\) 创建项目 > 创建 > ZEPETO > TypeScript，并将其重命名为ObjectAnimationController。

- 编写如下示例脚本。

:::CodeblockTabs
ObjectAnimationController.ts

```typescript
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'

export default class ObjectAnimationController extends ZepetoScriptBehaviour {

    public Up() {
        console.log(`Cube is Up`);
    }

    public Down(){
        console.log(`Cube is Down`);
    }
}
```
:::

4\) 如果你按下播放按钮运行它，你可以看到在立方体动画播放时控制台日志输出。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-nI5ZbpXCgO1e15p48DAX4-20240904-102422.gif)



### 使用动画事件在本地角色上播放脚步声。

为了将动画事件添加到ZEPETO角色的Animator中，事件必须在运行时添加，即在角色创建时。

以下示例在运行时将事件应用于本地玩家Animator的特定动画剪辑，并在事件触发时播放脚步声。

1\) 创建一个 FootStepController 脚本，该脚本将应用于本地玩家的动画器对象。FootStepController 负责向 Animator 添加动画事件。

- 创建项目 > 创建 > ZEPETO > TypeScript，并将其重命名为 FootStepController。
- 按照以下方式编写脚本。

:::CodeblockTabs
FootStepController.ts

```Text
import { AnimationEvent, Animator, AudioClip, AudioSource } from 'UnityEngine';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';

export default class FootStepController extends ZepetoScriptBehaviour {

    // 用于脚步声效果的声音
    public footStepSound: AudioClip;

    // 播放脚步声的音频源
    private _zepetoAudio: AudioSource;

    // 添加动画事件以触发脚步声
    public AddAnimationEvent() {
        
        // 获取附加到此游戏对象的 Animator 组件
        const anim = this.GetComponent<Animator>();

        // 检查 Animator 组件是否存在
        if (anim != null) {
            // 创建用于脚步声的 AudioSource
            this.SetAudio();

            // 指定在动画剪辑中播放脚步声的时间
            const runSoundEventTimes = [0.1, 0.6];

            // 向指定的动画剪辑添加动画事件
            this.AddAnimationClipEvent(anim, "move_walk", runSoundEventTimes);
        }
    }

    // 设置用于播放脚步声的 AudioSource
    private SetAudio() {

        // 创建并配置 AudioSource 组件
        this._zepetoAudio = this.gameObject.AddComponent<AudioSource>();
        this._zepetoAudio.clip = this.footStepSound;
    }

    // 激活脚步声
    public ActivateFootSteps() {

        // 输出日志消息，指示脚步声正在播放
        console.log("脚步声!");

        // 使用配置的 AudioSource 播放脚步声
        this._zepetoAudio.Play();
    }

    // 向指定的动画剪辑添加动画事件
    private AddAnimationClipEvent(anim: Animator, clipName: string, eventTimes: number[]) {

        // 初始化动画剪辑的索引
        let animationClipIndex: number;

        // 从 runtimeAnimatorController 获取所有动画剪辑
        const clips = anim.runtimeAnimatorController.animationClips;

        // 查找指定动画剪辑的索引
        for (let i = 0; i < clips.length; i++) {
            if (clips[i].name == clipName) {
                animationClipIndex = i;
            }
        }

        // 遍历指定的事件时间
        eventTimes.forEach(eventTime => {

            // 创建一个新的 AnimationEvent 用于触发脚步声
            const newEvent: AnimationEvent = new AnimationEvent();
            newEvent.time = eventTime;
            newEvent.functionName = "Invoke";
            newEvent.stringParameter = "ActivateFootSteps";

            // 将新的 AnimationEvent 添加到动画剪辑中
            clips[animationClipIndex].AddEvent(newEvent);
        });
    }
}
```
:::

- 脚本描述
  - FootStepController 在事件发生时添加动画事件并播放脚步音效。
  - SetAudio() 将 AudioSource 组件添加到对象上以应用脚步音效。
  - AddAnimationClipEvent() 将动画事件添加到指定的动画剪辑中。您必须将以下元素作为参数值。
    - 本地玩家的 Animator
    - 应用事件的动画剪辑名称
    - 应用事件的动画时间数组
- 在通过 AddAnimationClipEvent() 注册的新事件中，设置了以下项目。

(1) newEvent.time : 事件触发时的动画时间。值为0表示全长的开始，值为1表示全长的结束。

(2) newEvent.functionName : 事件触发的函数名称。在TypeScript中，应将其类型设置为"Invoke"。

(3) newEvent.stringParameter : 事件执行函数时设置的参数值。TypeScript列出了要执行的函数名称。

- 当事件发生时，执行ActivateFootSteps()并播放脚步声。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-PQibq7pcRcM2_Iwp6BIwW-20240904-102422.png)



2\) 现在创建一个FootStepManager，当本地玩家在运行时创建时，将FootStepController组件添加到本地玩家的Animator对象中。

- 项目 > 创建 > ZEPETO > TypeScript，并将其重命名为FootStepManager。
- 按照以下方式编写脚本。

:::CodeblockTabs
FootStepManager.ts

```Text
import { SpawnInfo, ZepetoPlayers } from 'ZEPETO.Character.Controller'
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import FootStepController from './FootStepController'
import { WorldService } from 'ZEPETO.World'
import { AudioClip } from 'UnityEngine';

export default class FootStepManager extends ZepetoScriptBehaviour {

    // 要播放的脚步声
    public footStepSound: AudioClip;

    Start() {

        // 当本地玩家被添加时的事件监听器
        ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {

            // 访问本地玩家的Zepeto角色
            const zepetoCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;

            // 将FootStepController脚本附加到ZepetoAnimator的GameObject
            const footStepController = zepetoCharacter.ZepetoAnimator.gameObject.AddComponent<FootStepController>();

            // 为FootStepController设置footStepSound
            footStepController.footStepSound = this.footStepSound;

            // 添加动画事件以触发脚步声
            footStepController.AddAnimationEvent();

        });
    }

}
```
:::

- 脚本描述
  - 在FootStepManager检测到本地玩家被添加后，它将FootStepController脚本附加到玩家的ZepetoAnimator的GameObject上。
  - FootStepController负责添加播放脚步声的动画事件。

:::hint{type="success"}
* 由Animator的动画事件执行的函数的脚本必须完全应用于相应的Animator组件所应用的对象。
* 因此，您需要找到ZepetoAnimator.gameObject并将FootStepController脚本作为AddComponent应用。
:::



3\) 将脚步声应用于 FootStepManager 的 footStepSound。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-xzBMK0F7JDaBCBNoarBm0-20240904-102422.png)



4\) 如果您按下播放按钮运行它，您可以看到声音会根据本地玩家的步伐在他们走路时播放。

::Image[]{src="https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-sOZL6vMX48bJ25maFSkLR-20240904-102423.gif" size="70" width="1328" height="745" position="center" showCaption="false"}



:::hint{type="success"}
**👍 提示**

- ZEPETO 基础动画师的行走状态由多个动画混合和状态组成。
- 由于示例仅将声音应用于 move\_walk 状态，您可以自定义 AddAnimationClipEvent() 并直接输入到快速行走和奔跑等状态中。
- 当声音应用于多个动画状态时，由于动画混合，脚步声可能会重叠。为了解决这个问题，编写一个额外的条件语句，以便在 AnimationEvent.animatorClipInfo.weight 为 0.5 时播放相应的声音。
:::

:::hint{type="danger"}
**❗️ 已知问题**

- 按照开发指南样式实施时，可能会出现以下错误消息，但执行应该没有问题。
:::

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-233nZRPAw731S17OMHzra-20240904-102422.png)



