Laya.Animator2D 使用指南
简介
Laya.Animator2D 是 LayaAir 引擎的 2D 动画组件,用于控制 2D 精灵的动画播放。通过动画状态机(AnimatorController2D)管理多个动画状态的切换,支持参数控制、混合过渡、多层动画等高级功能。
适用场景:
- 角色动画状态管理(待机、行走、攻击、死亡等)
- 复杂动画切换和过渡
- 参数驱动的动画控制
- 多层动画混合(如下半身行走 + 上半身攻击)
继承关系:
复制代码
Laya.Animator2D → Laya.Component → Laya.Object
目录
API 参考
属性
| 属性 |
类型 |
默认值 |
说明 |
controller |
AnimatorController2D |
- |
2D 动画控制器 |
parameters |
Record<string, Animation2DParm> |
- |
动画参数集合 |
speed |
number |
1.0 |
播放速度倍率 |
isPlaying |
boolean |
- |
是否正在播放(只读) |
播放控制
| 方法 |
参数 |
返回值 |
说明 |
play(name?, layerIndex?, normalizedTime?) |
状态名, 层索引, 归一化时间 |
void |
播放指定动画状态 |
stop() |
- |
void |
停止播放动画 |
crossFade(name, layerIndex, normalizedTime, transitionDuration?) |
状态名, 层索引, 起始时间, 过渡时间 |
boolean |
融合过渡到目标状态 |
gotoAndStop(name, layerIndex, normalizedTime) |
状态名, 层索引, 归一化时间 |
void |
跳转到指定时间并停止 |
gotoAndStopByFrame(name, layerIndex, frame) |
状态名, 层索引, 帧号 |
void |
跳转到指定帧并停止 |
参数控制
| 方法 |
参数 |
返回值 |
说明 |
setParamsTrigger(name) |
参数名(字符串或数字) |
void |
触发 Trigger 类型参数 |
setParamsBool(name, value) |
参数名, 布尔值 |
void |
设置 Bool 类型参数 |
setParamsNumber(name, value) |
参数名, 数值 |
void |
设置 Number 类型参数 |
getParamsvalue(name) |
参数名(字符串或数字) |
`number |
boolean` |
层管理
| 方法 |
参数 |
返回值 |
说明 |
addControllerLayer(controllerLayer) |
控制器层 |
void |
添加动画控制器层 |
getControllerLayer(layerIndex?) |
层索引 |
AnimatorControllerLayer2D |
获取控制器层 |
getDefaultState(layerIndex?) |
层索引 |
- |
获取默认状态 |
生命周期
| 方法 |
参数 |
返回值 |
说明 |
onUpdate() |
- |
void |
每帧更新(由引擎自动调用) |
相关类型
AnimatorController2D - 动画控制器
typescript
复制代码
// 动画控制器,管理动画状态机
let controller = new Laya.AnimatorController2D(data);
animator.controller = controller;
AnimatorState2D - 动画状态
| 属性 |
类型 |
说明 |
name |
string |
状态名称 |
clip |
AnimationClip2D |
动画片段 |
speed |
number |
播放速度 |
loop |
number |
循环次数(-1=跟随clip, 0=无限, 1=一次) |
yoyo |
boolean |
是否往返播放 |
clipStart |
number |
播放起始时间(0-1) |
clipEnd |
number |
播放结束时间(0-1) |
cycleOffset |
number |
播放起始偏移 |
状态事件
| 事件 |
说明 |
EVENT_OnStateEnter |
进入状态时触发 |
EVENT_OnStateUpdate |
状态更新时触发 |
EVENT_OnStateExit |
退出状态时触发 |
EVENT_OnStateLoop |
状态循环时触发 |
EVENT_OnStateSwitch |
状态切换时触发 |
基础用法
1. 添加动画组件
typescript
复制代码
@regClass()
export class Hero extends Laya.Sprite {
private animator: Laya.Animator2D;
onAwake(): void {
// 添加动画组件
this.animator = this.addComponent(Laya.Animator2D);
}
}
2. 设置动画控制器
typescript
复制代码
// 加载动画控制器资源
Laya.loader.load("res/hero/HeroController.controller", Laya.Handler.create(this, (controller: Laya.AnimatorController2D) => {
this.animator.controller = controller;
this.animator.play(); // 播放默认动画
}));
3. 播放指定动画
typescript
复制代码
// 播放名为 "Walk" 的动画状态
this.animator.play("Walk");
// 播放第 0 层的 "Run" 动画
this.animator.play("Run", 0);
// 从动画中间开始播放(normalizedTime: 0.5 = 50% 位置)
this.animator.play("Attack", 0, 0.5);
4. 停止动画
typescript
复制代码
// 停止所有动画
this.animator.stop();
// 跳转到指定时间并停止
this.animator.gotoAndStop("Idle", 0, 0.0);
// 跳转到指定帧并停止
this.animator.gotoAndStopByFrame("Attack", 0, 10);
5. 融合过渡
typescript
复制代码
// 融合过渡到 "Run" 状态,过渡时间为 0.3 秒
this.animator.crossFade("Run", 0, 0.0, 0.3);
实用示例
示例1: 角色状态机
typescript
复制代码
@regClass()
export class HeroController extends Laya.Script {
@property(Laya.Animator2D)
public animator: Laya.Animator2D = null;
private speed: number = 0;
onAwake(): void {
// 加载动画控制器
Laya.loader.load("res/hero/HeroController.controller", Laya.Handler.create(this, (controller: Laya.AnimatorController2D) => {
this.animator.controller = controller;
this.playIdle();
}));
}
// 待机状态
public playIdle(): void {
this.animator.setParamsBool("isMoving", false);
this.animator.setParamsBool("isAttacking", false);
}
// 移动状态
public playWalk(): void {
this.animator.setParamsBool("isMoving", true);
this.animator.setParamsNumber("speed", 1.0);
}
public playRun(): void {
this.animator.setParamsBool("isMoving", true);
this.animator.setParamsNumber("speed", 2.0);
}
// 攻击状态
public playAttack(): void {
this.animator.setParamsTrigger("attack");
}
// 死亡状态
public playDie(): void {
this.animator.setParamsBool("isDead", true);
}
// 设置移动速度
public setSpeed(value: number): void {
this.speed = value;
this.animator.setParamsNumber("speed", value);
if (value > 0) {
this.animator.setParamsBool("isMoving", true);
} else {
this.animator.setParamsBool("isMoving", false);
}
}
}
示例2: 动画事件监听
typescript
复制代码
@regClass()
export class AnimationEventListener extends Laya.Script {
@property(Laya.Animator2D)
public animator: Laya.Animator2D = null;
onAwake(): void {
this.setupAnimationEvents();
}
private setupAnimationEvents(): void {
let controller = this.animator.controller;
// 获取动画状态
let attackState = this.getAnimatorState("Attack");
if (attackState) {
// 监听状态进入事件
attackState.on(Laya.AnimatorState2D.EVENT_OnStateEnter, this, () => {
console.log("进入攻击状态");
});
// 监听状态更新事件
attackState.on(Laya.AnimatorState2D.EVENT_OnStateUpdate, this, () => {
console.log("攻击状态更新");
});
// 监听状态退出事件
attackState.on(Laya.AnimatorState2D.EVENT_OnStateExit, this, () => {
console.log("退出攻击状态");
});
}
}
private getAnimatorState(name: string): Laya.AnimatorState2D {
// 通过层获取状态
let layer = this.animator.getControllerLayer(0);
if (layer && layer.states) {
for (let state of layer.states) {
if (state.name === name) {
return state;
}
}
}
return null;
}
}
示例3: 平滑动画过渡
typescript
复制代码
@regClass()
export class SmoothTransition extends Laya.Script {
@property(Laya.Animator2D)
public animator: Laya.Animator2D = null;
// 使用 crossFade 实现平滑过渡
public transitToWalk(): void {
// 在 0.2 秒内过渡到 Walk 状态
this.animator.crossFade("Walk", 0, Number.NEGATIVE_INFINITY, 0.2);
}
public transitToRun(): void {
// 在 0.15 秒内过渡到 Run 状态
this.animator.crossFade("Run", 0, Number.NEGATIVE_INFINITY, 0.15);
}
public transitToAttack(): void {
// 攻击动画快速过渡(0.05 秒)
this.animator.crossFade("Attack", 0, 0.0, 0.05);
}
public transitToIdle(): void {
// 较慢的过渡到待机(0.3 秒)
this.animator.crossFade("Idle", 0, Number.NEGATIVE_INFINITY, 0.3);
}
}
示例4: 多层动画混合
typescript
复制代码
@regClass()
export class LayerAnimation extends Laya.Script {
@property(Laya.Animator2D)
public animator: Laya.Animator2D = null;
onAwake(): void {
this.setupLayers();
}
private setupLayers(): void {
// Base Layer - 控制下半身动画
// 已在控制器中配置
// 添加 UpperBody 层 - 控制上半身动画
let upperLayer = new Laya.AnimatorControllerLayer2D("UpperBody");
upperLayer.blendingMode = Laya.AnimatorControllerLayer2D.BLENDINGMODE_ADDTIVE;
this.animator.addControllerLayer(upperLayer);
// 播放下半身行走动画
this.animator.play("Walk", 0); // Base Layer
// 同时播放上半身攻击动画
this.animator.play("Attack", 1); // UpperBody Layer
}
// 下半身行走,上半身攻击
public walkAndAttack(): void {
this.animator.crossFade("Walk", 0, Number.NEGATIVE_INFINITY, 0.2);
this.animator.crossFade("Attack", 1, 0.0, 0.1);
}
// 下半身站立,上半身瞄准
public idleAndAim(): void {
this.animator.crossFade("Idle", 0, Number.NEGATIVE_INFINITY, 0.2);
this.animator.crossFade("Aim", 1, 0.0, 0.3);
}
}
示例5: 动画速度控制
typescript
复制代码
@regClass()
export class AnimationSpeedControl extends Laya.Script {
@property(Laya.Animator2D)
public animator: Laya.Animator2D = null;
// 慢动作
public setSlowMotion(): void {
this.animator.speed = 0.5; // 0.5 倍速
}
// 正常速度
public setNormalSpeed(): void {
this.animator.speed = 1.0; // 1.0 倍速
}
// 快进
public setFastForward(): void {
this.animator.speed = 2.0; // 2.0 倍速
}
// 暂停动画
public pause(): void {
this.animator.speed = 0;
}
// 根据输入动态调整速度
public updateSpeed(inputValue: number): void {
// inputValue 范围 0~1,映射到速度 0.5~2.0
this.animator.speed = 0.5 + inputValue * 1.5;
}
}
高级技巧
1. 动画参数驱动
typescript
复制代码
// 通过参数控制状态切换
let animator = sprite.getComponent(Laya.Animator2D);
// 设置 Bool 参数
animator.setParamsBool("isGrounded", true);
animator.setParamsBool("hasWeapon", true);
// 设置 Number 参数
animator.setParamsNumber("moveSpeed", 5.0);
animator.setParamsNumber("health", 100);
// 触发 Trigger 参数(一次性触发)
animator.setParamsTrigger("jump");
animator.setParamsTrigger("fire");
2. 获取当前动画状态
typescript
复制代码
// 获取控制器层信息
let layer = animator.getControllerLayer(0);
if (layer) {
let playState = layer.getCurrentPlayState();
if (playState && playState.animatorState) {
console.log("当前状态:", playState.animatorState.name);
}
}
3. 动画状态脚本
typescript
复制代码
// 在 AnimatorState2D 上添加脚本
@regClass()
export class AttackStateScript extends Laya.AnimatorState2DScript {
// 状态进入时调用
onStateEnter(): void {
console.log("攻击开始");
// 创建攻击判定
}
// 状态更新时调用
onStateUpdate(): void {
// 每帧检测
}
// 状态退出时调用
onStateExit(): void {
console.log("攻击结束");
// 清理攻击判定
}
// 状态循环时调用(动画循环一次)
onStateLoop(): void {
console.log("攻击动画循环");
}
}
// 在状态上添加脚本
let attackState = this.getAnimatorState("Attack");
attackState.addScript(AttackStateScript);
4. 条件过渡检测
typescript
复制代码
// 根据条件判断是否可以切换状态
public canTransitToAttack(): boolean {
// 检查当前是否在播放特定动画
let layer = this.animator.getControllerLayer(0);
if (!layer) return false;
let playState = layer.getCurrentPlayState();
if (!playState || !playState.animatorState) return true;
let currentStateName = playState.animatorState.name;
// 如果当前在受伤状态,不能攻击
if (currentStateName === "Hurt") {
return false;
}
// 如果当前在死亡状态,不能攻击
if (currentStateName === "Die") {
return false;
}
return true;
}
5. 动画完成回调
typescript
复制代码
@regClass()
export class AnimationCallback extends Laya.Script {
@property(Laya.Animator2D)
public animator: Laya.Animator2D = null;
private onComplete: Laya.Handler = null;
// 播放一次性动画并监听完成
public playOnce(animName: string, callback: Laya.Handler): void {
this.onComplete = callback;
let state = this.getAnimatorState(animName);
if (state) {
state.once(Laya.AnimatorState2D.EVENT_OnStateExit, this, this.onAnimationComplete);
}
this.animator.play(animName);
}
private onAnimationComplete(): void {
if (this.onComplete) {
this.onComplete.run();
this.onComplete = null;
}
}
}
最佳实践
1. 状态机设计原则
| 原则 |
说明 |
| 单一职责 |
每个状态只负责一个明确的行为 |
| 最小化状态 |
避免创建过多相似的状态 |
| 参数驱动 |
优先使用参数控制,而非硬编码状态切换 |
| 明确过渡 |
每个状态应有清晰的进入/退出条件 |
2. 动画层使用建议
typescript
复制代码
// Layer 0: Base Layer - 基础动画
// 用途:待机、行走、跑步、跳跃等全身动画
animator.play("Walk", 0);
// Layer 1: Upper Body - 上半身动画
// 用途:瞄准、射击、挥砍等上半身动作
animator.play("Attack", 1);
// Layer 2: Face - 面部表情
// 用途:眨眼、说话口型等面部动画
animator.play("Talking", 2);
3. 性能优化
typescript
复制代码
// 1. 减少不必要的状态检查
// ❌ 每帧检查所有参数
onUpdate(): void {
this.animator.setParamsBool("condition1", this.check1());
this.animator.setParamsBool("condition2", this.check2());
this.animator.setParamsBool("condition3", this.check3());
}
// ✅ 只在条件变化时设置
onConditionChanged(): void {
this.animator.setParamsBool("condition1", this.check1());
}
// 2. 合理使用动画缓存
animator.controller.enableCache = true;
// 3. 避免频繁创建/销毁组件
// 预先添加组件,通过 play/stop 控制
4. 参数命名规范
typescript
复制代码
// 使用清晰的前缀区分参数类型
// is/has - Bool 类型
animator.setParamsBool("isGrounded", true);
animator.setParamsBool("hasWeapon", true);
animator.setParamsBool("isDead", false);
// 数值 - Number 类型
animator.setParamsNumber("speed", 5.0);
animator.setParamsNumber("health", 100);
animator.setParamsNumber("direction", 1);
// 动作 - Trigger 类型
animator.setParamsTrigger("jump");
animator.setParamsTrigger("attack");
animator.setParamsTrigger("interact");
5. 调试技巧
typescript
复制代码
// 查看当前状态
let layer = animator.getControllerLayer(0);
if (layer) {
let playState = layer.getCurrentPlayState();
if (playState && playState.animatorState) {
console.log("当前状态:", playState.animatorState.name);
}
}
// 查看所有参数
let params = animator.parameters;
for (let key in params) {
console.log(key, ":", params[key]);
}
6. 常见状态机结构
复制代码
HeroAnimatorController
├── Base Layer (Layer 0)
│ ├── Idle (默认状态)
│ ├── Walk (条件: isMoving && speed < 2)
│ ├── Run (条件: isMoving && speed >= 2)
│ ├── Jump (条件: trigger "jump")
│ ├── Fall (条件: !isGrounded)
│ ├── Attack (条件: trigger "attack")
│ ├── Hurt (条件: trigger "hurt")
│ └── Die (条件: isDead)
├── Upper Body Layer (Layer 1)
│ ├── None (默认)
│ ├── Aim (条件: isAiming)
│ └── Shoot (条件: trigger "shoot")
└── Face Layer (Layer 2)
├── Neutral (默认)
├── Happy (条件: mood > 0.5)
└── Angry (条件: mood < -0.5)
注意事项
- 资源预加载 :使用
AnimatorController2D 前需先加载 .controller 资源
- 参数类型匹配:设置参数时确保类型与控制器定义一致(Bool/Number/Trigger)
- 归一化时间 :
normalizedTime 取值范围为 0 到 1,Number.NEGATIVE_INFINITY 表示从头播放
- 层索引 :层索引从
0 开始,确保添加层后再访问
- Trigger 特性:Trigger 参数触发后会被自动重置,适合一次性事件
- 性能考虑:过多动画层会影响性能,建议不超过 3 层
- 状态脚本 :使用
AnimatorState2DScript 实现状态内逻辑,避免在主逻辑中频繁查询
相关文档