# 骨骼动画入门指引

在游戏开发中,通常使用骨骼动画来实现人物、怪物等的表现。

本章节我们将使用一个简单的例子来介绍如何在小游戏3D中使用骨骼动画

# 模型资源导入

通常带动画的模型资源来自于第三方建模工具,如Maya、3DSMax,目前小游戏3D仅支持fbx格式的模型及动画资源导入。

由于fbx是一种复合资源格式,其中可能包含模型网格、材质、贴图、动画、骨架等资源,在小游戏3D中,一个fbx中包含的所有资源都可以被单独使用,这样可以实现一些工程优化需求,例如一个动画片段被多个角色复用等。

将fbx文件放入assets目录,即可在Project目录下看到。点击fbx右边的箭头,即可展开其中包含的所有资源。

随后在Hierarchy面板中右键->添加Prefab,选择Running.fbx,即可添加进场景中。

点击播放,即可看到播放人物正常运动

如果你的动画资源来自于第三方游戏引擎,请参考转换方案章节。

# 使用挂点

游戏中一个很常见的需求就是人物手持武器或者挂件,通常的做法是将武器的模型作为人物手部骨骼节点的子节点。 在小游戏框架中,基于性能考虑,人物的骨架节点并不是作为场上节点存在的。所以小游戏框架提供了挂点的机制。

这里我们以一个简单的模型代替武器来说明挂点的用法。在Running下创建一个Hand节点,创建一个Capsure模型作为其子节点,调整Capsure节点的属性到合适大小。

Running节点的Skeleton组件中点击Animation Remapping ConfigurationEdit按钮,进入到骨架节点配置模式。找到左手的骨架节点,将Hierarchy面板中Hand节点拖入Model Binding Transform字段中,该操作表示左手的骨架节点的Transform信息会被实时同步到场上的Hand节点,即Hand成为模型的挂点。

  • 注意: 挂点节点必须是Skeleton所在节点的直接子节点

点击Apply可以应用刚刚的骨架节点配置。

预览场景,可以看到模型会跟随手进行运动,Hand节点的Transform组件属性也会每帧更新

# 提取资源

本章的例子中,直接运行Running会看到动画只播了一次。如果需要循环,则需要修改AnimationClip,此时我们就需要提取资源。

Project中右键Running.fbx,选择提取。则可看到同路径下创建了Running的目录,其中包含了fbx里所含的所有资源,包含一个Prefab,以及其所依赖的Mesh、Material、Texture、AnimationClip、Avatar等

将Running目录中解包后的prefab添加到场上,随后对AnimationClip修改设置为Loop。运行场景即可看到生效。

# 使用动画状态机

在游戏中人物或怪物的动作常被定义为一个状态机的形式,人物的走、跑、跳等都被作为其中的状态。小游戏框架提供了对动画状态机的支持。

在小游戏框架中,使用AnimatorController资源表示对动画状态机的配置,使用Animator组件对动画进行控制和管理。

# 创建动画控制器(AnimatorController)

在Project目录下右键->新建->Animation->animatorcontroller,可以创建AnimatorController资源。然后新增标签->Animation->Animator打开动画Animator面板,即可进入动画状态机的编辑界面。

# 添加状态(State)

在动画状态机视图的网格位置右键->Create State->Empty,即可创建一个State。选中这个State,在Inspector面板中可以编辑详情。这里我们修改name为Running,并且选择Motion为Running的动画片段。

同时我们以相同的方法创建一个Idle的State,表示站立时的状态;创建一个Jump的State,表示跳跃的状态。 在Idle状态上右键->Set As Layer Default State,即可将Idle变成默认状态。

点上方绿色的保存按钮,保存目前为止的编辑状态。

# 使用Animator组件

在模型的Inspector面板中,添加组件->Animation->Animator,为物体添加一个Animator组件,同时可以把Animation组件禁用或删除。

将controller字段设置为刚刚创建的AnimatorController资源,播放场景,即可看到动画默认在播放Idle动作

如果想要了解更多Animator组件相关的内容,请参考Animator组件参考章节

# 在不同状态之间切换

我们在模型上添加一个自定义Script组件,命名为TestAnimator,并复制以下代码保存

import engine from "engine";
@engine.decorators.serialize("TestAnimator")
export default class TestAnimator extends engine.Script {
  public animComp: engine.Animator;

  public onStart() {
    this.animComp = this.entity.getComponent(engine.Animator);

    setTimeout(() => {
      this.animComp.crossFade("Base Layer.Running",0.5);
    }, 3000);
    setTimeout(() => {
      this.animComp.crossFade("Base Layer.Jump",0.5);
    }, 5000);
  }
}

播放场景,即可看到动画由站立平滑过度至跑步动作,又平滑过度至跳跃。

;

# 设置状态机的转移关系

实际应用中,状态之间的转移关系及条件通常会很复杂,使用代码控制难以维护。小游戏框架提供了一套状态转移编辑与控制机制。

打开Animator面板,在Idle状态上右键->Make Transition->点击Running,即可建立IdleRunning之间的状态转移。用同样的方法建立RunningRunningJump之间的双向转移。

选择左侧的Paramerters,添加一个Float类型的参数speed和一个Trigger类型的参数jump

接下来我们要为状态转移配置条件(condition)。点击Idle和Running之间的箭头,在Inspector面板下即可看到该转移的详情。在Conditions下添加一个speed Greater 0的条件

同理我们在Running指向RunningJump的转移中添加jump的条件

最后我们修改TestAnimator脚本,运行时使用代码触发Parameter变动

import engine from "engine";
@engine.decorators.serialize("TestAnimator")
export default class TestAnimator extends engine.Script {
  public animComp: engine.Animator;

  public onStart() {
    this.animComp = this.entity.getComponent(engine.Animator);

    setTimeout(() => {
      this.animComp.setFloat("speed",1.0);
    }, 3000);
    setTimeout(() => {
      this.animComp.setTrigger("jump");
    }, 5000);
  }
}

预览场景,即可看到效果