## 1. Project Overview & Quickstart (galacean/engine)

## File: README.md

# Galacean Engine

[](https://www.npmjs.com/package/@galacean/engine)


[](https://codecov.io/gh/galacean/engine)

[Galacean Engine](https://www.galacean.com/engine) is a high-performance, real-time interactive engine designed primarily for web and mobile platforms. It employs a component system architecture and emphasizes ease of use and lightweight design. Developers can create projects using either editor or pure code.



## Features

- 🖥 &nbsp;**Platform** - Support HTML5 and wechat minigame
- 🔮 &nbsp;**Graphics** - Advanced 2D + 3D graphics engine
- 🏃 &nbsp;**Animation** - Powerful animation system
- 🧱 &nbsp;**Physics** - Powerful and easy-to-use physical features
- 🎨 &nbsp;**GUI** - Flexible UI system with drag-and-drop and dynamic interactions
- 👆 &nbsp;**Input** - Easy-to-use interactive capabilities
- 📑 &nbsp;**Scripts** - Use TypeScript to write logic efficiently

## Usage

### Using Editor
We recommend using [**Editor**](https://galacean.antgroup.com/editor)  for a streamlined workflow that enables seamless integration between artists and developers. Its intuitive visual tools allow artists to quickly create scenes and enable developers to write custom logic, with convenient platform export. You can even create projects based on pre-built case templates.




### Using Pure Code
If you want to build your project using pure code via runtime, install the engine from npm:

```sh
npm install @galacean/engine
```

Create a simple scene:

```typescript
import { BlinnPhongMaterial, Camera, DirectLight, MeshRenderer, WebGLEngine, PrimitiveMesh } from "@galacean/engine";

// Create engine by passing in the HTMLCanvasElement id and adjust canvas size
const engine = await WebGLEngine.create({ canvas: "canvas-id" });
engine.canvas.resizeByClientSize();

// Get scene and create root entity
const scene = engine.sceneManager.activeScene;
const rootEntity = scene.createRootEntity("Root");

// Create light
const lightEntity = rootEntity.createChild("Light");
const directLight = lightEntity.addComponent(DirectLight);
lightEntity.transform.setRotation(-45, -45, 0);
directLight.intensity = 0.4;

// Create camera
const cameraEntity = rootEntity.createChild("Camera");
cameraEntity.addComponent(Camera);
cameraEntity.transform.setPosition(0, 0, 12);

// Create sphere
const meshEntity = rootEntity.createChild("Sphere");
const meshRenderer = meshEntity.addComponent(MeshRenderer);
const material = new BlinnPhongMaterial(engine);
meshRenderer.setMaterial(material);
meshRenderer.mesh = PrimitiveMesh.createSphere(engine, 1);

// Run engine
engine.run();
```

## Contributing

This repository contains the runtime's source code and documentation. Everyone is welcome to contribute—whether you find a bug, have a feature request, or want to tackle a task from our roadmap, please get in touch.

Make sure to read the [Contributing Guide](.github/HOW_TO_CONTRIBUTE.md) / [贡献指南](https://github.com/galacean/engine/wiki/%E5%A6%82%E4%BD%95%E4%B8%8E%E6%88%91%E4%BB%AC%E5%85%B1%E5%BB%BA-Oasis-%E5%BC%80%E6%BA%90%E4%BA%92%E5%8A%A8%E5%BC%95%E6%93%8E) before submitting changes.

## Clone

Prerequisites:

- [git-lfs](https://git-lfs.com/) (Install by official website)

Clone this repository:

```sh
git clone git@github.com:galacean/engine.git
```

## Build

Prerequisites:

- [Node.js v15.0.0+](https://nodejs.org/en/) and NPM (Install by official website)
- [PNPM](https://pnpm.io/) (Install globally by `npm install pnpm`)

In the folder where you have cloned the repository, install the build dependencies using pnpm:

```sh
pnpm install
```

Then, to build the source, using npm:

```sh
npm run b:all
```

## Links

- [Official Site](https://galacean.antgroup.com/engine)
- [Editor](https://galacean.antgroup.com/editor)
- [Documentation](https://galacean.antgroup.com/engine/docs)

## License

The engine is released under the [MIT](https://opensource.org/licenses/MIT) license. See LICENSE file.

---

## File: docs/zh/animation/examples/blending.mdx

---
order: 3
title: 动画混合
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中实现动画的多层混合。通过简单的步骤，你将学习如何让一个模型混合播放多个动画。
如果你是第一次使用动画编辑器，建议先阅读前面几篇文档：

[1.播放模型中的动画](/docs/animation/examples/playAnimation/)

[2.动画复用](/docs/animation/examples/reuseAnimation/)

[3.动画过渡](/docs/animation/examples/crossFade/)

## 0. 准备工作

1. 在开始之前，我们需要有一个有动画的模型并且它需要包含一个适合混合的动画。如果你没有模型，可以在点此下载 [模型](https://mdn.alipayobjects.com/oasis_be/afts/file/A*N99IQYh_g5YAAAAAAAAAAAAADkp5AQ/player.glb)。
然后我们将其拖入到场景中。

2. 我们要有一个已编辑好的 `动画控制器` 。请参考：[编辑动画控制器](/docs/animation/examples/crossFade/#3-编辑动画控制器)。





## 1. 添加一个动画层

想要对动画进行混合播放，需要在动画控制器中添加一个 `动画层` 。当 `动画控制组件` 运行时，所有的 `动画层` 都会一起运行，多个 `动画层` 通过不同的权重来实现动画的混合。
关于 `动画层` 的详细介绍请参考 [动画层](/docs/animation/layer) 文档。

在 `Layers` 面板点击 `+` 添加一个 `动画层`




## 2. 编辑动画层
添加一个 `动画状态`（如果你不知道如何添加，请参考：[播放模型中的动画](/docs/animation/examples/playAnimation/)），绑定模型中的 **"shakeHead"** `动画片段`，并从 `entry` 连接到这个 `动画状态`，可以根据需要调整 `entry` 到这个 `动画状态`的过渡时间。



编辑之后为模型绑定此 `动画控制器` 。




## 3. 调整动画层权重

新添加的 `动画层` 的默认混合模式是 `Override`，我们先预览下效果。



可以看到我们之前在 `Base` 动画层添加的 `动画状态` 都失效了，因为混合模式是 `Override`，它会覆盖掉它下面的所有 `动画层` ，我们可以调整 `Layer1` 的权重看看权重对其的影响。



可以看到随着权重减少 `Layer1` 对动画的影响越小，而 `Base` 对动画的影响越大。

## 4. 调整混合模式

在本示例中，我们不希望 `Layer1` 的权重影响到 `Base` 动画层，因此我们将 `Layer1` 的混合模式修改为 `Additive`。



这样 `Layer1` 的动画会叠加到 `Base` 动画层上，它的权重修改只影响自身，而不会影响 `Base` 层的动画。
为了更好的观察 `Additve` 的效果，我们删掉 `Base` 层 `run` 到 `exit` 的过渡。

---

## File: docs/zh/animation/examples/controlAnimationByInput.mdx

---
order: 4
title: 用户输入控制动画
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中让用户的输入控制动画。通过简单的步骤，你将学习如何结合用户输入来控制动画的切换，让角色根据用户的交互进行相应的动作过渡。
如果你是第一次使用动画编辑器，建议先阅读前面几篇文档：

[1.播放模型中的动画](/docs/animation/examples/playAnimation/)

[2.动画复用](/docs/animation/examples/reuseAnimation/)

[3.动画过渡](/docs/animation/examples/crossFade/)

## 0. 准备工作

1. 在开始之前，我们需要有一个包含多个动画的模型， 并将其拖入到场景中。如果你没有模型，可以在点此下载 [模型](https://mdn.alipayobjects.com/oasis_be/afts/file/A*N99IQYh_g5YAAAAAAAAAAAAADkp5AQ/player.glb)。

2. 我们要有一个已编辑好动画过渡的 `动画控制器` 。请参考：[编辑动画控制器](/docs/animation/examples/crossFade/#3-编辑动画控制器)。





## 1. 添加动画控制参数

为了能够根据用户输入控制动画，首先需要给 `动画控制器` 加一些参数，根据这些参数来切换 `动画状态` 。

1. 切换到 `Parameters` 面板



2. 点击 `+` 添加一个浮点参数 `speed`，并将默认值设置为 0。



## 2. 添加过渡条件

之前为大家介绍了如何在 `动画状态机` 中添加 `动画过渡`（[详细介绍](/docs/animation/examples/crossFade/)），在之前的示例中，时间超过了 `ExitTime` 就会触发过渡。这里我们需要根据用户输入来触发过渡，所以我们需要添加一个 `过渡条件` 。

1. 在 `动画状态机` 中，选中从 `idle` 到 `walk` 的过渡，点击 `Add Condition` 添加一个 `Condition`。



`Condition` 的默认值刚好符合我们的需求，当 `speed` 大于 0 时，就会从 `idle` 过渡到 `walk`。

2. 同样的，选中从 `walk` 到 `run` 的过渡，添加一个 `Condition`，使其 `speed` 大于 5 时，从 `walk` 过渡到 `run`。



3. 选中 `run` 到 `exit` 的过渡，添加一个 `Condition` ，使其 `speed` 为 0 时，从 `run` 退出动画，重新回到 `idle`。



### 3. 添加脚本
我们需要一个脚本来接收用户输入，并根据用户输入来修改 `speed` 参数的值，以触发动画状态的切换。
添加脚本有两种方式：

1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `New Empty Script`



2. 点击添加资产按钮 `+`，选择 `New Empty Script`



### 4. 编辑并绑定脚本
1. 双击脚本，打开脚本编辑器，输入以下代码：

```typescript
import { Script, Animator, Keys } from '@galacean/engine';

export default class extends Script {
  animator: Animator;
  onStart() {
    this.animator = this.entity.getComponent(Animator);
  }

  onUpdate(deltaTime: number) {
    const inputManager = this.engine.inputManager;
    if (inputManager.isKeyHeldDown(Keys.KeyW)) {
        // 如果用户按下 W + Shift 键，则设置 speed 参数值为 6
      if (inputManager.isKeyHeldDown(Keys.ShiftLeft)) {
        this.animator.setParameterValue('speed', 6);
      } else {
        // 如果用户仅按住 W 键，则设置 speed 参数值为 1
        this.animator.setParameterValue('speed', 1);
      }
    }

    // 如果用户松开 W 键，则设置 speed 参数值为 0
    if (inputManager.isKeyUp(Keys.KeyW)) {
      this.animator.setParameterValue('speed', 0);
    }
  }
}
```

关于脚本的详细说明请参考：[脚本](/docs/script/script)

关于用户输入的详细说明请参考：[交互总览](/docs/input/input)

2. 找到 `Animator` 组件所在的实体，为其添加刚刚创建的脚本。



### 5. 预览动画
与之前的教程不同，因为本次我们为实体添加了脚本，仅点击预览动画按钮，脚本代码是不会生效的，我们需要点击项目预览（项目预览才会编译脚本文件）按钮来预览动画。



当角色处于 `idle` 状态时，按下 `W` 键，角色会从 `idle` 过渡到 `walk`，按住 `Shift` 键，角色会从 `walk` 过渡到 `run`，松开 `W` 键，角色会从 `run` 退出动画，重新回到 `idle`。



### 6. 优化动画
我们预览时会发现，如果我们在 `walk` 状态时松开 `W` 键，角色并不会切换到 `idle` 状态，而是继续播放 `walk` 动画。这是因为我们并没有从 `walk` 到 `idle` 的过渡。
而且从 `run` 到 `idle` 也没有过渡效果。因为我们是通过重新进入 `entry` 切换到 `idle` 的，而 `entry` 到 `idle` 的过渡的 `Duration` 是 0。想要从 `run` 过渡到 `idle` ，我们可以将 `run` 连接到 `idle`。
同理想要从 `run` 过渡到 `walk` 也需要添加过渡。



再次预览，我们发现 `walk` 到 `idle` 和 `run` 到 `walk` 的过渡效果已经生效了。



但是从 `run` 到 `idle` 的过渡效果并没有生效，而是先到 `walk` 再由 `walk` 到 `idle`，这是因为当 `speed` 参数为 0 时，`run` 到 `walk ` 和 `run` 到 `idle` 的 `Condition` 都满足。
为了避免这种情况，我们可以给 `run` 到 `walk` 再添加一个 `Condition` 并将其设置为 `speed` 大于 0。



这样当我们同时松开 `W` 键和 `Shift` 键时，角色就会从 `run` 直接过渡到 `idle` 了。

---

## File: docs/zh/animation/examples/crossFade.mdx

---
order: 2
title: 动画过渡
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中实现动画过渡。通过简单的步骤，你将学习如何让一个动画过渡到另一个动画。
如果你是第一次使用动画编辑器，建议先阅读前面几篇文档：

[1.播放模型中的动画](/docs/animation/examples/playAnimation/)

[2.动画复用](/docs/animation/examples/reuseAnimation/)

## 0. 准备工作

在开始之前，我们需要有一个包含多个动画的模型。如果你没有模型，可以在点此下载 [模型](https://mdn.alipayobjects.com/oasis_be/afts/file/A*AN3aSZzEijkAAAAAAAAAAAAADkp5AQ/player.glb)。

<Callout type="positive">
  在实际项目中强烈建议将FBX模型通过Blender等第三方工具转换为GLB/GLTF格式。Galacean目前仅支持解析GLB/GLTF模型。虽然可以直接将FBX拖入编辑器进行自动转换，但这种方式可能会导致转换后的模型动画数据过大，并可能出现还原问题。使用专业工具进行转换可以更好地控制转换质量和文件大小。
</Callout>

### 导入模型
如果你不清楚如何导入模型，请参考：[导入模型](/docs/animation/examples/playAnimation/#导入模型)

## 1. 查看模型
点击模型资产上的展开按钮



可以看到模型中包含了三个 [动画片段](/docs/animation/clip) 资产, 以及一个 [动画控制器](/docs/animation/animatorController/) 资产




## 2. 创建动画控制资产
想要控制模型的动画播放，需要编辑 `动画控制器` 的内容，而模型中的 `动画控制器` 是只读的，所以需要我们自己创建一个 `动画控制器` 。
如果你不知道如何创建 `动画控制器` ，请参考：[创建动画控制器](/docs/animation/examples/playAnimation/#2-创建动画控制资产)

## 3. 编辑动画控制器

1. 打开动画控制器编辑器，添加三个 `动画状态` ，分别绑定模型中的三个 `动画片段`



如果你不知道如何创建 `动画状态` 并绑定 `动画片段` ，请参考：[编辑动画控制器](/docs/animation/examples/playAnimation/#3-编辑动画控制器)

2. 创建 `动画过渡`（关于动画过渡的详细介绍请参考 [动画状态机](/docs/animation/state-machine/#动画过渡) 文档的动画过渡部分）：将 `idle` 动画连接到 `entry`，并点击连线将 `Duration` 改为 0，这样在这个动画控制器被应用时，角色就会播放 `idle` 动画。



3. 以同样的方式将 `idle` 连接到 `walk`，将 `walk` 连接到 `run`， 将 `run` 连接到 `exit`, 这样角色就会在播完 `idle` 动画后，自动过渡到 `walk` 动画，再过渡到 `run` 动画，最后到 `exit` 退出，重新进入 `entry`，来循环播放这段动画。



## 4. 使用动画控制器并预览
绑定 `动画控制器`（如果你不知道如何绑定 `动画控制器` ，请参考：[使用动画控制器](/docs/animation/examples/playAnimation/#4-使用动画控制器)文档）之后点击播放按钮，可以看到模型从 `idle` 过渡到 `walk`，再过渡到 `run`，最后退出，重新回到 `idle` 状态。



<Callout type="positive">
  实际项目中，将 `run` 连接到 `idle` 可能是更好的选择，这样角色在播完 `run` 动画后，会过渡到 `idle` 动画，而不是直接重新播放，本示例仅是为了演示 `exit` 的效果。
</Callout>

## 用户输入控制动画
上述方式主要是为了调试动画的过渡效果，你可以修改过渡的 `Duration` , `ExitTime` , `Offset` 等参数（详细介绍请参考 [动画状态机](/docs/animation/state-machine/) 文档），来调整到合适的过渡效果。

实际项目中，动画一般不会像本示例一样自动切换，大多数情况需要结合用户输入来控制动画的切换，具体可以参考：[用户输入控制动画](/docs/animation/examples/controlAnimationByInput) 文档

---

## File: docs/zh/animation/examples/material-animation.mdx

---
order: 7
title: 材质动画
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中实现材质的动画。通过简单的步骤，你将学习如何制作一个材质动画的[动画片段](/docs/animation/clip)。
如果你是第一次使用动画编辑器，建议先阅读前面几篇文档：

[1.播放模型中的动画](/docs/animation/examples/playAnimation/)

[2.动画复用](/docs/animation/examples/reuseAnimation/)

## 0. 准备工作
在开始之前，我们需要在 `实体` 上添加一个 `网格渲染器` 组件，刚创建的项目中默认会有一个立方体，它已经包含了 `网格渲染器` 组件。



除此以外，因为默认的 `材质` 是只读的，所以需要我们还需要创建一个 `材质`。

创建 `材质` 有两种方式：
1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Material`



2. 点击添加资产按钮 `+`，选择 `Material`



将 `网格渲染器` 的 `材质` 属性替换为刚刚创建的 `材质`。



## 1. 创建动画片段
添加好组件后， 我们需要创建一个 `动画片段`（关于动画片段的介绍，请参考 [动画片段](/docs/animation/clip) 文档）创建 `动画片段` 有两种方式：
1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Animation Clip`



2. 点击添加资产按钮 `+`，选择 `Animation Clip`



## 2. 打开动画片段编辑器

1. 双击刚刚创建的 `动画片段` 资产



2. 选择 `实体`，根据提示，我们需要在 **[层级面板](/docs/interface/hierarchy)** 选择一个 `实体` 作为编辑动画的目标。

选择编辑器默认创建的 `实体`（你也可以点击层级面板的 `+` 按钮创建一个新的）



3. 点击动画片段编辑器的 `Create` 按钮，编辑器会为你做好一些准备工作，本示例你可以先忽略对这些概念的理解。

<Callout type="info">
上述的准备工作包含：
1. 为你选择的 `实体` 创建一个 `动画控制组件` （[详细介绍](/docs/animation/animator/)），以及创建一个 `动画控制器` （[详细介绍]((/docs/animation/animatorController/))） 资产。
2. `动画控制器` 内会包含一个 `动画状态`，并且 `动画状态` 会自动绑定你选择的 `动画片段`，并连接到 `entry` 节点上（关于 `entry` 节点的详细介绍请参考 [动画状态机](/docs/animation/state-machine/) 文档）。
3. `动画控制组件` 会自动绑定 `动画控制器`。
</Callout>

创建完成之后，你会打开动画片段编辑器，如图所示：




## 3. 编辑动画片段

1. 开启 `动画片段` 编辑器的录制模式



2. 将时间线调到 `0:00`， 然后将 `网格渲染器` 组件的 `材质` 的颜色属性设置为红色，在录制模式下，编辑器会自动添加对应的属性及关键帧。



3. 分别在 `0:20`，`0:40` 处，将 `网格渲染器` 组件的 `材质` 的颜色属性设置为绿色， 蓝色。在 `1:00` 处，再设置回红色。
首尾的关键帧相同，动画就可以自然的循环播放了，点击播放按钮，可以预览这个 `动画片段`。



这样我们的材质颜色变化 `动画片段` 就制作完成了。关于动画编辑器的更多操作，请参考 [动画片段](/docs/animation/clip) 文档。

关于如何使用这个 `动画片段` 到你的项目中，可以参考文章顶部列出的两篇文档。

---

## File: docs/zh/animation/examples/playAnimation.mdx

---
order: 0
title: 播放模型中的动画
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中播放模型自带的动画。通过简单的步骤，你将学习如何加载一个带有动画的 3D 模型，并控制其动画的播放。这个指南适合初学者了解 Galacean 编辑器的动画系统，也为更复杂的动画应用奠定基础。

## 0. 准备工作

在开始之前，我们需要有一个带动画的模型。如果你没有模型，可以在 [mixamo](https://www.mixamo.com/#/?page=1&query=&type=Character) 上下载一个。

### 下载模型

1. 在 [mixamo](https://www.mixamo.com/#/?page=1&query=&type=Character) 中选择一个模型

 

2. 在 [动画Tab](https://www.mixamo.com/#/?page=1&query=&type=Motion%2CMotionPack) 中选择一个动画

 

3. 下载模型

 

<Callout type="positive">
  在实际项目中强烈建议将FBX模型通过Blender等第三方工具转换为GLB/GLTF格式。Galacean目前仅支持解析GLB/GLTF模型。虽然可以直接将FBX拖入编辑器进行自动转换，但这种方式可能会导致转换后的模型动画数据过大，并可能出现还原问题。使用专业工具进行转换可以更好地控制转换质量和文件大小。
</Callout>

### 导入模型
有三种方式导入模型：
1. 将模型文件拖入编辑器的 **[资产面板](/docs/assets/interface)** 中

 

2. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Upload` -> `Model`

 

3. 点击上传按钮，选择 `Model`

 


## 1. 查看模型

点击模型资产上的展开按钮

 

可以看到模型中包含了一个 `动画片段`（[详细介绍](/docs/animation/clip)）资产(如果模型中包含多个动画，这里会有多个 `动画片段` 资产)

 

以及一个 `动画控制器`（[详细介绍](/docs/animation/animatorController/)）资产

 

## 2. 创建动画控制资产

想要控制动画的播放，需要编辑 `动画控制器` 的内容，而模型中的 `动画控制器` 是只读的，所以需要我们自己创建一个 `动画控制器` 。

创建 `动画控制器` 有两种方式：
1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Animation Controller`

 

2. 点击添加资产按钮 `+`，选择 `Animation Controller`

 

## 3. 编辑动画控制器

1. 双击 [动画控制器](/docs/animation/animatorController/) 资产，进入动画控制器编辑器（编辑器的各个功能介绍请参考 [动画控制器编辑器](/docs/animation/animatorController/) 文档）

 

2. 点击添加动画状态按钮，添加一个动画状态

 

3. 点击动画状态（动画状态的属性介绍请参考 [动画状态机](/docs/animation/state-machine/#动画状态) 文档的动画状态部分），绑定动画片段。 

 

4. 从 `entry` 连接到此 `动画状态` 并点击连线，并将 `Duration` 改为0（动画过渡的详细介绍请参考 [动画状态机](/docs/animation/state-machine/) 文档），这样在这个动画控制器被应用时，就会立即自动播放此动画状态。




## 4. 使用动画控制器并预览

1. 将模型拖入到场景中



2. 找到`动画控制组件` （[详细介绍](/docs/animation/animator/) , 组件一般在模型实例的根 `实体` 上，也就是编辑器的模型 `实体` 下的第一个子 `实体` 上。
 


3. 在动画控制器属性上替换为刚才创建的`动画控制器`

 


4. 点击播放按钮，就可以看到模型开始播放动画了

---

## File: docs/zh/animation/examples/reuseAnimation.mdx

---
order: 1
title: 动画复用
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中播放其他模型的动画。通过简单的步骤，你将学习如何让一个模型复用另一个模型的动画。这个指南适合初学者了解 Galacean 编辑器的动画系统，也为更复杂的动画应用奠定基础。
如果你是第一次使用动画编辑器，建议先阅读这篇文档：

[播放模型中的动画](/docs/animation/examples/playAnimation/)

## 0. 准备工作

在开始之前，我们需要有一个没有动画的模型，和一个只有动画的模型（角色模型必须包含与动画模型同名的实体）。如果你没有模型，可以在 [mixamo](https://www.mixamo.com/#/?page=1&query=&type=Character) 上下载一个。

### 下载模型

1. 在 [mixamo](https://www.mixamo.com/#/?page=1&query=&type=Character) 中选择一个模型

 

直接点击下载


2. 在 [动画Tab](https://www.mixamo.com/#/?page=1&query=&type=Motion%2CMotionPack) 中选择一个动画

 

选择 `Without Skin` 选项，然后点击下载


<Callout type="positive">
  在实际项目中强烈建议将FBX模型通过Blender等第三方工具转换为GLB/GLTF格式。Galacean目前仅支持解析GLB/GLTF模型。虽然可以直接将FBX拖入编辑器进行自动转换，但这种方式可能会导致转换后的模型动画数据过大，并可能出现还原问题。使用专业工具进行转换可以更好地控制转换质量和文件大小。
</Callout>

### 导入模型
如果你不知道如何导入模型，请参考：[导入模型](/docs/animation/examples/playAnimation/#导入模型)

将角色模型和动画模型导入到编辑器中。  



## 1. 创建动画控制资产
想要播放其他模型的动画，需要编辑 `动画控制器` 的内容，而模型中的 `动画控制器` 是只读的，所以需要我们自己创建一个 `动画控制器` 。
如果你不知道如何创建` 动画控制器` ，请参考：[创建动画控制器](/docs/animation/examples/playAnimation/#2-创建动画控制资产)

## 2. 编辑动画控制器

1. 双击 [动画控制器](/docs/animation/animatorController/) 资产，进入动画控制器编辑器（编辑器的各个功能介绍请参考 [动画控制器编辑器](/docs/animation/animatorController/) 文档）

 

2. 点击 `添加动画状态` 按钮，添加一个 `动画状态`

 

3. 点击 `动画状态`（动画状态的属性介绍请参考 [动画状态](/docs/animation/animatorState/) 文档），绑定动画模型中的 `动画片段` 。 



4. 从 `entry` 连接到此 `动画状态` 并点击连线将 `Duration` 改为 0（动画状态及动画过渡的详细介绍请参考 [动画状态机](/docs/animation/state-machine/) 文档），这样在这个动画控制器被应用时，就会立即自动播放此 `动画状态` 。




## 3. 使用动画控制器并预览

1. 将模型拖入到场景中



2. 与直接播放模型中的动画不同，我们需要找到动画模型的 `动画控制组件`（[详细介绍](/docs/animation/animator/)）位置, 组件一般在模型实例的根实体上，也就是编辑器的模型实体下的第一个子实体上，因此我们需要将包含动画的模型也拖到节点树中



我们发现 `动画控制组件` 在动画模型的 `mixamorig:Hips` 实体上，因此我们需要在角色模型的`mixamorig:Hips`实体上添加 `动画控制组件`




3. 在动画控制器属性上替换为刚才创建的`动画控制器`



4. 点击播放按钮，就可以看到模型开始播放动画了

---

## File: docs/zh/animation/examples/sprite-sheet.mdx

---
order: 6
title: 2D 帧动画
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中实现 2D 帧动画。通过简单的步骤，你将学习如何制作一个帧动画[动画片段](/docs/animation/clip)。
如果你是第一次使用动画编辑器，建议先阅读前面几篇文档：

[1.播放模型中的动画](/docs/animation/examples/playAnimation/)

[2.动画复用](/docs/animation/examples/reuseAnimation/)

## 0. 准备工作
在开始之前，我们需要有制作帧动画所需的序列帧图片，雪碧图和单帧图片都可以。本示例使用雪碧图，如果你没有，可以在这里下载 [雪碧图](https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*DmSyQZQCnrEAAAAAAAAAAAAADsJ_AQ/original)。

## 1. 导入纹理
有三种方式导入 `纹理` ：
1. 将图片拖入编辑器的 **[资产面板](/docs/assets/interface)** 中



2. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Upload` -> `Texture2D` 



3. 点击上传按钮，选择 `Texture2D`



## 2. 创建精灵
关于 `精灵` 的详细介绍请参考 [精灵](/docs/graphics/2D/sprite) 文档

创建 `精灵` 有三种方式：
1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Sprite`



2. 点击添加资产按钮 `+`，选择 `Sprite`



3. 如果你是单帧图片，可以右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Upload` -> `Sprite`，然后选择你的单帧图片。
这种方式，在导入 `纹理` 的同时会自动创建一个 `精灵` 资产并绑定此图片作为其 `纹理` 。




创建三个 `精灵` 资产，分别对应雪碧图中的三帧。




## 3. 为精灵绑定纹理
如果你使用雪碧图的话，需要根据帧数，配置合适的 `Region`。如果你使用单帧图片的话，直接按第一步绑定即可。

1. 点击 `精灵` 资产，点击 `纹理` 属性，选择对应的纹理。



2. 根据帧数，配置合适的 `Region`，本示例共有三帧，并且只有一排， 所以每帧的 `Region` 的 `W` 为 1/3 (对应的纹理 `width` 也改为原来的1/3) `H` 为 1。



3. 修改另外两个 `精灵` 的 `Region`，与第一个 `精灵` 的 `Region` 不同的是，它们的 `Region` 的 `X` 值不同，分别为 0, 1/3, 2/3。

`Sprite1` :



`Sprite2` :



全部绑定好纹理后，`精灵` 资产会展示对应的图片，如图所示：




## 4. 创建动画片段
准备好 `精灵` 后， 我们需要创建一个 `动画片段`（关于动画片段的介绍，请参考 [动画片段](/docs/animation/clip) 文档）创建 `动画片段` 有两种方式：
1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Animation Clip`



2. 点击添加资产按钮 `+`，选择 `Animation Clip`



## 5. 打开动画片段编辑器

1. 双击刚刚创建的 `动画片段` 资产



2. 选择 `实体`，根据提示，我们需要在 **[层级面板](/docs/interface/hierarchy)** 选择一个 `实体` 作为编辑动画的目标。

选择编辑器默认创建的 `实体`（你也可以点击层级面板的 `+` 按钮创建一个新的）



3. 点击动画片段编辑器的 `Create` 按钮，编辑器会为你做好一些准备工作，本示例你可以先忽略对这些概念的理解。

<Callout type="info">
上述的准备工作包含：
1. 为你选择的 `实体` 创建一个 `动画控制组件`（[详细介绍](/docs/animation/animator/)），以及创建一个 `动画控制器` （[详细介绍](/docs/animation/animatorController/)） 资产。
2. `动画控制器` 内会包含一个 `动画状态`，并且 `动画状态` 会自动绑定你选择的 `动画片段`，并连接到 `entry` 节点上（关于 `entry` 节点的详细介绍请参考 [动画状态机](/docs/animation/state-machine/) 文档）。
3. `动画控制组件` 会自动绑定 `动画控制器`。
</Callout>

创建完成之后，你会打开动画片段编辑器，如图所示：



## 6. 设置实体并切换到2D视图

1. 删掉默认的 `网格渲染器` 组件（如果你是自己创建的, 忽略）。为选择的 `实体` 添加一个 `精灵渲染器` 组件，






2. 为 `精灵渲染器` 组件的 `精灵` 属性绑定一个我们刚刚创建的 `精灵` 资产， 作为默认的 `精灵`。



3. 将 `实体` 默认的旋转重制，并切换到 `2D` 视图



## 7. 编辑动画片段

1. 开启 `动画片段` 编辑器的录制模式



2. 将时间线调到 `0:00`， 然后将 `精灵渲染器` 组件的 `精灵` 属性切换到第一帧的 `精灵` 资产。因为我们的第一帧与 `精灵渲染器` 组件的默认 `精灵` 相同，所以仍然选择此 `精灵`。
在录制模式下，编辑器会自动添加对应的属性及关键帧。



3. 分别在 `0:20`, `0:40` 处，将 `精灵渲染器` 组件的 `精灵` 属性切换到第二帧，第三帧的 `精灵` 资产。在 `1:00` 处，再切换回第一帧的 `精灵` 资产。
首尾的关键帧相同，在循环播放时动画就不会出现抖动了，点击播放按钮，可以预览这个 `动画片段`。



这样我们的帧动画 `动画片段` 就制作完成了。关于动画编辑器的更多操作，请参考 [动画片段](/docs/animation/clip) 文档。

关于如何应用这个 `动画片段` 到你的项目中，可以参考文章顶部列出的两篇文档。

---

## File: docs/zh/animation/examples/text-animation.mdx

---
order: 5
title: 文字动画
type: 动画
group: 指南和示例
label: Animation/Examples
---

本示例展示了如何在 Galacean 编辑器中实现文字的动画。通过简单的步骤，你将学习如何制作一个文字动画的[动画片段](/docs/animation/clip)。
如果你是第一次使用动画编辑器，建议先阅读前面几篇文档：

[1.播放模型中的动画](/docs/animation/examples/playAnimation/)

[2.动画复用](/docs/animation/examples/reuseAnimation/)

## 0. 准备工作
在开始之前，我们需要在 `实体` 上添加一个 `文本渲染器` 组件。



## 1. 创建动画片段
添加好组件后， 我们需要创建一个 `动画片段`（关于动画片段的介绍，请参考 [动画片段](/docs/animation/clip) 文档）创建 `动画片段` 有两种方式：
1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Animation Clip`



2. 点击添加资产按钮 `+`，选择 `Animation Clip`



## 2. 打开动画片段编辑器

1. 双击刚刚创建的 `动画片段` 资产



2. 选择 `实体`，根据提示，我们需要在 **[层级面板](/docs/interface/hierarchy)** 选择一个 `实体` 作为编辑动画的目标。

选择编辑器默认创建的 `实体`（你也可以点击层级面板的 `+` 按钮创建一个新的）



3. 点击动画片段编辑器的 `Create` 按钮，编辑器会为你做好一些准备工作，本示例你可以先忽略对这些概念的理解。

<Callout type="info">
上述的准备工作包含：
1. 为你选择的 `实体` 创建一个 `动画控制组件` （[详细介绍](/docs/animation/animator/)），以及创建一个 `动画控制器` （[详细介绍]((/docs/animation/animatorController/))） 资产。
2. `动画控制器` 内会包含一个 `动画状态`，并且 `动画状态` 会自动绑定你选择的 `动画片段`，并连接到 `entry` 节点上（关于 `entry` 节点的详细介绍请参考 [动画状态机](/docs/animation/state-machine/) 文档）。
3. `动画控制组件` 会自动绑定 `动画控制器`。
</Callout>

创建完成之后，你会打开动画片段编辑器，如图所示：




## 3. 设置测试文字并切换到2D视图

1. 在 `实体` 上的 `文本渲染器` 组件的 `文本` 属性中输入 `Hello World`，并设置 `字体大小` 为 `200`。你也可以按需设置其他属性，关于文字的更多信息，请参考 [文本渲染器](/docs/graphics/2D/text/) 文档。



2. 切换到 `2D` 视图



## 4. 编辑动画片段

1. 开启 `动画片段` 编辑器的录制模式



2. 将时间线调到 `0:00`， 然后将 `文本渲染器` 组件的 `字体大小` 属性设置为0，在录制模式下，编辑器会自动添加对应的属性及关键帧。



3. 分别在 `0:30`，`1:00` 处，将 `文本渲染器` 组件的 `字体大小` 属性设置为100， 200。点击播放按钮，可以预览这个 `动画片段`。



这样我们的文字放大 `动画片段` 就制作完成了，你也可以尝试其他 `文本渲染器` 的属性，比如 `颜色`，`字体` 等。关于动画编辑器的更多操作，请参考 [动画片段](/docs/animation/clip) 文档。

关于如何使用这个 `动画片段` 到你的项目中，可以参考文章顶部列出的两篇文档。

---

## File: docs/zh/animation/animator.mdx

---
order: 3
title: 动画控制组件
type: 动画
label: Animation
---

## 简介
`动画控制组件`（[API](/apis/core/#Animator)）的作用是读取 `动画控制器`（[详细介绍](/docs/animation/animatorController/)）的数据，并播放其内容。

### 参数说明

| 属性               | 功能说明                       |
| :----------------- | :----------------------------- |
| animatorController | 绑定 `AnimatorController` 资产 |

## 编辑器使用

当把模型拖入到场景中，模型以初始姿态展示，并不会播放任何动画，这时需要在模型实体上找到 `动画控制组件` 并为它绑定一个 `动画控制器` 资产。


1. 找到或者创建 `动画控制组件`


<Callout type="info">
模型的 `动画控制组件` 在glTF实例的根实体上，也就是编辑器的模型实体下的第一个子 `实体` 上。

如果模型内有动画，会为你自动绑定一个只读的 `动画控制器`。
</Callout>



如果没有 `动画控制组件` 可以按下图方式创建



2. 创建一个 `动画控制器` 资产并绑定到模型上，创建 `动画控制器` 有两种方式：
    1. 右键点击 **[资产面板](/docs/assets/interface)** 中的空白处，选择 `Create` -> `Animation Controller`

       

    2. 点击添加资产按钮 `+`，选择 `Animation Controller`

       

3. 在`动画控制组件` 的动画控制器属性上替换为刚才创建的 `动画控制器`



4. 编辑 `动画控制器` 后（可以参考[动画控制器](/docs/animation/animatorController)文档）你就可以播放其中的动画了

## 脚本使用

在使用脚本之前，建议先阅读以下文档：

- [脚本](/docs/script/script)
- [动画系统构成](/docs/animation/system)


### 播放指定动画状态

你可以使用 [animator.play](/apis/core/#Animator-play) 方法来播放指定的 `动画状态`，参数说明详见[API 文档](/apis/core/#Animator-play)。

```typescript
import { Script, Animator, Keys } from '@galacean/engine';

export default class extends Script {
  onStart() {
    const animator = this.entity.getComponent(Animator);
    animator.play("State Name");
  }
}
```

如果需要在动画中的某一时刻开始播放可以通过以下方式

```typescript
import { Script, Animator, Keys } from '@galacean/engine';

export default class extends Script {
  onStart() {
    const animator = this.entity.getComponent(Animator);
    const layerIndex = 0;
    const normalizedTimeOffset = 0.5; // 归一化的时间
    animator.play("State Name", layerIndex, normalizedTimeOffset);
  }
}
```

### 控制播放速度

你可以通过 [speed](/apis/core/#Animator-speed)  属性来控制动画的播放速度。 `speed`  默认值为 `1.0` ，值越大播放的越快，越小播放的越慢。当值为负数时，进行倒播。

```typescript
animator.speed = 2.0；
```

### 停止/重新播放

你可以通过设置 [animator.enabled](/apis/core/#Animator-enabled) 来停止和重新播放动画

```typescript
// 停止
animator.enabled = false;
// 重新播放
animator.enabled = true;
```

### 暂停/恢复播放
你可以通过设置 [animator.speed](/apis/core/#Animator-speed) 暂停和恢复播放

```typescript
// 暂停
animator.speed = 0;
// 恢复
animator.speed = 1;
```


如果你只想针对某一个 `动画状态` 进行暂停，可以通过将它的速度设置为 0 来实现。

```typescript
const state = animator.findAnimatorState("xxx");
state.speed = 0;
```

### 过渡指定动画状态

你可以使用 [animator.crossFade](/apis/core/#Animator-crossFade) 方法来将动画过渡到指定的 `动画状态`，参数说明详见[API 文档](/apis/core/#Animator-crossFade)。

```typescript
animator.crossFade("OtherStateName", 0.3);
```


### 获取当前在播放的动画状态

你可以使用 [getCurrentAnimatorState](/apis/core/#Animator-getCurrentAnimatorState)  方法来获取当前正在播放的 `动画状态`。参数为 `动画状态` 所在 `动画层` 的序号`layerIndex`, 详见[API 文档](/apis/core/#Animator-getCurrentAnimatorState)。获取之后可以设置 `动画状态` 的属性，比如将默认的循环播放改为一次。

```typescript
const currentState = animator.getCurrentAnimatorState(0);
// 播放一次
currentState.wrapMode = WrapMode.Once;
// 循环播放
currentState.wrapMode = WrapMode.Loop;
```

### 获取动画状态

你可以使用 [findAnimatorState](/apis/core/#Animator-findAnimatorState)  方法来获取指定名称的 `动画状态` 。获取之后可以设置动画状态的属性，比如将默认的循环播放改为一次。

```typescript
const state = animator.findAnimatorState("xxx");
// 播放一次
state.wrapMode = WrapMode.Once;
// 循环播放
state.wrapMode = WrapMode.Loop;
```

### 动画裁剪
你可以通过设置 `动画控制组件` 的 [cullingMode](/apis/core/#Animator-cullingMode) 来设置当 `实体` 不可见时动画是否进行计算。在动画被裁剪时，动画不会进行计算并应用到 `实体` 上，但动画的状态仍然会随着时间更新，使其再可见时是正确地表现。

## 2. Official Technical Reference & Guides (galacean/galacean.github.io)

## File: README.md

This repository is archived. The documentation source of Galacean Engine has been moved to [galacean/engine](https://github.com/galacean/engine/docs).

# Galacean Site

The documentation source of [Galacean Engine](https://github.com/galacean/engine).

## Development
#### Develop site

```bash
$ yarn
$ yarn dev

open http://local.alipay.net
```
#### Only develop playground
```bash
$ yarn playground
```
## Deploy
```bash
$ yarn build
```

## Q&A
#### How to insert a playground in markdown
```
// leave a blank line here

```
#### How to link engine
* Mac
```bash
$ yarn link ../engine/packages/*
```
* windows
```bash
$ yarn link ..\engine\packages\galacean
```

---

## File: docs/animation-animator.zh-CN.md

---
order: 3
title: 动画控制组件
type: 动画
label: Animation
---

动画控制组件（[Animator](${api}core/Animator)）的作用是读取[动画控制器](${docs}animation-animatorController)（[AnimatorController](${api}core/AnimatorController)）的数据，并播放其内容。

### 参数说明

| 属性               | 功能说明                       |
| :----------------- | :----------------------------- |
| animatorController | 绑定 `AnimatorController` 资产 |

## 编辑器使用

1. 当我们把模型拖入到场景中，模型以初始姿态展示出来，但是并不会播放任何动画，我们需要在模型实体上添加动画控制组件（[Animator](${api}core/Animator)）



2. 动画控制组件（[Animator](${api}core/Animator)）需要绑定一个 [动画控制器](${docs}animation-animatorController) 资产，我们创建并绑定





3. 至此你在导出的项目中就可以通过 `animator.play` 播放[动画控制器](${docs}animation-animatorController)中的动画了。

如果你没有为实体添加 动画控制组件（[Animator](${api}core/Animator)）的话 Galacean Engine 会为你默认创建一个并且 [动画控制器](${docs}animation-animatorController) 中默认添加了模型的所有动画片段，当然除此以外你可以通过 [动画控制器](${docs}animation-animatorController) 实现更多的功能。

## 脚本使用

> 在使用脚本之前，最好阅读[动画系统构成](${docs}animation-system)文档，以帮助你更好的了解动画系统的运行逻辑

### 播放动画

在加载 GLTF 模型后引擎会自动为模型添加一个 Animator 组件，并将模型中的动画片段加入其中。可以直接在模型的根实体上获取 Animator 组件，并播放指定动画。

```typescript
engine.resourceManager
  .load<GLTFResource>(
    "https://gw.alipayobjects.com/os/bmw-prod/5e3c1e4e-496e-45f8-8e05-f89f2bd5e4a4.glb"
  )
  .then((asset) => {
    const { defaultSceneRoot } = asset;
    rootEntity.addChild(defaultSceneRoot);
    const animator = defaultSceneRoot.getComponent(Animator);
    animator.play("run");
  });
```

#### 控制播放速度

你可以通过 [speed](${api}core/Animator#speed)  属性来控制动画的播放速度。 `speed`  默认值为 `1.0` ，值越大播放的越快，越小播放的越慢。当值为负数时，进行倒播。

```typescript
animator.speed = 2.0；
```

#### 暂停/恢复播放

你可以通过设置 Animator 的 [enabled](${api}core/Animator#enabled) 来控制动画的暂停和播放.

```typescript
// 暂停
animator.enabled = false;
// 恢复
animator.enabled = true;
```

如果你只想针对某一个动画状态进行暂停，可以通过将它的速度设置为 0 来实现。

```typescript
const state = animator.findAnimatorState("xxx");
state.speed = 0;
```

#### 播放指定动画状态



你可以使用 [play](${api}core/Animator#play)  方法来播放指定的 AnimatorState。参数为 AnimatorState 的`name`，其他参数说明详见[API 文档](${api}core/Animator#play)。

```typescript
animator.play("run");
```

如果需要在动画中的某一时刻开始播放可以通过以下方式

```typescript
const layerIndex = 0;
const normalizedTimeOffset = 0.5; // 归一化的时间
animator.play("run", layerIndex, normalizedTimeOffset);
```

### 获取当前在播放的动画状态

你可以使用 [getCurrentAnimatorState](${api}core/Animator#getCurrentAnimatorState)  方法来获取当前正在播放的 AnimatorState。参数为动画状态所在层的序号`layerIndex`, 详见[API 文档](${api}core/Animator#getCurrentAnimatorState)。获取之后可以设置动画状态的属性，比如将默认的循环播放改为一次。

```typescript
const currentState = animator.getCurrentAnimatorState(0);
// 播放一次
currentState.wrapMode = WrapMode.Once;
// 循环播放
currentState.wrapMode = WrapMode.Loop;
```

### 获取动画状态

你可以使用 [findAnimatorState](${api}core/Animator#findAnimatorState)  方法来获取指定名称的 AnimatorState。详见[API 文档](${api}core/Animator#getCurrentAnimatorState)。获取之后可以设置动画状态的属性，比如将默认的循环播放改为一次。

```typescript
const state = animator.findAnimatorState("xxx");
// 播放一次
state.wrapMode = WrapMode.Once;
// 循环播放
state.wrapMode = WrapMode.Loop;
```

---

## File: docs/animation-animatorController.zh-CN.md

---
order: 2
title: 动画控制器
type: 动画
label: Animation
---

动画控制器（[AnimatorController](${api}core/AnimatorController)）用于组织[动画片段](${docs}animation-clip)（[AnimationClip](${api}core/AnimationClip)）实现更加灵活丰富的动画效果。

## 编辑器使用

### 基础使用

通过动画控制器的编辑器，用户可以在其中组织[动画片段](${docs}animation-clip)的播放逻辑

1. 准备好动画片段（[制作动画片段](${docs}animation-clip)）



2. 要组织播放这些动画片段我们需要创建一个动画控制器（[AnimatorController](${api}core/AnimatorController)）资产



3. 刚创建的动画控制器中没有任何数据，我们需要对他进行编辑，双击资产, 并为它添加一个 AnimatorState



4. 点击 AnimatorState 为它绑定一个动画片段



5. 在[动画控制组件](${docs}animation-animator)上绑定该动画控制器（[AnimatorController](${api}core/AnimatorController)）资产



6. 至此你在导出的项目中就可以通过 `animator.play("New State")` 播放 `run` 动画了

你还可以通过动画控制器的编辑器实现更多的功能：

### 默认播放

将 AnimatorState 连接到`entry`上你导出的项目运行时就会自动播放其上的动画，而不需再调用 `animator.play`。同时你也会看到编辑器的模型也开始播放动画了。


### 动画过渡

将两个想要过渡的 `AnimatorState` 连接即可实现动画过渡的效果, 点击两个动画间的连线，可以修改动画过渡的参数调整效果



#### 参数说明

| 属性     | 功能说明                                                           |
| :------- | :----------------------------------------------------------------- |
| duration | 过渡时长，时间为相对目标状态的归一化时间, 默认值为 1.0             |
| offset   | 目标状态向前的偏移时间，时间为相对目标状态的归一化时间, 默认值为 0 |
| exitTime | 起始状态过渡开始时间，时间为相对起始状态的归一化时间, 默认值为 0.3 |

### 动画叠加

Galacean 引擎支持多层的动画叠加。动画叠加是通过 `AnimatorControllerLayer` 间的混合达到的效果。第一层是基础动画层，修改它的权重及混合模式将不会生效。

双击 `AnimatorController` 资源文件编辑动画，添加 Layer，将混合的动作也连接`entry`



有的时候你想要得到一个固定的姿势，需要裁减设计师给到的动画切片，可以修改 `AnimatorState` 的`StartTime` 及 `EndTime`，点击 `AnimatorState` 即可对其进行编辑:



| 属性          | 功能说明                                                                                                                                                                                 |
| :------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Name          | 修改 `AnimatorState` 的名字，名字在所在的层要是**唯一**的。                                                                                                                              |
| AnimationClip | 用于绑定 `AnimationClip` 资产，`AnimationClip` 存储了模型的动画数据。                                                                                                                    |
| WrapMode      | `AnimatorState` 是循环播放还是播放一次，默认为 `Once` 即播放一次。                                                                                                                       |
| Speed         | `AnimatorState` 的播放速度，默认值为 1.0 ，值越大动画速度越快                                                                                                                            |
| StartTime     | `AnimatorState` 从 `AnimationClip` 的哪个时间开始播放，时间为相对 `AnimationClip` 时长的归一化时间。默认值为 0 ，即从头开始播放。 例如：值为 1.0 ，则是 `AnimationClip` 的最后一帧状态。 |
| EndTime       | `AnimatorState` 播放到 `AnimationClip` 的哪个时间结束播放，时间为相对 `AnimationClip` 时长的归一化时间。默认值为 1.0 ，即播放到最后。                                                    |

你也可以通过修改 `Layer` 的 `Weight` 参数来调整 `Layer` 在混合中的权重，通过修改 `Blending` 来修改混合模式。



| 属性     | 功能说明                                                                           |
| :------- | :--------------------------------------------------------------------------------- |
| Name     | 该层的名字。                                                                       |
| Weight   | 该层的混合权重，默认值为 1.0 。                                                    |
| Blending | 该层的混合模式，`Additive` 为叠加模式， `Override` 为覆盖模式，默认值为 `Override` |

## 脚本使用

> 在使用脚本之前，最好阅读[动画系统构成](${docs}animation-system)文档，以帮助你更好的了解动画系统的运行逻辑

### 默认播放

你可以通过设置 AnimatorStateMachine 的[defaultState](${api}core/AnimatorStateMachine#defaultState) 来设置所在层的默认播放动画，这样当 Animator `enabled=true` 时你不需要调用 `play` 方法即可默认播放。

```typescript
const layers = animator.animatorController.layers;
layers[0].stateMachine.defaultState = animator.findAnimatorState("walk");
layers[1].stateMachine.defaultState = animator.findAnimatorState("sad_pose");
layers[1].blendingMode = AnimatorLayerBlendingMode.Additive;
```

### 动画过渡

你可以通过为 `AnimatorState` 添加 `AnimatorTransition` 实现动画状态间的过渡。

```typescript
const walkThenRunState = animatorStateMachine.addState("walkThenRun");
walkThenRunState.clip = walkClip;
const runState = animatorStateMachine.addState("run");
runState.clip = runClip;
const transition = new AnimatorStateTransition();
transition.duration = 1;
transition.offset = 0;
transition.exitTime = 0.5;
transition.destinationState = runState;
walkThenRunState.addTransition(transition);
animator.play("walkThenRun");
```

通过这样的方式你之后每次在该动画状态机所在的层播放 `walkThenRun` 动画时都会在 `walk` 动画播放一半时开始过渡到 `run` 动画。

### 动画叠加

将想要叠加的动画状态添加到其他层并将它的混合模式设置为 `AnimatorLayerBlendingMode.Additive` 即可实现动画叠加效果，



### 动画数据

#### 设置动画数据

你可以通过 [animatorController](${api}core/Animator#animatorController)  属性来设置动画控制器的动画数据，加载完成的 GLTF 模型会自动添加一个默认的 AnimatorController。

```typescript
animator.animatorController = new AnimatorController()；
```

#### 复用动画数据

有的时候模型的动画数据存储在其他模型中，可以用如下的方式引入使用：



除此以外还有一种方式，Animator 的 [AnimatorController](${api}core/AnimatorController) 就是一个数据存储的类，它不会包含运行时的数据，基于这种设计只要绑定 Animator 组件的模型的**骨骼节点的层级结构和命名相同**，我们就可以对动画数据进行复用。

```typescript
const animator = model1.getComponent(Animator);
animator.animatorController = model2.getComponent(Animator).animatorController;
```

### 状态机脚本



状态机脚本为用户提供了动画状态的生命周期钩子函数来编写自己的游戏逻辑代码。用户可以通过继承 [StateMachineScript](${api}core/StateMachineScript) 类来使用状态机脚本。

状态机脚本提供了三个动画状态周期：

- `onStateEnter`：动画状态开始播放时回调。
- `onStateUpdate`：动画状态更新时回调。
- `onStateExit`：动画状态结束时回调。

```typescript
class theScript extends StateMachineScript {
  // onStateEnter is called when a transition starts and the state machine starts to evaluate this state
  onStateEnter(animator: Animator, stateInfo: any, layerIndex: number) {
    console.log("onStateEnter", animator, stateInfo, layerIndex);
  }

  // onStateUpdate is called on each Update frame between onStateEnter and onStateExit callbacks
  onStateUpdate(animator: Animator, stateInfo: any, layerIndex: number) {
    console.log("onStateUpdate", animator, stateInfo, layerIndex);
  }

  // onStateExit is called when a transition ends and the state machine finishes evaluating this state
  onStateExit(animator: Animator, stateInfo: any, layerIndex: number) {
    console.log("onStateExit", animator, stateInfo, layerIndex);
  }
}

animatorState.addStateMachineScript(theScript);
```

如果你的脚本不用复用的话你也可以这么写:

```typescript
state.addStateMachineScript(
  class extends StateMachineScript {
    onStateEnter(
      animator: Animator,
      animatorState: AnimatorState,
      layerIndex: number
    ): void {
      console.log("onStateEnter: ", animatorState);
    }
  }
);
```

---

## File: docs/animation-clip.zh-CN.md

---
order: 1
title: 动画片段
type: 动画
label: Animation
---

**动画片段**是 Galacean 动画系统的核心元素之一，Galacean 支持导入外部设计软件设计的模型动画，设计师输出的带动画的模型中的每个动画在 Galacean 中会被解析成一个个的**动画片段**资产，我们也可以通过动画片段编辑器创作额外的动画。

有两种常用的方式得到动画片段

1. 导入用第三方工具（例如 Autodesk® 3ds Max®， Autodesk® Maya®， Blender）创建的带动画的模型，详见[美术制作动画片段](${docs}animation-clip-for-artist)



2. 在 Galacean 中创建动画片段（下文会介绍编辑器和脚本这两种创建方式）

## 动画面板介绍

动画片段编辑器目前支持除物理相关组件的所有可插值属性的编辑，如果你需要编辑其他属性，要在实体上添加对应的组件。



> 注：Galacean 动画编辑器默认以 60FPS 为基准，上图所示时间`0:30`为 30 帧处， 若时间轴刻度为`1:30`则为 90 帧处

## 基础操作

### Transform 组件示例

1. 在 **[资产面板](${docs}assets-interface)** 中 右键/点击+ 创建 `动画片段` 资产



2. 双击 `动画片段` 资产,并选择一个实体作为 `动画片段` 的编辑对象



点击 `创建` 按钮编辑器会自动为你的Entity添加 [动画控制组件](${docs}animation-animator) 并将该动画片段添加到 [动画控制器](${docs}animation-animatorController) 中




3. 添加要做动画的属性(这里我添加了两个)




4. 给属性添加关键帧


当我们点击添加关键帧按钮时，关键帧会存入当前指定属性的值，所以当我们什么都未改变时，关键帧存入的是此刻这个实体的 `position` 值。我们希望他 60 帧后移动到 (3, 0, 0)的位置，所以先将这个正方体通过属性面板修改到(3, 0, 0) 再添加关键帧

同理我们也为 `rotation` 属性添加关键帧


##### 录制模式

我们提供了录制模式以方便开发者快速的添加关键帧。开启录制模式，当对应的属性修改时就会自动添加关键帧



### 文字动画示例

首先要有一个具有 TextRender 组件的实体



此时我们再添加属性时可以看到，可添加关键帧的属性增加了 `TextRenderer` 组件相关的可插值的属性



我们按照上文的方式用录制模式添加关键帧




### 帧动画示例

除了数值类型，Galacean 还支持引用类型的动画曲线，可以阅读[帧动画](${docs}animation-sprite-sheet)来了解，如何制作帧动画。

### 材质动画示例
Galacean 还支持组件中的资产属性的动画编辑。如果组件中有材质资产，在 `Inspector` 中会有额外的资产属性的编辑



需要注意的是，编辑器默认的[材质](${docs}graphics-material)是不可编辑的



所以我们想给这个立方体做材质的动画需要新建一个材质并替换它之后就同上文一样开启录制模式直接修改属性即可





## 更多操作

### 操作关键帧

#### 修改关键帧时间

选中关键帧并拖动即可



可以滑动 `鼠标滚轮` 缩放时间轴



#### 修改关键帧的值

开启录制模式，把时间线移动到指定关键帧上，然后重新输入值即可, 在不开启录制模式的情况，需要重新点击添加关键帧按钮。



#### 删除关键帧

选中关键帧右键选择删除，也可以按键盘上的删除键/退格键删除



### 编辑子实体

`动画片段` 不仅可以作用于添加`Animator`组件的实体上，还可以作用于它的子实体上。

1. 我们为刚才的立方体添加一个子实体



2. 我们再点击添加属性就可以看到子实体的属性可以添加了



3. 开启录制模式，编辑子实体添加关键帧即可




### 编辑动画曲线

`动画片段编辑器` 支持动画曲线编辑，点击右上角的曲线Icon即可切换



曲线模式的纵轴为属性的数值




你可以按 `shift+鼠标滚轮` 调整纵轴的比例



属性对应曲线的颜色与按钮的颜色相同



选择关键帧会出现两个控制点，调整控制点即可调整曲线



也可以通过右键关键帧直接设置内置的预设值



选择属性面板的属性可以仅编辑指定属性的曲线



## 脚本使用

> 在使用脚本之前，最好阅读[动画系统构成](${docs}animation-system)文档，以帮助你更好的了解动画系统的运行逻辑

你可以自己创建一个 [AnimationClip](${api}core/AnimationClip)  并通过 [addCurveBinding](${api}core/AnimationClip#addCurveBinding) 为它绑定 [AnimationCurve](${api}core/AnimationCurve)。

```typescript
//custom rotate clip
const rotateClip = new AnimationClip("rotate");
const rotateState =
  animator.animatorController.layers[0].stateMachine.addState("rotate");
rotateState.clip = rotateClip;

const rotateCurve = new AnimationVector3Curve();
const key1 = new Keyframe<Vector3>();
key1.time = 0;
key1.value = new Vector3(0, 0, 0);
const key2 = new Keyframe<Vector3>();
key2.time = 10;
key2.value = new Vector3(0, 360, 0);
rotateCurve.addKey(key1);
rotateCurve.addKey(key2);
rotateClip.addCurveBinding("", Transform, "rotation", rotateCurve);

//custom color clip
const colorClip = new AnimationClip("color");
const colorState =
  animator.animatorController.layers[0].stateMachine.addState("color");
colorState.clip = colorClip;

const colorCurve = new AnimationFloatCurve();
const key1 = new Keyframe<number>();
key1.time = 0;
key1.value = 0;
const key2 = new Keyframe<number>();
key2.time = 10;
key2.value = 1;
colorCurve.addKey(key1);
colorCurve.addKey(key2);
colorClip.addCurveBinding("/light", DirectLight, "color.r", colorCurve);
```

你同样可以为你的子实体 `/light` 绑定 AnimationCurve，就像上面的代码示例，同时 `addCurveBinding` 的第三个参数并不局限于组件的属性，它是一个能索引到值的路径。



### 动画事件

你可以使用 [AnimationEvent](${api}core/AnimationEvent)  来为 AnimationClip 添加事件，动画事件将在指定时间调用你在同一实体上绑定组件的指定回调函数。

```typescript
const event = new AnimationEvent();
event.functionName = "test";
event.time = 0.5;
clip.addEvent(event);
```

---

## File: docs/animation-clip-for-artist.zh-CN.md

---
order: 7
title: 美术动画切片
type: 动画
label: Animation
---

动画切片(**AnimationClip**) 为**一段时间轴上的动画组合**，可以是多个物体的旋转、位移、缩放、权重动画，如**走路、跑步、跳跃**可以分别导出 3 个动画切片；Galacean 引擎可以选择播放哪一个动画切片，前提是建模软件导出的 FBX 或者 glTF 里面包含多个动画切片。

为减少沟通成本，本文列举了几种常见的动画切片方法，导出 glTF 方便 Galacean 引擎直接使用，也可以通过 [glTF 预览](https://galacean.antgroup.com/#/gltf-viewer) 页面进行功能校验。

Blender 的动画编辑页面非常友好，能够清晰地可视化显示受动画影响的节点，并且在时间轴上显示关键帧，因此推荐使用 Blender 进行动画切片。

### Blender

1. 打开 Blender，导入 Blender 支持的模型格式，然后切换到 **动画编辑** 窗口：



2. 通过上图的 “新建动画切片”按钮，可以快速的复制当前动画切片，然后进行独有的操作，如果没有显示该按钮，请确保打开了 “**动作编辑器**”：



举例，新建了一个名为 **animation_copy** 的动画切片，然后只保留最后 5 帧动画：







导出的切片时间轴必须一致，可以通过右下角或者输出属性两个地方进行配置：





3. 因为时间轴必须一致，因此需要将刚才切的动画切片，都移到起始帧，拖拽即可：





4. 至此，动画切片已经准备完成，导出 glTF 或者 FBX ，接入 Galacean 引擎即可：





Unity 也可以导出动画切片，但是效率比较低。

### Unity

插件：[AntG-Unity-Plugin.unitypackage.zip](https://www.yuque.com/attachments/yuque/0/2021/zip/381718/1622541632701-4f33e890-5295-4430-8798-d979b8df504f.zip?_lake_card=%7B%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2021%2Fzip%2F381718%2F1622541632701-4f33e890-5295-4430-8798-d979b8df504f.zip%22%2C%22name%22%3A%22AntG-Unity-Plugin.unitypackage.zip%22%2C%22size%22%3A490677%2C%22type%22%3A%22application%2Fzip%22%2C%22ext%22%3A%22zip%22%2C%22status%22%3A%22done%22%2C%22taskId%22%3A%22u4c98eaae-9ce5-43c7-ae94-c26f4ce0c0f%22%2C%22taskType%22%3A%22upload%22%2C%22id%22%3A%22uef3d6075%22%2C%22card%22%3A%22file%22%7D)

1. 下载插件。

2. 打开 Unity 。

3. 双击插件， **Import** 默认框选选项：



若安装成功，可以看到菜单栏多出 **AntG** 选项：



4. 把需要切片的 FBX 文件拖拽进资源栏：



5. 单击该资源，出现动画调试预览框。增加动画切片，并根据需求调整每个切片的时间轴 **start**、**end**，如果预览动画效果异常，确认没有勾选 **Resample Curves** 这个默认开启选项，切片完成后，记得点击右下角的 **Apply** 确认按钮。





6. 至此，动画切片资源已经制作完毕，接下来介绍如何导出，先将该资源拖拽到节点树中：



7. 然后给该节点增加 **Animator** Component:



8. 可以看到，Animator 组件需要绑定一个 Animator Controller 资源，因此我们需要在资源栏新建一个 Animator Controller 资源：



9. 双击该 controller 资源，将我们之前的动画切片拖拽进去：



10. Animator Controller 资源制作完毕，绑定到刚才的 Component 上：



11. 右键该节点，选择导出 AntG：



12. 至此，制作的动画切片 glTF 文件导出完毕，可以访问 Galacean 的 [glTF 预览](https://galacean.antgroup.com/#/gltf-viewer) 进行功能校验。

---

## File: docs/animation-overview.zh-CN.md

---
order: 0
title: 动画系统概述
type: 动画
label: Animation
---

Galacean 的动画系统具有以下功能：

- 解析 GLTF/FBX 模型中的动画并转为 Galacean 中的 AnimationClip 对象
- 为 Galacean 的所有组件及其属性添加动画
- 设置动画的开始/结束时间以裁剪动画
- 设置动画间的过渡，将多个动画叠加
- 将一个模型的动画应用到另一个模型上
- 添加动画事件及动画生命周期的脚本

## 动画工作流程

使用编辑器创建互动项目的整体流程：

```mermaid
flowchart LR
 添加动画片段 --> 创建动画控制器并导入动画片段 --> 添加动画控制组件播放动画
```

### 1. 添加动画片段

Galacean 的动画系统基于动画片段的概念，动画片段包含某些对象应如何随时间改变其位置、旋转或其他属性的相关信息。每个动画片段可视为单个线性录制。

你可以在编辑器中创建动画片段，详见[制作动画片段](${docs}animation-clip)，也可以导入用第三方工具（例如 Autodesk® 3ds Max®， Autodesk® Maya®， Blender）创建的带动画的模型，详见[美术制作动画片段](${docs}animation-clip-for-artist)，或者来自动作捕捉工作室或其他来源。

### 2. 创建动画控制器并导入动画片段

动画控制器是一个类似于流程图的结构化系统，它在动画系统中充当状态机，负责跟踪当前应该播放哪个片段以及动画应该何时改变或混合在一起。

你可以在这篇[动画控制器](${docs}animation-animatorController)中了解如何使用。

### 3. 添加动画控制组件

编辑好动画控制器后，我们需要在实体上添加[动画控制组件](${docs}animation-animator)并绑定动画控制器资产来播放动画。

---

## File: docs/animation-sprite-sheet.zh-CN.md

---
order: 4
title: 帧动画
type: 动画
label: Animation
---

Galacean 支持引用类型的动画曲线，你可以添加类型为资产的关键帧比如（精灵）下图为制作精灵动画的流程：

1. 给节点添加 `SpriteRenderer` 组件



2. 添加`精灵`，可以参考[精灵](${docs}graphics-2d-sprite)
   

  
3. 在 **[资产面板](${docs}assets-interface)** 中创建 [动画片段](${docs}animation-clip)
   



4. 开启录制模式，编辑器中点到对应的帧数，在 `SpriteRenderer` 中添加 `Sprite` 即可自动添加关键帧




### 脚本实现

引擎在 1.1 版本支持引用类型的动画曲线（[AnimationRefCurve](${api}core/AnimationRefCurve)），关键帧的值可以是资产如（精灵，材质），你可以通过创建引用类型的动画曲线实现比如帧动画的能力：

---

## File: docs/animation-state-machine.zh-CN.md

---
order: 5
title: 动画状态机
type: 动画
label: Animation
---

### 动画过渡

将两个想要过渡的 `AnimatorState` 连接即可实现动画过渡的效果, 点击两个动画间的连线，可以修改动画过渡的参数调整效果



#### 参数说明

| 属性 | 功能说明 |
| :------- | :---------------------------------------------------------|
| duration | 过渡时长，时间为相对目标状态的归一化时间, 默认值为 1.0            |
| offset   | 目标状态向前的偏移时间，时间为相对目标状态的归一化时间, 默认值为 0  |
| exitTime | 起始状态过渡开始时间，时间为相对起始状态的归一化时间, 默认值为 0.3  |

#### 脚本使用

你可以通过为 `AnimatorState` 添加 `AnimatorTransition` 实现动画状态间的过渡。

```typescript
const walkState = animatorStateMachine.addState('walk');
walkState.clip = walkClip;
const runState = animatorStateMachine.addState('run');
runState.clip = runClip;
const transition = new AnimatorStateTransition();
transition.duration = 1;
transition.offset = 0;
transition.exitTime = 0.5;
transition.destinationState = runState;
walkState.addTransition(transition);
animator.play("walk");
```
通过这样的方式你之后每次在该动画状态机所在的层播放 `walk` 动画时都会在播放一半时开始过渡到 `run` 动画。

### 动画叠加

Galacean引擎支持多层的动画叠加。动画叠加是通过 `AnimatorControllerLayer` 间的混合达到的效果。第一层是基础动画层，修改它的权重及混合模式将不会生效。

双击 `AnimatorController` 资源文件编辑动画，添加 Layer，将混合的动作也连接`entry`



有的时候你想要得到一个固定的姿势，需要裁减设计师给到的动画切片，可以向上图一样修改 `AnimatorState` 的`StartTime` 及 `EndTime`，点击 `AnimatorState` 即可对其进行编辑:



| 属性 | 功能说明 |
| :------- | :------------------------------------------------------------------- |
| Name          | 修改 `AnimatorState` 的名字，名字在所在的层要是**唯一**的。           |
| AnimationClip | 用于绑定 `AnimationClip` 资产，`AnimationClip` 存储了模型的动画数据。 |
| WrapMode      | `AnimatorState` 是循环播放还是播放一次，默认为 `Once` 即播放一次。     |
| Speed         | `AnimatorState` 的播放速度，默认值为 1.0 ，值越大动画速度越快          |
| StartTime     | `AnimatorState` 从 `AnimationClip` 的哪个时间开始播放，时间为相对 `AnimationClip` 时长的归一化时间。默认值为 0 ，即从头开始播放。 例如：值为 1.0 ，则是 `AnimationClip` 的最后一帧状态。        |
| EndTime       | `AnimatorState` 播放到 `AnimationClip` 的哪个时间结束播放，时间为相对 `AnimationClip` 时长的归一化时间。默认值为 1.0 ，即播放到最后。                                                      |

你也可以通过修改 `Layer` 的 `Weight` 参数来调整 `Layer` 在混合中的权重，通过修改 `Blending` 来修改混合模式。



| 属性      | 功能说明                                                                    |
| :------- | :------------------------------------------------------------------------- |
| Name     | 该层的名字。                                                                 |
| Weight   | 该层的混合权重，默认值为 1.0 。                                                |
| Blending | 该层的混合模式，`Additive` 为叠加模式， `Override` 为覆盖模式，默认值为 `Override` |


#### 脚本使用

将想要叠加的动画状态添加到其他层并将它的混合模式设置为 `AnimatorLayerBlendingMode.Additive` 即可实现动画叠加效果，



### 默认播放

将 AnimatorState 连接到`entry`上你导出的项目运行时就会自动播放其上的动画，而不需再调用 `animator.play`。同时你也会看到编辑器的模型也开始播放动画了。


#### 脚本使用

你可以通过设置AnimatorStateMachine的[defaultState](${api}core/AnimatorStateMachine#defaultState) 来设置所在层的默认播放动画，这样当Animator `enabled=true` 时你不需要调用 `play` 方法即可默认播放。

```typescript
const layers = animator.animatorController.layers;
layers[0].stateMachine.defaultState = animator.findAnimatorState('walk');
layers[1].stateMachine.defaultState = animator.findAnimatorState('sad_pose');
layers[1].blendingMode = AnimatorLayerBlendingMode.Additive;
```

### 获取当前在播放的动画状态

你可以使用 [getCurrentAnimatorState](${api}core/Animator#getCurrentAnimatorState) 方法来获取当前正在播放的AnimatorState。参数为动画状态所在层的序号`layerIndex`, 详见[API文档](${api}core/Animator#getCurrentAnimatorState)。获取之后可以设置动画状态的属性，比如将默认的循环播放改为一次。

```typescript
const currentState = animator.getCurrentAnimatorState(0);
// 播放一次
currentState.wrapMode = WrapMode.Once;
// 循环播放
currentState.wrapMode = WrapMode.Loop;
```

### 获取动画状态

你可以使用 [findAnimatorState](${api}core/Animator#findAnimatorState) 方法来获取指定名称的AnimatorState。详见[API文档](${api}core/Animator#getCurrentAnimatorState)。获取之后可以设置动画状态的属性，比如将默认的循环播放改为一次。

```typescript
const state = animator.findAnimatorState('xxx');
// 播放一次
state.wrapMode = WrapMode.Once;
// 循环播放
state.wrapMode = WrapMode.Loop;
```

### 状态机脚本



状态机脚本为用户提供了动画状态的生命周期钩子函数来编写自己的游戏逻辑代码。用户可以通过继承 [StateMachineScript](${api}core/StateMachineScript) 类来使用状态机脚本。

状态机脚本提供了三个动画状态周期：

- `onStateEnter`：动画状态开始播放时回调。
- `onStateUpdate`：动画状态更新时回调。
- `onStateExit`：动画状态结束时回调。

```typescript
class theScript extends StateMachineScript {
  // onStateEnter is called when a transition starts and the state machine starts to evaluate this state
  onStateEnter(animator: Animator, stateInfo: any, layerIndex: number) {
    console.log('onStateEnter', animator, stateInfo, layerIndex);
  }

  // onStateUpdate is called on each Update frame between onStateEnter and onStateExit callbacks
  onStateUpdate(animator: Animator, stateInfo: any, layerIndex: number) {
    console.log('onStateUpdate', animator, stateInfo, layerIndex);
  }

  // onStateExit is called when a transition ends and the state machine finishes evaluating this state
  onStateExit(animator: Animator, stateInfo: any, layerIndex: number) {
    console.log('onStateExit', animator, stateInfo, layerIndex);
  }
}

animatorState.addStateMachineScript(theScript)
```

如果你的脚本不用复用的话你也可以这么写:

```typescript
state.addStateMachineScript(
  class extends StateMachineScript {
    onStateEnter(
      animator: Animator,
      animatorState: AnimatorState,
      layerIndex: number
    ): void {
      console.log("onStateEnter: ", animatorState);
    }
  }
);
```

---

## File: docs/animation-system.zh-CN.md

---
order: 6
title: 动画系统构成
type: 动画
label: Animation
---

### 动画系统的构成

```mermaid
flowchart TD
	%% Colors %%
		linkStyle default stroke-width:1px,stroke-dasharray:3
		classDef white fill:white,stroke:#000,stroke-width:1px,color:#000
		classDef yellow fill:#fffd75,stroke:#000,stroke-width:1px,color:#000
		classDef green fill:#93ff75,stroke:#000,stroke-width:1px,color:#000

    Animator:::green --> AnimatorController:::yellow
    Animator --> AnimatorControllerParameter:::white
    AnimatorController --> AnimatorControllerLayer
    AnimatorControllerLayer --> AnimatorStateMachine
    AnimatorControllerLayer --> BlendingMode:::white
    AnimatorStateMachine --> AnimatorState
    AnimatorStateMachine --> AnimatorTransition:::white
    AnimatorState --> AnimationClip
    AnimationClip:::yellow --> AnimationCurve
    AnimationClip --> AnimationEvent:::white
    AnimationCurve --> Keyframe
    AnimationCurve --> Interpolation:::white
```

| 概念                                                            | 解释                                                                                                                                                                                             |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Animator](${api}core/Animator)                                 | 动画控制器组件，用于控制动画的播放。Animator 组件读取 AnimatorController 作为动画数据。通过 AnimatorControllerParameter 设置该 Animator 中的变量。                                               |
| [AnimatorController](${api}core/AnimatorController)             | 用于存储 Animator 组件的动画数据。一个 AnimatorController 包含多个 AnimatorControllerLayer，用于分层播放或动画叠加。                                                                             |
| AnimatorControllerParameter（开发中）                           | 动画控制器中使用的变量，使用户可以通过在脚本中改变变量以控制动画状态的切换。                                                                                                                     |
| [AnimatorControllerLayer](${api}core/AnimatorControllerLayer)   | 存储该层的动画状态机数据，混合模式以及混合的权重。多个 AnimatorControllerLayer 同时播放时可以通过设置 `blendingMode = AnimatorLayerBlendingMode.Additive` 实现动画叠加的效果。                   |
| [AnimatorStateMachine](${api}core/AnimatorStateMachine)         | 每个 AnimatorControllerLayer 中有一个 AnimatorStateMachine，用于控制动画状态的播放及状态间的切换及过渡。                                                                                         |
| [BlendingMode](${api}core/AnimatorControllerLayer#blendingMode) | 动画层的混合模式，默认为 `AnimatorLayerBlendingMode.Override` 既覆盖模式，可以通过将下面的层设置为 `AnimatorLayerBlendingMode.Additive` 实现动画叠加的效果。                                     |
| [AnimatorState](${api}core/AnimatorState)                       | AnimatorState 是 AnimatorStateMachine 的基本构成。可以控制 AnimationClip 的速度，是否循环，开始结束时间。每个 AnimatorState 需绑定一个 AnimationClip，当处于该状态时，则会播放该 AnimationClip。 |
| [AnimatorTransition](${api}core/AnimatorTransition)             | AnimatorTransition 定义了状态机何时以及如何从一个状态过渡到另一个状态。通过它可以设置两个动画状态的过渡开始时间 `exitTime`，目标状态的开始时间 `offset` 及过渡时长 `duration`。                  |
| [AnimationClip](${api}core/AnimationClip)                       | 动画片段，存储设计师制作的基于关键帧的动画数据。一个 AnimationClip 一般对应一个模型的特定动作，每个 AnimationClip 包含多个 AnimationCurve。                                                      |
| [AnimationCurve](${api}core/AnimationCurve)                     | 一个模型拥有多个骨骼，模型动画中每个骨骼实体的指定属性的动画关键帧数据存储于 AnimationCurve 中。一个 AnimationCurve 中包含多个 Keyframe 既关键帧数据。                                           |
| [AnimationEvent](${api}core/AnimationEvent)                     | AnimationEvent 可以让你在指定时间调用其同一实体绑定的脚本的回调函数.                                                                                                                             |
| [Keyframe](${api}core/KeyFrame)                                 | 存储动画关键帧数据，既指定时间实体的属性的值应是多少。                                                                                                                                           |
| [Interpolation](${api}core/AnimationCurve#interpolation)        | 动画曲线中关键帧的插值方式。既当时间在两个关键帧间时，属性的值该如何计算。                                                                                                                       |

---

## File: docs/art-bake-blender.zh-CN.md

---
order: 0
title: Blender 烘焙
type: 美术
label: Art
---

> **_特别感谢 扫地盲僧及 UU 跑腿效能团队 提供本篇教程_**

美术在模型实际制作过程中，可能会使用较多的素材，材质等以达到更好的视觉效果。但在导出时会出现较大的渲染落差，有时还会丢失一些效果。

我们在最大程度还原 3D 模型渲染流程方面做了一些优化，希望能给大家带来帮助。

### Blender 烘焙

1. 选择模型，目前这个建筑由很多不同的模型组成，我们需要把它们合并成为一个模型



2. 选中 2 个模型合并的快捷键是【ctrl+j】,如果遇到哪些模型有修改器，需要先把修改器应用了再合并，否则修改器做出来的效果会失效。





3. 开始烘焙，选择着色器视图，选中材质属性，在节点视图【shift+a】新建：纹理 -> 图像纹理。



4. 点击新建>取一个合适的名字即可，左侧展开就能看到你刚新建的图像纹理。





5. 把你新建的这个图像纹理复制到建筑下的所有材质节点中，并且是选中状态。



6. 配置烘焙参数，先不要点击烘焙，需要下一步的 UV



### 拆 UV

1. 进入 UV 编辑视图，选中建筑模型，【tab 键】打开编辑模式，可以看到现在 UV 是混乱的



2. 按【U】选择智能 UV 投射，点击确定，就能获得到一个还不错的 UV 展开图，当然如果美术有时间也可以手工







3. 返回到着色器视图，选中模型图层和图层下所有材质，设置烘焙下参数不用改，点击烘焙。下面有进度条，耐心等待即可



4. 烘焙完成，左侧我们新建的图像纹理已经烘焙好了，【alt+s】可快速保存这个烘焙贴图



### 导出 glTF 文件

1. 将上述模型的所有材质都删掉，新建一个背景材质赋予上





2. 把房子的烘焙图拖进节点视图中，连接颜色



3. 导出 glTF 格式，确保这 2 个图层的眼睛和相机都开启状态



4. 放入[glTF 查看器](https://galacean.antgroup.com/#/gltf-viewer)预览效果



希望能给大家带来帮助~