1. Project Overview & Quickstart (galacean/engine)
File: README.md
Galacean Engine
Galacean 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
- 🖥 Platform - Support HTML5 and wechat minigame
- 🔮 Graphics - Advanced 2D + 3D graphics engine
- 🏃 Animation - Powerful animation system
- 🧱 Physics - Powerful and easy-to-use physical features
- 🎨 GUI - Flexible UI system with drag-and-drop and dynamic interactions
- 👆 Input - Easy-to-use interactive capabilities
- 📑 Scripts - Use TypeScript to write logic efficiently
Usage
Using Editor
We recommend using 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:
npm install @galacean/engineCreate a simple scene:
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 / 贡献指南 before submitting changes.
Clone
Prerequisites:
- git-lfs (Install by official website)
Clone this repository:
git clone [email protected]:galacean/engine.gitBuild
Prerequisites:
- Node.js v15.0.0+ and NPM (Install by official website)
- PNPM (Install globally by
npm install pnpm)
In the folder where you have cloned the repository, install the build dependencies using pnpm:
pnpm installThen, to build the source, using npm:
npm run b:allLinks
License
The engine is released under the MIT license. See LICENSE file.
File: docs/zh/animation/examples/blending.mdx
order: 3 title: 动画混合 type: 动画 group: 指南和示例 label: Animation/Examples
本示例展示了如何在 Galacean 编辑器中实现动画的多层混合。通过简单的步骤,你将学习如何让一个模型混合播放多个动画。
如果你是第一次使用动画编辑器,建议先阅读前面几篇文档:
0. 准备工作
在开始之前,我们需要有一个有动画的模型并且它需要包含一个适合混合的动画。如果你没有模型,可以在点此下载 模型。
然后我们将其拖入到场景中。我们要有一个已编辑好的
动画控制器。请参考:编辑动画控制器。
1. 添加一个动画层
想要对动画进行混合播放,需要在动画控制器中添加一个 动画层 。当 动画控制组件 运行时,所有的 动画层 都会一起运行,多个 动画层 通过不同的权重来实现动画的混合。
关于 动画层 的详细介绍请参考 动画层 文档。
在 Layers 面板点击 + 添加一个 动画层
2. 编辑动画层
添加一个 动画状态(如果你不知道如何添加,请参考:播放模型中的动画),绑定模型中的 "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 编辑器中让用户的输入控制动画。通过简单的步骤,你将学习如何结合用户输入来控制动画的切换,让角色根据用户的交互进行相应的动作过渡。
如果你是第一次使用动画编辑器,建议先阅读前面几篇文档:
0. 准备工作
1. 添加动画控制参数
为了能够根据用户输入控制动画,首先需要给 动画控制器 加一些参数,根据这些参数来切换 动画状态 。
切换到
Parameters面板点击
+添加一个浮点参数speed,并将默认值设置为 0。
2. 添加过渡条件
之前为大家介绍了如何在 动画状态机 中添加 动画过渡(详细介绍),在之前的示例中,时间超过了 ExitTime 就会触发过渡。这里我们需要根据用户输入来触发过渡,所以我们需要添加一个 过渡条件 。
- 在
动画状态机中,选中从idle到walk的过渡,点击Add Condition添加一个Condition。
Condition 的默认值刚好符合我们的需求,当 speed 大于 0 时,就会从 idle 过渡到 walk。
同样的,选中从
walk到run的过渡,添加一个Condition,使其speed大于 5 时,从walk过渡到run。选中
run到exit的过渡,添加一个Condition,使其speed为 0 时,从run退出动画,重新回到idle。
3. 添加脚本
我们需要一个脚本来接收用户输入,并根据用户输入来修改 speed 参数的值,以触发动画状态的切换。
添加脚本有两种方式:
右键点击 资产面板 中的空白处,选择
Create->New Empty Script点击添加资产按钮
+,选择New Empty Script
4. 编辑并绑定脚本
- 双击脚本,打开脚本编辑器,输入以下代码:
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);
}
}
}关于脚本的详细说明请参考:脚本
关于用户输入的详细说明请参考:交互总览
- 找到
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 编辑器中实现动画过渡。通过简单的步骤,你将学习如何让一个动画过渡到另一个动画。
如果你是第一次使用动画编辑器,建议先阅读前面几篇文档:
0. 准备工作
在开始之前,我们需要有一个包含多个动画的模型。如果你没有模型,可以在点此下载 模型。
导入模型
如果你不清楚如何导入模型,请参考:导入模型
1. 查看模型
点击模型资产上的展开按钮
可以看到模型中包含了三个 动画片段 资产, 以及一个 动画控制器 资产
2. 创建动画控制资产
想要控制模型的动画播放,需要编辑 动画控制器 的内容,而模型中的 动画控制器 是只读的,所以需要我们自己创建一个 动画控制器 。
如果你不知道如何创建 动画控制器 ,请参考:创建动画控制器
3. 编辑动画控制器
- 打开动画控制器编辑器,添加三个
动画状态,分别绑定模型中的三个动画片段
如果你不知道如何创建 动画状态 并绑定 动画片段 ,请参考:编辑动画控制器
创建
动画过渡(关于动画过渡的详细介绍请参考 动画状态机 文档的动画过渡部分):将idle动画连接到entry,并点击连线将Duration改为 0,这样在这个动画控制器被应用时,角色就会播放idle动画。以同样的方式将
idle连接到walk,将walk连接到run, 将run连接到exit, 这样角色就会在播完idle动画后,自动过渡到walk动画,再过渡到run动画,最后到exit退出,重新进入entry,来循环播放这段动画。
4. 使用动画控制器并预览
绑定 动画控制器(如果你不知道如何绑定 动画控制器 ,请参考:使用动画控制器文档)之后点击播放按钮,可以看到模型从 idle 过渡到 walk,再过渡到 run,最后退出,重新回到 idle 状态。
用户输入控制动画
上述方式主要是为了调试动画的过渡效果,你可以修改过渡的 Duration , ExitTime , Offset 等参数(详细介绍请参考 动画状态机 文档),来调整到合适的过渡效果。
实际项目中,动画一般不会像本示例一样自动切换,大多数情况需要结合用户输入来控制动画的切换,具体可以参考:用户输入控制动画 文档
File: docs/zh/animation/examples/material-animation.mdx
order: 7 title: 材质动画 type: 动画 group: 指南和示例 label: Animation/Examples
本示例展示了如何在 Galacean 编辑器中实现材质的动画。通过简单的步骤,你将学习如何制作一个材质动画的动画片段。
如果你是第一次使用动画编辑器,建议先阅读前面几篇文档:
0. 准备工作
在开始之前,我们需要在 实体 上添加一个 网格渲染器 组件,刚创建的项目中默认会有一个立方体,它已经包含了 网格渲染器 组件。
除此以外,因为默认的 材质 是只读的,所以需要我们还需要创建一个 材质。
创建 材质 有两种方式:
右键点击 资产面板 中的空白处,选择
Create->Material点击添加资产按钮
+,选择Material
将 网格渲染器 的 材质 属性替换为刚刚创建的 材质。
1. 创建动画片段
添加好组件后, 我们需要创建一个 动画片段(关于动画片段的介绍,请参考 动画片段 文档)创建 动画片段 有两种方式:
右键点击 资产面板 中的空白处,选择
Create->Animation Clip点击添加资产按钮
+,选择Animation Clip
2. 打开动画片段编辑器
双击刚刚创建的
动画片段资产选择
实体,根据提示,我们需要在 层级面板 选择一个实体作为编辑动画的目标。
选择编辑器默认创建的 实体(你也可以点击层级面板的 + 按钮创建一个新的)
- 点击动画片段编辑器的
Create按钮,编辑器会为你做好一些准备工作,本示例你可以先忽略对这些概念的理解。
创建完成之后,你会打开动画片段编辑器,如图所示:
3. 编辑动画片段
开启
动画片段编辑器的录制模式将时间线调到
0:00, 然后将网格渲染器组件的材质的颜色属性设置为红色,在录制模式下,编辑器会自动添加对应的属性及关键帧。分别在
0:20,0:40处,将网格渲染器组件的材质的颜色属性设置为绿色, 蓝色。在1:00处,再设置回红色。
首尾的关键帧相同,动画就可以自然的循环播放了,点击播放按钮,可以预览这个动画片段。
这样我们的材质颜色变化 动画片段 就制作完成了。关于动画编辑器的更多操作,请参考 动画片段 文档。
关于如何使用这个 动画片段 到你的项目中,可以参考文章顶部列出的两篇文档。
File: docs/zh/animation/examples/playAnimation.mdx
order: 0 title: 播放模型中的动画 type: 动画 group: 指南和示例 label: Animation/Examples
本示例展示了如何在 Galacean 编辑器中播放模型自带的动画。通过简单的步骤,你将学习如何加载一个带有动画的 3D 模型,并控制其动画的播放。这个指南适合初学者了解 Galacean 编辑器的动画系统,也为更复杂的动画应用奠定基础。
0. 准备工作
在开始之前,我们需要有一个带动画的模型。如果你没有模型,可以在 mixamo 上下载一个。
下载模型
导入模型
有三种方式导入模型:
1. 查看模型
点击模型资产上的展开按钮
可以看到模型中包含了一个 动画片段(详细介绍)资产(如果模型中包含多个动画,这里会有多个 动画片段 资产)
以及一个 动画控制器(详细介绍)资产
2. 创建动画控制资产
想要控制动画的播放,需要编辑 动画控制器 的内容,而模型中的 动画控制器 是只读的,所以需要我们自己创建一个 动画控制器 。
创建 动画控制器 有两种方式:
右键点击 资产面板 中的空白处,选择
Create->Animation Controller点击添加资产按钮
+,选择Animation Controller
3. 编辑动画控制器
点击添加动画状态按钮,添加一个动画状态
点击动画状态(动画状态的属性介绍请参考 动画状态机 文档的动画状态部分),绑定动画片段。
从
entry连接到此动画状态并点击连线,并将Duration改为0(动画过渡的详细介绍请参考 动画状态机 文档),这样在这个动画控制器被应用时,就会立即自动播放此动画状态。
4. 使用动画控制器并预览
将模型拖入到场景中
找到
动画控制组件(详细介绍 , 组件一般在模型实例的根实体上,也就是编辑器的模型实体下的第一个子实体上。在动画控制器属性上替换为刚才创建的
动画控制器点击播放按钮,就可以看到模型开始播放动画了
File: docs/zh/animation/examples/reuseAnimation.mdx
order: 1 title: 动画复用 type: 动画 group: 指南和示例 label: Animation/Examples
本示例展示了如何在 Galacean 编辑器中播放其他模型的动画。通过简单的步骤,你将学习如何让一个模型复用另一个模型的动画。这个指南适合初学者了解 Galacean 编辑器的动画系统,也为更复杂的动画应用奠定基础。
如果你是第一次使用动画编辑器,建议先阅读这篇文档:
0. 准备工作
在开始之前,我们需要有一个没有动画的模型,和一个只有动画的模型(角色模型必须包含与动画模型同名的实体)。如果你没有模型,可以在 mixamo 上下载一个。
下载模型
- 在 mixamo 中选择一个模型
直接点击下载
- 在 动画Tab 中选择一个动画
选择 Without Skin 选项,然后点击下载
导入模型
如果你不知道如何导入模型,请参考:导入模型
将角色模型和动画模型导入到编辑器中。
1. 创建动画控制资产
想要播放其他模型的动画,需要编辑 动画控制器 的内容,而模型中的 动画控制器 是只读的,所以需要我们自己创建一个 动画控制器 。
如果你不知道如何创建 动画控制器 ,请参考:创建动画控制器
2. 编辑动画控制器
点击
添加动画状态按钮,添加一个动画状态点击
动画状态(动画状态的属性介绍请参考 动画状态 文档),绑定动画模型中的动画片段。从
entry连接到此动画状态并点击连线将Duration改为 0(动画状态及动画过渡的详细介绍请参考 动画状态机 文档),这样在这个动画控制器被应用时,就会立即自动播放此动画状态。
3. 使用动画控制器并预览
将模型拖入到场景中
与直接播放模型中的动画不同,我们需要找到动画模型的
动画控制组件(详细介绍)位置, 组件一般在模型实例的根实体上,也就是编辑器的模型实体下的第一个子实体上,因此我们需要将包含动画的模型也拖到节点树中
我们发现 动画控制组件 在动画模型的 mixamorig:Hips 实体上,因此我们需要在角色模型的mixamorig:Hips实体上添加 动画控制组件
在动画控制器属性上替换为刚才创建的
动画控制器点击播放按钮,就可以看到模型开始播放动画了
File: docs/zh/animation/examples/sprite-sheet.mdx
order: 6 title: 2D 帧动画 type: 动画 group: 指南和示例 label: Animation/Examples
本示例展示了如何在 Galacean 编辑器中实现 2D 帧动画。通过简单的步骤,你将学习如何制作一个帧动画动画片段。
如果你是第一次使用动画编辑器,建议先阅读前面几篇文档:
0. 准备工作
在开始之前,我们需要有制作帧动画所需的序列帧图片,雪碧图和单帧图片都可以。本示例使用雪碧图,如果你没有,可以在这里下载 雪碧图。
1. 导入纹理
有三种方式导入 纹理 :
2. 创建精灵
关于 精灵 的详细介绍请参考 精灵 文档
创建 精灵 有三种方式:
右键点击 资产面板 中的空白处,选择
Create->Sprite点击添加资产按钮
+,选择Sprite如果你是单帧图片,可以右键点击 资产面板 中的空白处,选择
Upload->Sprite,然后选择你的单帧图片。
这种方式,在导入纹理的同时会自动创建一个精灵资产并绑定此图片作为其纹理。
创建三个 精灵 资产,分别对应雪碧图中的三帧。
3. 为精灵绑定纹理
如果你使用雪碧图的话,需要根据帧数,配置合适的 Region。如果你使用单帧图片的话,直接按第一步绑定即可。
点击
精灵资产,点击纹理属性,选择对应的纹理。根据帧数,配置合适的
Region,本示例共有三帧,并且只有一排, 所以每帧的Region的W为 1/3 (对应的纹理width也改为原来的1/3)H为 1。修改另外两个
精灵的Region,与第一个精灵的Region不同的是,它们的Region的X值不同,分别为 0, 1/3, 2/3。
Sprite1 :
Sprite2 :
全部绑定好纹理后,精灵 资产会展示对应的图片,如图所示:
4. 创建动画片段
准备好 精灵 后, 我们需要创建一个 动画片段(关于动画片段的介绍,请参考 动画片段 文档)创建 动画片段 有两种方式:
右键点击 资产面板 中的空白处,选择
Create->Animation Clip点击添加资产按钮
+,选择Animation Clip
5. 打开动画片段编辑器
双击刚刚创建的
动画片段资产选择
实体,根据提示,我们需要在 层级面板 选择一个实体作为编辑动画的目标。
选择编辑器默认创建的 实体(你也可以点击层级面板的 + 按钮创建一个新的)
- 点击动画片段编辑器的
Create按钮,编辑器会为你做好一些准备工作,本示例你可以先忽略对这些概念的理解。
创建完成之后,你会打开动画片段编辑器,如图所示:
6. 设置实体并切换到2D视图
删掉默认的
网格渲染器组件(如果你是自己创建的, 忽略)。为选择的实体添加一个精灵渲染器组件,为
精灵渲染器组件的精灵属性绑定一个我们刚刚创建的精灵资产, 作为默认的精灵。将
实体默认的旋转重制,并切换到2D视图
7. 编辑动画片段
开启
动画片段编辑器的录制模式将时间线调到
0:00, 然后将精灵渲染器组件的精灵属性切换到第一帧的精灵资产。因为我们的第一帧与精灵渲染器组件的默认精灵相同,所以仍然选择此精灵。
在录制模式下,编辑器会自动添加对应的属性及关键帧。分别在
0:20,0:40处,将精灵渲染器组件的精灵属性切换到第二帧,第三帧的精灵资产。在1:00处,再切换回第一帧的精灵资产。
首尾的关键帧相同,在循环播放时动画就不会出现抖动了,点击播放按钮,可以预览这个动画片段。
这样我们的帧动画 动画片段 就制作完成了。关于动画编辑器的更多操作,请参考 动画片段 文档。
关于如何应用这个 动画片段 到你的项目中,可以参考文章顶部列出的两篇文档。
File: docs/zh/animation/examples/text-animation.mdx
order: 5 title: 文字动画 type: 动画 group: 指南和示例 label: Animation/Examples
本示例展示了如何在 Galacean 编辑器中实现文字的动画。通过简单的步骤,你将学习如何制作一个文字动画的动画片段。
如果你是第一次使用动画编辑器,建议先阅读前面几篇文档:
0. 准备工作
在开始之前,我们需要在 实体 上添加一个 文本渲染器 组件。
1. 创建动画片段
添加好组件后, 我们需要创建一个 动画片段(关于动画片段的介绍,请参考 动画片段 文档)创建 动画片段 有两种方式:
右键点击 资产面板 中的空白处,选择
Create->Animation Clip点击添加资产按钮
+,选择Animation Clip
2. 打开动画片段编辑器
双击刚刚创建的
动画片段资产选择
实体,根据提示,我们需要在 层级面板 选择一个实体作为编辑动画的目标。
选择编辑器默认创建的 实体(你也可以点击层级面板的 + 按钮创建一个新的)
- 点击动画片段编辑器的
Create按钮,编辑器会为你做好一些准备工作,本示例你可以先忽略对这些概念的理解。
创建完成之后,你会打开动画片段编辑器,如图所示:
3. 设置测试文字并切换到2D视图
在
实体上的文本渲染器组件的文本属性中输入Hello World,并设置字体大小为200。你也可以按需设置其他属性,关于文字的更多信息,请参考 文本渲染器 文档。切换到
2D视图
4. 编辑动画片段
开启
动画片段编辑器的录制模式将时间线调到
0:00, 然后将文本渲染器组件的字体大小属性设置为0,在录制模式下,编辑器会自动添加对应的属性及关键帧。分别在
0:30,1:00处,将文本渲染器组件的字体大小属性设置为100, 200。点击播放按钮,可以预览这个动画片段。
这样我们的文字放大 动画片段 就制作完成了,你也可以尝试其他 文本渲染器 的属性,比如 颜色,字体 等。关于动画编辑器的更多操作,请参考 动画片段 文档。
关于如何使用这个 动画片段 到你的项目中,可以参考文章顶部列出的两篇文档。
File: docs/zh/animation/animator.mdx
order: 3 title: 动画控制组件 type: 动画 label: Animation
简介
动画控制组件(API)的作用是读取 动画控制器(详细介绍)的数据,并播放其内容。
参数说明
| 属性 | 功能说明 |
|---|---|
| animatorController | 绑定 AnimatorController 资产 |
编辑器使用
当把模型拖入到场景中,模型以初始姿态展示,并不会播放任何动画,这时需要在模型实体上找到 动画控制组件 并为它绑定一个 动画控制器 资产。
- 找到或者创建
动画控制组件
如果模型内有动画,会为你自动绑定一个只读的 动画控制器。
如果没有 动画控制组件 可以按下图方式创建
创建一个
动画控制器资产并绑定到模型上,创建动画控制器有两种方式:右键点击 资产面板 中的空白处,选择
Create->Animation Controller点击添加资产按钮
+,选择Animation Controller
在
动画控制组件的动画控制器属性上替换为刚才创建的动画控制器编辑
动画控制器后(可以参考动画控制器文档)你就可以播放其中的动画了
脚本使用
在使用脚本之前,建议先阅读以下文档:
播放指定动画状态
你可以使用 animator.play 方法来播放指定的 动画状态,参数说明详见API 文档。
import { Script, Animator, Keys } from '@galacean/engine';
export default class extends Script {
onStart() {
const animator = this.entity.getComponent(Animator);
animator.play("State Name");
}
}如果需要在动画中的某一时刻开始播放可以通过以下方式
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 属性来控制动画的播放速度。 speed 默认值为 1.0 ,值越大播放的越快,越小播放的越慢。当值为负数时,进行倒播。
animator.speed = 2.0;停止/重新播放
你可以通过设置 animator.enabled 来停止和重新播放动画
// 停止
animator.enabled = false;
// 重新播放
animator.enabled = true;暂停/恢复播放
你可以通过设置 animator.speed 暂停和恢复播放
// 暂停
animator.speed = 0;
// 恢复
animator.speed = 1;如果你只想针对某一个 动画状态 进行暂停,可以通过将它的速度设置为 0 来实现。
const state = animator.findAnimatorState("xxx");
state.speed = 0;过渡指定动画状态
你可以使用 animator.crossFade 方法来将动画过渡到指定的 动画状态,参数说明详见API 文档。
animator.crossFade("OtherStateName", 0.3);获取当前在播放的动画状态
你可以使用 getCurrentAnimatorState 方法来获取当前正在播放的 动画状态。参数为 动画状态 所在 动画层 的序号layerIndex, 详见API 文档。获取之后可以设置 动画状态 的属性,比如将默认的循环播放改为一次。
const currentState = animator.getCurrentAnimatorState(0);
// 播放一次
currentState.wrapMode = WrapMode.Once;
// 循环播放
currentState.wrapMode = WrapMode.Loop;获取动画状态
你可以使用 findAnimatorState 方法来获取指定名称的 动画状态 。获取之后可以设置动画状态的属性,比如将默认的循环播放改为一次。
const state = animator.findAnimatorState("xxx");
// 播放一次
state.wrapMode = WrapMode.Once;
// 循环播放
state.wrapMode = WrapMode.Loop;动画裁剪
你可以通过设置 动画控制组件 的 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.
Galacean Site
The documentation source of Galacean Engine.
Development
Develop site
$ yarn
$ yarn dev
open http://local.alipay.netOnly develop playground
$ yarn playgroundDeploy
$ yarn buildQ&A
How to insert a playground in markdown
// leave a blank line hereHow to link engine
- Mac
$ yarn link ../engine/packages/*- windows
$ yarn link ..\engine\packages\galaceanFile: docs/animation-animator.zh-CN.md
order: 3 title: 动画控制组件 type: 动画 label: Animation
动画控制组件(Animator)的作用是读取动画控制器(AnimatorController)的数据,并播放其内容。
参数说明
| 属性 | 功能说明 |
|---|---|
| animatorController | 绑定 AnimatorController 资产 |
编辑器使用
当我们把模型拖入到场景中,模型以初始姿态展示出来,但是并不会播放任何动画,我们需要在模型实体上添加动画控制组件(Animator)
至此你在导出的项目中就可以通过
animator.play播放动画控制器中的动画了。
如果你没有为实体添加 动画控制组件(Animator)的话 Galacean Engine 会为你默认创建一个并且 动画控制器 中默认添加了模型的所有动画片段,当然除此以外你可以通过 动画控制器 实现更多的功能。
脚本使用
在使用脚本之前,最好阅读动画系统构成文档,以帮助你更好的了解动画系统的运行逻辑
播放动画
在加载 GLTF 模型后引擎会自动为模型添加一个 Animator 组件,并将模型中的动画片段加入其中。可以直接在模型的根实体上获取 Animator 组件,并播放指定动画。
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 属性来控制动画的播放速度。 speed 默认值为 1.0 ,值越大播放的越快,越小播放的越慢。当值为负数时,进行倒播。
animator.speed = 2.0;暂停/恢复播放
你可以通过设置 Animator 的 enabled 来控制动画的暂停和播放.
// 暂停
animator.enabled = false;
// 恢复
animator.enabled = true;如果你只想针对某一个动画状态进行暂停,可以通过将它的速度设置为 0 来实现。
const state = animator.findAnimatorState("xxx");
state.speed = 0;播放指定动画状态
你可以使用 play 方法来播放指定的 AnimatorState。参数为 AnimatorState 的name,其他参数说明详见API 文档。
animator.play("run");如果需要在动画中的某一时刻开始播放可以通过以下方式
const layerIndex = 0;
const normalizedTimeOffset = 0.5; // 归一化的时间
animator.play("run", layerIndex, normalizedTimeOffset);获取当前在播放的动画状态
你可以使用 getCurrentAnimatorState 方法来获取当前正在播放的 AnimatorState。参数为动画状态所在层的序号layerIndex, 详见API 文档。获取之后可以设置动画状态的属性,比如将默认的循环播放改为一次。
const currentState = animator.getCurrentAnimatorState(0);
// 播放一次
currentState.wrapMode = WrapMode.Once;
// 循环播放
currentState.wrapMode = WrapMode.Loop;获取动画状态
你可以使用 findAnimatorState 方法来获取指定名称的 AnimatorState。详见API 文档。获取之后可以设置动画状态的属性,比如将默认的循环播放改为一次。
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)用于组织动画片段(AnimationClip)实现更加灵活丰富的动画效果。
编辑器使用
基础使用
通过动画控制器的编辑器,用户可以在其中组织动画片段的播放逻辑
准备好动画片段(制作动画片段)
要组织播放这些动画片段我们需要创建一个动画控制器(AnimatorController)资产
刚创建的动画控制器中没有任何数据,我们需要对他进行编辑,双击资产, 并为它添加一个 AnimatorState
点击 AnimatorState 为它绑定一个动画片段
在动画控制组件上绑定该动画控制器(AnimatorController)资产
至此你在导出的项目中就可以通过
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 |
脚本使用
在使用脚本之前,最好阅读动画系统构成文档,以帮助你更好的了解动画系统的运行逻辑
默认播放
你可以通过设置 AnimatorStateMachine 的defaultState 来设置所在层的默认播放动画,这样当 Animator enabled=true 时你不需要调用 play 方法即可默认播放。
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 实现动画状态间的过渡。
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 属性来设置动画控制器的动画数据,加载完成的 GLTF 模型会自动添加一个默认的 AnimatorController。
animator.animatorController = new AnimatorController();复用动画数据
有的时候模型的动画数据存储在其他模型中,可以用如下的方式引入使用:
除此以外还有一种方式,Animator 的 AnimatorController 就是一个数据存储的类,它不会包含运行时的数据,基于这种设计只要绑定 Animator 组件的模型的骨骼节点的层级结构和命名相同,我们就可以对动画数据进行复用。
const animator = model1.getComponent(Animator);
animator.animatorController = model2.getComponent(Animator).animatorController;状态机脚本
状态机脚本为用户提供了动画状态的生命周期钩子函数来编写自己的游戏逻辑代码。用户可以通过继承 StateMachineScript 类来使用状态机脚本。
状态机脚本提供了三个动画状态周期:
onStateEnter:动画状态开始播放时回调。onStateUpdate:动画状态更新时回调。onStateExit:动画状态结束时回调。
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);如果你的脚本不用复用的话你也可以这么写:
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 中会被解析成一个个的动画片段资产,我们也可以通过动画片段编辑器创作额外的动画。
有两种常用的方式得到动画片段
导入用第三方工具(例如 Autodesk® 3ds Max®, Autodesk® Maya®, Blender)创建的带动画的模型,详见美术制作动画片段
在 Galacean 中创建动画片段(下文会介绍编辑器和脚本这两种创建方式)
动画面板介绍
动画片段编辑器目前支持除物理相关组件的所有可插值属性的编辑,如果你需要编辑其他属性,要在实体上添加对应的组件。
注:Galacean 动画编辑器默认以 60FPS 为基准,上图所示时间
0:30为 30 帧处, 若时间轴刻度为1:30则为 90 帧处
基础操作
Transform 组件示例
在 资产面板 中 右键/点击+ 创建
动画片段资产双击
动画片段资产,并选择一个实体作为动画片段的编辑对象
点击 创建 按钮编辑器会自动为你的Entity添加 动画控制组件 并将该动画片段添加到 动画控制器 中
添加要做动画的属性(这里我添加了两个)
给属性添加关键帧
当我们点击添加关键帧按钮时,关键帧会存入当前指定属性的值,所以当我们什么都未改变时,关键帧存入的是此刻这个实体的 position 值。我们希望他 60 帧后移动到 (3, 0, 0)的位置,所以先将这个正方体通过属性面板修改到(3, 0, 0) 再添加关键帧
同理我们也为 rotation 属性添加关键帧
录制模式
我们提供了录制模式以方便开发者快速的添加关键帧。开启录制模式,当对应的属性修改时就会自动添加关键帧
文字动画示例
首先要有一个具有 TextRender 组件的实体
此时我们再添加属性时可以看到,可添加关键帧的属性增加了 TextRenderer 组件相关的可插值的属性
我们按照上文的方式用录制模式添加关键帧
帧动画示例
除了数值类型,Galacean 还支持引用类型的动画曲线,可以阅读帧动画来了解,如何制作帧动画。
材质动画示例
Galacean 还支持组件中的资产属性的动画编辑。如果组件中有材质资产,在 Inspector 中会有额外的资产属性的编辑
需要注意的是,编辑器默认的材质是不可编辑的
所以我们想给这个立方体做材质的动画需要新建一个材质并替换它之后就同上文一样开启录制模式直接修改属性即可
更多操作
操作关键帧
修改关键帧时间
选中关键帧并拖动即可
可以滑动 鼠标滚轮 缩放时间轴
修改关键帧的值
开启录制模式,把时间线移动到指定关键帧上,然后重新输入值即可, 在不开启录制模式的情况,需要重新点击添加关键帧按钮。
删除关键帧
选中关键帧右键选择删除,也可以按键盘上的删除键/退格键删除
编辑子实体
动画片段 不仅可以作用于添加Animator组件的实体上,还可以作用于它的子实体上。
我们为刚才的立方体添加一个子实体
我们再点击添加属性就可以看到子实体的属性可以添加了
开启录制模式,编辑子实体添加关键帧即可
编辑动画曲线
动画片段编辑器 支持动画曲线编辑,点击右上角的曲线Icon即可切换
曲线模式的纵轴为属性的数值
你可以按 shift+鼠标滚轮 调整纵轴的比例
属性对应曲线的颜色与按钮的颜色相同
选择关键帧会出现两个控制点,调整控制点即可调整曲线
也可以通过右键关键帧直接设置内置的预设值
选择属性面板的属性可以仅编辑指定属性的曲线
脚本使用
在使用脚本之前,最好阅读动画系统构成文档,以帮助你更好的了解动画系统的运行逻辑
你可以自己创建一个 AnimationClip 并通过 addCurveBinding 为它绑定 AnimationCurve。
//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 来为 AnimationClip 添加事件,动画事件将在指定时间调用你在同一实体上绑定组件的指定回调函数。
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 预览 页面进行功能校验。
Blender 的动画编辑页面非常友好,能够清晰地可视化显示受动画影响的节点,并且在时间轴上显示关键帧,因此推荐使用 Blender 进行动画切片。
Blender
打开 Blender,导入 Blender 支持的模型格式,然后切换到 动画编辑 窗口:
通过上图的 “新建动画切片”按钮,可以快速的复制当前动画切片,然后进行独有的操作,如果没有显示该按钮,请确保打开了 “动作编辑器”:
举例,新建了一个名为 animation_copy 的动画切片,然后只保留最后 5 帧动画:
导出的切片时间轴必须一致,可以通过右下角或者输出属性两个地方进行配置:
因为时间轴必须一致,因此需要将刚才切的动画切片,都移到起始帧,拖拽即可:
至此,动画切片已经准备完成,导出 glTF 或者 FBX ,接入 Galacean 引擎即可:
Unity 也可以导出动画切片,但是效率比较低。
Unity
插件:AntG-Unity-Plugin.unitypackage.zip
下载插件。
打开 Unity 。
双击插件, Import 默认框选选项:
若安装成功,可以看到菜单栏多出 AntG 选项:
把需要切片的 FBX 文件拖拽进资源栏:
单击该资源,出现动画调试预览框。增加动画切片,并根据需求调整每个切片的时间轴 start、end,如果预览动画效果异常,确认没有勾选 Resample Curves 这个默认开启选项,切片完成后,记得点击右下角的 Apply 确认按钮。
至此,动画切片资源已经制作完毕,接下来介绍如何导出,先将该资源拖拽到节点树中:
然后给该节点增加 Animator Component:
可以看到,Animator 组件需要绑定一个 Animator Controller 资源,因此我们需要在资源栏新建一个 Animator Controller 资源:
双击该 controller 资源,将我们之前的动画切片拖拽进去:
Animator Controller 资源制作完毕,绑定到刚才的 Component 上:
右键该节点,选择导出 AntG:
至此,制作的动画切片 glTF 文件导出完毕,可以访问 Galacean 的 glTF 预览 进行功能校验。
File: docs/animation-overview.zh-CN.md
order: 0 title: 动画系统概述 type: 动画 label: Animation
Galacean 的动画系统具有以下功能:
- 解析 GLTF/FBX 模型中的动画并转为 Galacean 中的 AnimationClip 对象
- 为 Galacean 的所有组件及其属性添加动画
- 设置动画的开始/结束时间以裁剪动画
- 设置动画间的过渡,将多个动画叠加
- 将一个模型的动画应用到另一个模型上
- 添加动画事件及动画生命周期的脚本
动画工作流程
使用编辑器创建互动项目的整体流程:
flowchart LR
添加动画片段 --> 创建动画控制器并导入动画片段 --> 添加动画控制组件播放动画1. 添加动画片段
Galacean 的动画系统基于动画片段的概念,动画片段包含某些对象应如何随时间改变其位置、旋转或其他属性的相关信息。每个动画片段可视为单个线性录制。
你可以在编辑器中创建动画片段,详见制作动画片段,也可以导入用第三方工具(例如 Autodesk® 3ds Max®, Autodesk® Maya®, Blender)创建的带动画的模型,详见美术制作动画片段,或者来自动作捕捉工作室或其他来源。
2. 创建动画控制器并导入动画片段
动画控制器是一个类似于流程图的结构化系统,它在动画系统中充当状态机,负责跟踪当前应该播放哪个片段以及动画应该何时改变或混合在一起。
你可以在这篇动画控制器中了解如何使用。
3. 添加动画控制组件
编辑好动画控制器后,我们需要在实体上添加动画控制组件并绑定动画控制器资产来播放动画。
File: docs/animation-sprite-sheet.zh-CN.md
order: 4 title: 帧动画 type: 动画 label: Animation
Galacean 支持引用类型的动画曲线,你可以添加类型为资产的关键帧比如(精灵)下图为制作精灵动画的流程:
给节点添加
SpriteRenderer组件添加
精灵,可以参考精灵开启录制模式,编辑器中点到对应的帧数,在
SpriteRenderer中添加Sprite即可自动添加关键帧
脚本实现
引擎在 1.1 版本支持引用类型的动画曲线(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 实现动画状态间的过渡。
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 来设置所在层的默认播放动画,这样当Animator enabled=true 时你不需要调用 play 方法即可默认播放。
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 方法来获取当前正在播放的AnimatorState。参数为动画状态所在层的序号layerIndex, 详见API文档。获取之后可以设置动画状态的属性,比如将默认的循环播放改为一次。
const currentState = animator.getCurrentAnimatorState(0);
// 播放一次
currentState.wrapMode = WrapMode.Once;
// 循环播放
currentState.wrapMode = WrapMode.Loop;获取动画状态
你可以使用 findAnimatorState 方法来获取指定名称的AnimatorState。详见API文档。获取之后可以设置动画状态的属性,比如将默认的循环播放改为一次。
const state = animator.findAnimatorState('xxx');
// 播放一次
state.wrapMode = WrapMode.Once;
// 循环播放
state.wrapMode = WrapMode.Loop;状态机脚本
状态机脚本为用户提供了动画状态的生命周期钩子函数来编写自己的游戏逻辑代码。用户可以通过继承 StateMachineScript 类来使用状态机脚本。
状态机脚本提供了三个动画状态周期:
onStateEnter:动画状态开始播放时回调。onStateUpdate:动画状态更新时回调。onStateExit:动画状态结束时回调。
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)如果你的脚本不用复用的话你也可以这么写:
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
动画系统的构成
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 | 动画控制器组件,用于控制动画的播放。Animator 组件读取 AnimatorController 作为动画数据。通过 AnimatorControllerParameter 设置该 Animator 中的变量。 |
| AnimatorController | 用于存储 Animator 组件的动画数据。一个 AnimatorController 包含多个 AnimatorControllerLayer,用于分层播放或动画叠加。 |
| AnimatorControllerParameter(开发中) | 动画控制器中使用的变量,使用户可以通过在脚本中改变变量以控制动画状态的切换。 |
| AnimatorControllerLayer | 存储该层的动画状态机数据,混合模式以及混合的权重。多个 AnimatorControllerLayer 同时播放时可以通过设置 blendingMode = AnimatorLayerBlendingMode.Additive 实现动画叠加的效果。 |
| AnimatorStateMachine | 每个 AnimatorControllerLayer 中有一个 AnimatorStateMachine,用于控制动画状态的播放及状态间的切换及过渡。 |
| BlendingMode | 动画层的混合模式,默认为 AnimatorLayerBlendingMode.Override 既覆盖模式,可以通过将下面的层设置为 AnimatorLayerBlendingMode.Additive 实现动画叠加的效果。 |
| AnimatorState | AnimatorState 是 AnimatorStateMachine 的基本构成。可以控制 AnimationClip 的速度,是否循环,开始结束时间。每个 AnimatorState 需绑定一个 AnimationClip,当处于该状态时,则会播放该 AnimationClip。 |
| AnimatorTransition | AnimatorTransition 定义了状态机何时以及如何从一个状态过渡到另一个状态。通过它可以设置两个动画状态的过渡开始时间 exitTime,目标状态的开始时间 offset 及过渡时长 duration。 |
| AnimationClip | 动画片段,存储设计师制作的基于关键帧的动画数据。一个 AnimationClip 一般对应一个模型的特定动作,每个 AnimationClip 包含多个 AnimationCurve。 |
| AnimationCurve | 一个模型拥有多个骨骼,模型动画中每个骨骼实体的指定属性的动画关键帧数据存储于 AnimationCurve 中。一个 AnimationCurve 中包含多个 Keyframe 既关键帧数据。 |
| AnimationEvent | AnimationEvent 可以让你在指定时间调用其同一实体绑定的脚本的回调函数. |
| Keyframe | 存储动画关键帧数据,既指定时间实体的属性的值应是多少。 |
| Interpolation | 动画曲线中关键帧的插值方式。既当时间在两个关键帧间时,属性的值该如何计算。 |
File: docs/art-bake-blender.zh-CN.md
order: 0 title: Blender 烘焙 type: 美术 label: Art
特别感谢 扫地盲僧及 UU 跑腿效能团队 提供本篇教程
美术在模型实际制作过程中,可能会使用较多的素材,材质等以达到更好的视觉效果。但在导出时会出现较大的渲染落差,有时还会丢失一些效果。
我们在最大程度还原 3D 模型渲染流程方面做了一些优化,希望能给大家带来帮助。
Blender 烘焙
选择模型,目前这个建筑由很多不同的模型组成,我们需要把它们合并成为一个模型
选中 2 个模型合并的快捷键是【ctrl+j】,如果遇到哪些模型有修改器,需要先把修改器应用了再合并,否则修改器做出来的效果会失效。
开始烘焙,选择着色器视图,选中材质属性,在节点视图【shift+a】新建:纹理 -> 图像纹理。
点击新建>取一个合适的名字即可,左侧展开就能看到你刚新建的图像纹理。
把你新建的这个图像纹理复制到建筑下的所有材质节点中,并且是选中状态。
配置烘焙参数,先不要点击烘焙,需要下一步的 UV
拆 UV
进入 UV 编辑视图,选中建筑模型,【tab 键】打开编辑模式,可以看到现在 UV 是混乱的
按【U】选择智能 UV 投射,点击确定,就能获得到一个还不错的 UV 展开图,当然如果美术有时间也可以手工
返回到着色器视图,选中模型图层和图层下所有材质,设置烘焙下参数不用改,点击烘焙。下面有进度条,耐心等待即可
烘焙完成,左侧我们新建的图像纹理已经烘焙好了,【alt+s】可快速保存这个烘焙贴图
导出 glTF 文件
将上述模型的所有材质都删掉,新建一个背景材质赋予上
把房子的烘焙图拖进节点视图中,连接颜色
导出 glTF 格式,确保这 2 个图层的眼睛和相机都开启状态
放入glTF 查看器预览效果
希望能给大家带来帮助~