fix:编辑器文件导入. add:example,runtime
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { Animation, AnimationState, _decorator, __private } from "cc";
|
||||
import AnimatorBase, { AnimationPlayer } from "./core/AnimatorBase";
|
||||
import AnimatorStateLogic from "./core/AnimatorStateLogic";
|
||||
|
||||
const { ccclass, property, requireComponent, disallowMultiple } = _decorator;
|
||||
|
||||
/**
|
||||
* Cocos Animation状态机组件
|
||||
*/
|
||||
@ccclass
|
||||
@disallowMultiple
|
||||
@requireComponent(Animation)
|
||||
export default class AnimatorAnimation extends AnimatorBase {
|
||||
/** Animation组件 */
|
||||
private _animation: Animation = null!;
|
||||
/** 当前的动画实例 */
|
||||
private _animState: AnimationState = null!;
|
||||
/** 记录初始的wrapmode */
|
||||
private _wrapModeMap: Map<AnimationState, number> = new Map();
|
||||
|
||||
protected start() {
|
||||
if (!this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this._animation = this.getComponent(Animation)!;
|
||||
this._animation.on(Animation.EventType.FINISHED, this.onAnimFinished, this);
|
||||
this._animation.on(Animation.EventType.LASTFRAME, this.onAnimFinished, this);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动初始化状态机,可传入0-3个参数,类型如下
|
||||
* - onStateChangeCall 状态切换时的回调
|
||||
* - stateLogicMap 各个状态逻辑控制
|
||||
* - animationPlayer 自定义动画控制
|
||||
* @override
|
||||
*/
|
||||
public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
if (this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this.initArgs(...args);
|
||||
|
||||
this._animation = this.getComponent(Animation)!;
|
||||
this._animation.on(Animation.EventType.FINISHED, this.onAnimFinished, this);
|
||||
this._animation.on(Animation.EventType.LASTFRAME, this.onAnimFinished, this);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画
|
||||
* @override
|
||||
* @param animName 动画名
|
||||
* @param loop 是否循环播放
|
||||
*/
|
||||
protected playAnimation(animName: string, loop: boolean) {
|
||||
if (!animName) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._animation.play(animName);
|
||||
this._animState = this._animation.getState(animName);
|
||||
if (!this._animState) {
|
||||
return;
|
||||
}
|
||||
if (!this._wrapModeMap.has(this._animState)) {
|
||||
this._wrapModeMap.set(this._animState, this._animState.wrapMode);
|
||||
}
|
||||
this._animState.wrapMode = loop ? 2 : this._wrapModeMap.get(this._animState)!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放动画播放速率
|
||||
* @override
|
||||
* @param scale 缩放倍率
|
||||
*/
|
||||
protected scaleTime(scale: number) {
|
||||
if (this._animState) {
|
||||
this._animState.speed = scale;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "616e8140-9f63-4ebd-bab8-59a7061c9bcf",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
import { _decorator } from "cc";
|
||||
import AnimatorBase, { AnimationPlayer } from "./core/AnimatorBase";
|
||||
import AnimatorStateLogic from "./core/AnimatorStateLogic";
|
||||
|
||||
const { ccclass, property, requireComponent, disallowMultiple } = _decorator;
|
||||
|
||||
/**
|
||||
* 自定义动画控制的状态机组件
|
||||
*/
|
||||
@ccclass
|
||||
@disallowMultiple
|
||||
export default class AnimatorCustomization extends AnimatorBase {
|
||||
/** 此组件必须主动调用onInit初始化 */
|
||||
@property({ override: true, visible: false })
|
||||
protected PlayOnStart: boolean = false;
|
||||
|
||||
/**
|
||||
* 手动初始化状态机,可传入0-3个参数,类型如下
|
||||
* - onStateChangeCall 状态切换时的回调
|
||||
* - stateLogicMap 各个状态逻辑控制
|
||||
* - animationPlayer 自定义动画控制
|
||||
* @override
|
||||
*/
|
||||
public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
if (this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this.initArgs(...args);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画
|
||||
* @override
|
||||
* @param animName 动画名
|
||||
* @param loop 是否循环播放
|
||||
*/
|
||||
protected playAnimation(animName: string, loop: boolean) {
|
||||
if (this._animationPlayer && animName) {
|
||||
this._animationPlayer.playAnimation(animName, loop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放动画播放速率
|
||||
* @override
|
||||
* @param scale 缩放倍率
|
||||
*/
|
||||
protected scaleTime(scale: number) {
|
||||
if (this._animationPlayer) {
|
||||
this._animationPlayer.scaleTime(scale);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "3d89e3aa-c3f7-40bd-a114-156509c971eb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
import { dragonBones, _decorator } from "cc";
|
||||
import AnimatorBase, { AnimationPlayer } from "./core/AnimatorBase";
|
||||
import AnimatorStateLogic from "./core/AnimatorStateLogic";
|
||||
|
||||
const { ccclass, property, requireComponent, disallowMultiple } = _decorator;
|
||||
|
||||
/**
|
||||
* DragonBones状态机组件
|
||||
*/
|
||||
@ccclass
|
||||
@disallowMultiple
|
||||
@requireComponent(dragonBones.ArmatureDisplay)
|
||||
export default class AnimatorDragonBones extends AnimatorBase {
|
||||
/** DragonBones组件 */
|
||||
private _dragonBones: dragonBones.ArmatureDisplay = null!;
|
||||
|
||||
protected start() {
|
||||
if (!this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this._dragonBones = this.getComponent(dragonBones.ArmatureDisplay)!;
|
||||
this._dragonBones.addEventListener('complete', this.onAnimFinished, this);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动初始化状态机,可传入0-3个参数,类型如下
|
||||
* - onStateChangeCall 状态切换时的回调
|
||||
* - stateLogicMap 各个状态逻辑控制
|
||||
* - animationPlayer 自定义动画控制
|
||||
* @override
|
||||
*/
|
||||
public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
if (this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this.initArgs(...args);
|
||||
|
||||
this._dragonBones = this.getComponent(dragonBones.ArmatureDisplay)!;
|
||||
this._dragonBones.addEventListener('complete', this.onAnimFinished, this);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画
|
||||
* @override
|
||||
* @param animName 动画名
|
||||
* @param loop 是否循环播放
|
||||
*/
|
||||
protected playAnimation(animName: string, loop: boolean) {
|
||||
if (animName)
|
||||
this._dragonBones.playAnimation(animName, loop ? 0 : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放动画播放速率
|
||||
* @override
|
||||
* @param scale 缩放倍率
|
||||
*/
|
||||
protected scaleTime(scale: number) {
|
||||
if (scale > 0)
|
||||
this._dragonBones.timeScale = scale;
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e07167b2-0e81-4d90-a411-f178e55210fe",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
122
examples/example3-0rc/assets/script/animator/AnimatorSpine.ts
Normal file
122
examples/example3-0rc/assets/script/animator/AnimatorSpine.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { sp, _decorator } from "cc";
|
||||
import AnimatorSpineSecondary from "./AnimatorSpineSecondary";
|
||||
import AnimatorBase, { AnimationPlayer } from "./core/AnimatorBase";
|
||||
import AnimatorStateLogic from "./core/AnimatorStateLogic";
|
||||
|
||||
const { ccclass, property, requireComponent, disallowMultiple } = _decorator;
|
||||
|
||||
/**
|
||||
* Spine状态机组件(主状态机),trackIndex为0
|
||||
*/
|
||||
@ccclass
|
||||
@disallowMultiple
|
||||
@requireComponent(sp.Skeleton)
|
||||
export default class AnimatorSpine extends AnimatorBase {
|
||||
/** spine组件 */
|
||||
private _spine: sp.Skeleton = null!;
|
||||
/** 动画完成的回调 */
|
||||
private _completeListenerMap: Map<(entry?: any) => void, any> = new Map();
|
||||
/** 次状态机注册的回调 */
|
||||
private _secondaryListenerMap: Map<(entry?: any) => void, AnimatorSpineSecondary> = new Map();
|
||||
|
||||
protected start() {
|
||||
if (!this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this._spine = this.getComponent(sp.Skeleton)!;
|
||||
this._spine.setCompleteListener(this.onSpineComplete.bind(this));
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动初始化状态机,可传入0-3个参数,类型如下
|
||||
* - onStateChangeCall 状态切换时的回调
|
||||
* - stateLogicMap 各个状态逻辑控制
|
||||
* - animationPlayer 自定义动画控制
|
||||
* @override
|
||||
*/
|
||||
public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
if (this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this.initArgs(...args);
|
||||
|
||||
this._spine = this.getComponent(sp.Skeleton)!;
|
||||
this._spine.setCompleteListener(this.onSpineComplete.bind(this));
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
private onSpineComplete(entry: any) {
|
||||
entry.trackIndex === 0 && this.onAnimFinished();
|
||||
this._completeListenerMap.forEach((target, cb) => { target ? cb.call(target, entry) : cb(entry); });
|
||||
this._secondaryListenerMap.forEach((target, cb) => { entry.trackIndex === target.TrackIndex && cb.call(target, entry); });
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画
|
||||
* @override
|
||||
* @param animName 动画名
|
||||
* @param loop 是否循环播放
|
||||
*/
|
||||
protected playAnimation(animName: string, loop: boolean) {
|
||||
if (animName) {
|
||||
this._spine.setAnimation(0, animName, loop);
|
||||
} else {
|
||||
this._spine.clearTrack(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放动画播放速率
|
||||
* @override
|
||||
* @param scale 缩放倍率
|
||||
*/
|
||||
protected scaleTime(scale: number) {
|
||||
if (scale > 0)
|
||||
this._spine.timeScale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册次状态机动画结束的回调(状态机内部方法,不能由外部直接调用)
|
||||
*/
|
||||
public addSecondaryListener(cb: (entry?: any) => void, target: AnimatorSpineSecondary) {
|
||||
this._secondaryListenerMap.set(cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册动画完成时的监听
|
||||
* @param cb 回调
|
||||
* @param target 调用回调的this对象
|
||||
*/
|
||||
public addCompleteListener(cb: (entry?: any) => void, target: any = null) {
|
||||
if (this._completeListenerMap.has(cb)) {
|
||||
return;
|
||||
}
|
||||
this._completeListenerMap.set(cb, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销动画完成的监听
|
||||
* @param cb 回调
|
||||
*/
|
||||
public removeCompleteListener(cb: (entry?: any) => void) {
|
||||
this._completeListenerMap.delete(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空动画完成的监听
|
||||
*/
|
||||
public clearCompleteListener() {
|
||||
this._completeListenerMap.clear;
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "81c24459-f5fb-4bda-bba8-1a2696e1ff51",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
import { sp, _decorator } from "cc";
|
||||
import AnimatorSpine from "./AnimatorSpine";
|
||||
import AnimatorBase, { AnimationPlayer } from "./core/AnimatorBase";
|
||||
import AnimatorStateLogic from "./core/AnimatorStateLogic";
|
||||
|
||||
const { ccclass, property, requireComponent } = _decorator;
|
||||
|
||||
/**
|
||||
* Spine状态机组件(次状态机),同一节点可添加多个,用于在不同track中播放动画,trackIndex必须大于0
|
||||
*/
|
||||
@ccclass
|
||||
@requireComponent(sp.Skeleton)
|
||||
export default class AnimatorSpineSecondary extends AnimatorBase {
|
||||
@property({ tooltip: '动画播放的trackIndex,必须大于0' }) TrackIndex: number = 1;
|
||||
|
||||
/** 主状态机 */
|
||||
private _main: AnimatorSpine = null!;
|
||||
/** spine组件 */
|
||||
private _spine: sp.Skeleton = null!;
|
||||
|
||||
protected start() {
|
||||
if (!this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this._spine = this.getComponent(sp.Skeleton)!;
|
||||
this._main = this.getComponent(AnimatorSpine)!;
|
||||
this._main.addSecondaryListener(this.onAnimFinished, this);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动初始化状态机,可传入0-3个参数,类型如下
|
||||
* - onStateChangeCall 状态切换时的回调
|
||||
* - stateLogicMap 各个状态逻辑控制
|
||||
* - animationPlayer 自定义动画控制
|
||||
* @override
|
||||
*/
|
||||
public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
if (this.PlayOnStart || this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._hasInit = true;
|
||||
|
||||
this.initArgs(...args);
|
||||
|
||||
this._spine = this.getComponent(sp.Skeleton)!;
|
||||
this._main = this.getComponent(AnimatorSpine)!;
|
||||
this._main.addSecondaryListener(this.onAnimFinished, this);
|
||||
|
||||
if (this.AssetRawUrl !== null) {
|
||||
this.initJson(this.AssetRawUrl.json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画
|
||||
* @override
|
||||
* @param animName 动画名
|
||||
* @param loop 是否循环播放
|
||||
*/
|
||||
protected playAnimation(animName: string, loop: boolean) {
|
||||
if (animName) {
|
||||
this._spine.setAnimation(this.TrackIndex, animName, loop);
|
||||
} else {
|
||||
this._spine.clearTrack(this.TrackIndex);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e0a4140f-508b-46fe-812d-a105d323a675",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
12
examples/example3-0rc/assets/script/animator/core.meta
Normal file
12
examples/example3-0rc/assets/script/animator/core.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "44830f79-5a68-4423-92c7-e2458a1909b3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,228 @@
|
||||
import { Component, JsonAsset, _decorator } from 'cc';
|
||||
import AnimatorController from "./AnimatorController";
|
||||
import AnimatorState from "./AnimatorState";
|
||||
import AnimatorStateLogic from "./AnimatorStateLogic";
|
||||
|
||||
const { ccclass, property, executionOrder } = _decorator;
|
||||
|
||||
/**
|
||||
* 自定义控制动画播放的接口
|
||||
*/
|
||||
export interface AnimationPlayer {
|
||||
/** 设置动画播放结束的回调 */
|
||||
setFinishedCallback(callback: () => void, target: any): void;
|
||||
/** 播放动画 */
|
||||
playAnimation(animName: string, loop: boolean): void;
|
||||
/** 缩放动画播放速率 */
|
||||
scaleTime(scale: number): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态机组件基类 优先执行生命周期
|
||||
*/
|
||||
@ccclass
|
||||
@executionOrder(-1000)
|
||||
export default class AnimatorBase extends Component {
|
||||
@property({ type: JsonAsset, tooltip: '状态机json文件' })
|
||||
protected AssetRawUrl: JsonAsset = null!;
|
||||
|
||||
@property({ tooltip: '是否在start中自动启动状态机' })
|
||||
protected PlayOnStart: boolean = true;
|
||||
|
||||
@property({ tooltip: '是否在update中自动触发状态机逻辑更新' })
|
||||
protected AutoUpdate: boolean = true;
|
||||
|
||||
/** 是否初始化 */
|
||||
protected _hasInit: boolean = false;
|
||||
/** 状态机控制 */
|
||||
protected _ac: AnimatorController = null!;
|
||||
|
||||
/** 各个状态逻辑控制,key为状态名 */
|
||||
protected _stateLogicMap: Map<string, AnimatorStateLogic> = null!;
|
||||
/** 状态切换时的回调 */
|
||||
protected _onStateChangeCall: (fromState: string, toState: string) => void = null!;
|
||||
/** 自定义的动画播放控制器 */
|
||||
protected _animationPlayer: AnimationPlayer = null!;
|
||||
|
||||
/** 当前状态名 */
|
||||
public get curStateName(): string {
|
||||
return this._ac.curState.name;
|
||||
}
|
||||
/** 当前动画名 */
|
||||
public get curStateMotion(): string {
|
||||
return this._ac.curState.motion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动初始化状态机,可传入0-3个参数,类型如下
|
||||
* - onStateChangeCall 状态切换时的回调
|
||||
* - stateLogicMap 各个状态逻辑控制
|
||||
* - animationPlayer 自定义动画控制
|
||||
* @virtual
|
||||
*/
|
||||
public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理初始化参数
|
||||
*/
|
||||
protected initArgs(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
|
||||
args.forEach((arg) => {
|
||||
if (!arg) {
|
||||
return;
|
||||
}
|
||||
if (typeof arg === 'function') {
|
||||
this._onStateChangeCall = arg;
|
||||
} else if (typeof arg === 'object') {
|
||||
if (arg instanceof Map) {
|
||||
this._stateLogicMap = arg;
|
||||
} else {
|
||||
this._animationPlayer = arg;
|
||||
this._animationPlayer.setFinishedCallback(this.onAnimFinished, this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateAnimator() {
|
||||
// 混合当前动画播放速度
|
||||
let playSpeed = this._ac.curState.speed;
|
||||
if (this._ac.curState.multi) {
|
||||
playSpeed *= this._ac.params.getNumber(this._ac.curState.multi) || 1;
|
||||
}
|
||||
this.scaleTime(playSpeed);
|
||||
|
||||
// 更新AnimatorStateLogic
|
||||
if (this._stateLogicMap) {
|
||||
let curLogic = this._stateLogicMap.get(this._ac.curState.name);
|
||||
curLogic && curLogic.onUpdate();
|
||||
}
|
||||
|
||||
// 更新状态机逻辑
|
||||
this._ac.updateAnimator();
|
||||
}
|
||||
|
||||
protected update() {
|
||||
if (this._hasInit && this.AutoUpdate) {
|
||||
this.updateAnimator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动调用更新
|
||||
*/
|
||||
public manualUpdate() {
|
||||
if (this._hasInit && !this.AutoUpdate) {
|
||||
this.updateAnimator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析状态机json文件
|
||||
*/
|
||||
protected initJson(json: any) {
|
||||
this._ac = new AnimatorController(this, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 动画结束的回调
|
||||
*/
|
||||
protected onAnimFinished() {
|
||||
this._ac.onAnimationComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放动画
|
||||
* @virtual
|
||||
* @param animName 动画名
|
||||
* @param loop 是否循环播放
|
||||
*/
|
||||
protected playAnimation(animName: string, loop: boolean) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放动画播放速率
|
||||
* @virtual
|
||||
* @param scale 缩放倍率
|
||||
*/
|
||||
protected scaleTime(scale: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态切换时的逻辑(状态机内部方法,不能由外部直接调用)
|
||||
*/
|
||||
public onStateChange(fromState: AnimatorState, toState: AnimatorState) {
|
||||
this.playAnimation(toState.motion, toState.loop);
|
||||
|
||||
let fromStateName = fromState ? fromState.name : '';
|
||||
|
||||
if (this._stateLogicMap) {
|
||||
let fromLogic = this._stateLogicMap.get(fromStateName);
|
||||
fromLogic && fromLogic.onExit();
|
||||
let toLogic = this._stateLogicMap.get(toState.name);
|
||||
toLogic && toLogic.onEntry();
|
||||
}
|
||||
|
||||
this._onStateChangeCall && this._onStateChangeCall(fromStateName, toState.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置boolean类型参数的值
|
||||
*/
|
||||
public setBool(key: string, value: boolean) {
|
||||
this._ac.params.setBool(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取boolean类型参数的值
|
||||
*/
|
||||
public getBool(key: string): boolean {
|
||||
return this._ac.params.getBool(key) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置number类型参数的值
|
||||
*/
|
||||
public setNumber(key: string, value: number) {
|
||||
this._ac.params.setNumber(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取number类型参数的值
|
||||
*/
|
||||
public getNumber(key: string): number {
|
||||
return this._ac.params.getNumber(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置trigger类型参数的值
|
||||
*/
|
||||
public setTrigger(key: string) {
|
||||
this._ac.params.setTrigger(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置trigger类型参数的值
|
||||
*/
|
||||
public resetTrigger(key: string) {
|
||||
this._ac.params.resetTrigger(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置autoTrigger类型参数的值(autoTrigger类型参数不需要主动reset,每次状态机更新结束后会自动reset)
|
||||
*/
|
||||
public autoTrigger(key: string) {
|
||||
this._ac.params.autoTrigger(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无视条件直接跳转状态
|
||||
* @param 状态名
|
||||
*/
|
||||
public play(stateName: string) {
|
||||
if (!this._hasInit) {
|
||||
return;
|
||||
}
|
||||
this._ac.play(stateName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b8f391f1-5a8f-4068-9247-c3c63bd87f6a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
import { error } from "cc";
|
||||
import AnimatorController from "./AnimatorController";
|
||||
|
||||
/** 参数类型 */
|
||||
export enum ParamType {
|
||||
COMPLETE = 0,
|
||||
BOOLEAN = 1,
|
||||
NUMBER = 2,
|
||||
TRIGGER = 3,
|
||||
AUTO_TRIGGER = 4
|
||||
}
|
||||
|
||||
/** 逻辑类型 */
|
||||
export enum LogicType {
|
||||
EQUAL = 0,
|
||||
NOTEQUAL = 1,
|
||||
GREATER = 2,
|
||||
LESS = 3,
|
||||
GREATER_EQUAL = 4,
|
||||
LESS_EQUAL = 5
|
||||
}
|
||||
|
||||
/**
|
||||
* 单项条件
|
||||
*/
|
||||
export default class AnimatorCondition {
|
||||
private _ac: AnimatorController;
|
||||
/** 此条件对应的参数名 */
|
||||
private _param: string = "";
|
||||
/** 此条件对应的值 */
|
||||
private _value: number = 0;
|
||||
/** 此条件与值比较的逻辑 */
|
||||
private _logic: LogicType = LogicType.EQUAL;
|
||||
|
||||
constructor(data: any, ac: AnimatorController) {
|
||||
this._ac = ac;
|
||||
this._param = data.param;
|
||||
this._value = data.value;
|
||||
this._logic = data.logic;
|
||||
}
|
||||
|
||||
public getParamName() {
|
||||
return this._param;
|
||||
}
|
||||
|
||||
public getParamType(): ParamType {
|
||||
return this._ac.params.getParamType(this._param);
|
||||
}
|
||||
|
||||
/** 判断此条件是否满足 */
|
||||
public check(): boolean {
|
||||
let type: ParamType = this.getParamType();
|
||||
if (type === ParamType.BOOLEAN) {
|
||||
return this._ac.params.getBool(this._param) === this._value;
|
||||
} else if (type === ParamType.NUMBER) {
|
||||
let value: number = this._ac.params.getNumber(this._param);
|
||||
switch (this._logic) {
|
||||
case LogicType.EQUAL:
|
||||
return value === this._value;
|
||||
case LogicType.NOTEQUAL:
|
||||
return value !== this._value;
|
||||
case LogicType.GREATER:
|
||||
return value > this._value;
|
||||
case LogicType.LESS:
|
||||
return value < this._value;
|
||||
case LogicType.GREATER_EQUAL:
|
||||
return value >= this._value;
|
||||
case LogicType.LESS_EQUAL:
|
||||
return value <= this._value;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (type === ParamType.AUTO_TRIGGER) {
|
||||
return this._ac.params.getAutoTrigger(this._param) !== 0;
|
||||
} else if (type === ParamType.TRIGGER) {
|
||||
return this._ac.params.getTrigger(this._param) !== 0;
|
||||
} else {
|
||||
error(`[AnimatorCondition.check] 错误的type: ${type}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "257d2be5-e198-4f7c-a90e-8ec11702907f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
import { error } from "cc";
|
||||
import AnimatorBase from "./AnimatorBase";
|
||||
import AnimatorParams from "./AnimatorParams";
|
||||
import AnimatorState from "./AnimatorState";
|
||||
|
||||
/**
|
||||
* 状态机控制类
|
||||
*/
|
||||
export default class AnimatorController {
|
||||
private _jsonData: any = null;
|
||||
private _animator: AnimatorBase = null!;
|
||||
|
||||
private _params: AnimatorParams = null!;
|
||||
private _states: Map<string, AnimatorState> = null!;
|
||||
private _anyState: AnimatorState = null!;
|
||||
private _curState: AnimatorState = null!;
|
||||
|
||||
/** 状态切换次数 */
|
||||
private _changeCount: number = 0;
|
||||
/** 对应animComplete的状态 */
|
||||
public animCompleteState: AnimatorState = null!;
|
||||
/** 动画播放完毕的标记 */
|
||||
public animComplete: boolean = false;
|
||||
/** 当前运行的状态 */
|
||||
public get curState(): AnimatorState { return this._curState; }
|
||||
public get params(): AnimatorParams { return this._params; }
|
||||
|
||||
constructor(player: AnimatorBase, json: any) {
|
||||
this._animator = player;
|
||||
this._jsonData = json;
|
||||
this._states = new Map<string, AnimatorState>();
|
||||
this._params = new AnimatorParams(json.parameters);
|
||||
this.init(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化状态机所有动画状态
|
||||
*/
|
||||
private init(json: any) {
|
||||
if (json.states.length <= 0) {
|
||||
error(`[AnimatorController.init] 状态机json错误`);
|
||||
return;
|
||||
}
|
||||
|
||||
let defaultState: string = json.defaultState;
|
||||
this._anyState = new AnimatorState(json.anyState, this);
|
||||
for (let i = 0; i < json.states.length; i++) {
|
||||
let state: AnimatorState = new AnimatorState(json.states[i], this);
|
||||
this._states.set(state.name, state);
|
||||
}
|
||||
this.changeState(defaultState);
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
this._curState.checkAndTrans();
|
||||
if (this._curState !== this._anyState && this._anyState !== null) {
|
||||
this._anyState.checkAndTrans();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态机逻辑
|
||||
*/
|
||||
public updateAnimator() {
|
||||
// 重置计数
|
||||
this._changeCount = 0;
|
||||
|
||||
this.updateState();
|
||||
|
||||
// 重置动画完成标记
|
||||
if (this.animComplete && this.animCompleteState.loop) {
|
||||
this.animComplete = false;
|
||||
}
|
||||
// 重置autoTrigger
|
||||
this.params.resetAllAutoTrigger();
|
||||
}
|
||||
|
||||
public onAnimationComplete() {
|
||||
this.animComplete = true;
|
||||
this.animCompleteState = this._curState;
|
||||
// cc.log(`animation complete: ${this._curState.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无视条件直接跳转状态
|
||||
* @param 状态名
|
||||
*/
|
||||
public play(stateName: string) {
|
||||
if (!this._states.has(stateName) || this._curState.name === stateName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置动画完成标记
|
||||
this.animComplete = false;
|
||||
this.changeState(stateName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换动画状态
|
||||
*/
|
||||
public changeState(stateName: string) {
|
||||
this._changeCount++;
|
||||
if (this._changeCount > 1000) {
|
||||
error('[AnimatorController.changeState] error: 状态切换递归调用超过1000次,transition设置可能出错!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._states.has(stateName) && (this._curState === null || this._curState.name !== stateName)) {
|
||||
let oldState = this._curState;
|
||||
this._curState = this._states.get(stateName)!;
|
||||
|
||||
this._animator.onStateChange(oldState, this._curState);
|
||||
|
||||
this.updateState();
|
||||
} else {
|
||||
error(`[AnimatorController.changeState] error state: ${stateName}`);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "111f60e4-58f9-4212-b578-090e6fc32d85",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
import { ParamType } from "./AnimatorCondition";
|
||||
|
||||
/**
|
||||
* 参数结构
|
||||
*/
|
||||
interface Param {
|
||||
type: ParamType;
|
||||
value: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态机参数
|
||||
*/
|
||||
export default class AnimatorParams {
|
||||
private _paramMap: Map<string, Param> = new Map();
|
||||
|
||||
constructor(dataArr: any[]) {
|
||||
dataArr.forEach((data: any) => {
|
||||
let param: Param = {
|
||||
type: data.type,
|
||||
value: data.init
|
||||
};
|
||||
this._paramMap.set(data.param, param);
|
||||
});
|
||||
}
|
||||
|
||||
public getParamType(key: string): ParamType {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param) {
|
||||
return param.type;
|
||||
} else {
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
|
||||
public setNumber(key: string, value: number) {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.NUMBER) {
|
||||
param.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public setBool(key: string, value: boolean) {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.BOOLEAN) {
|
||||
param.value = value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public setTrigger(key: string) {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.TRIGGER) {
|
||||
param.value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public resetTrigger(key: string) {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.TRIGGER) {
|
||||
param.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public autoTrigger(key: string) {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.AUTO_TRIGGER) {
|
||||
param.value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public resetAutoTrigger(key: string) {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.AUTO_TRIGGER) {
|
||||
param.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public resetAllAutoTrigger() {
|
||||
this._paramMap.forEach((param: Param, key: string) => {
|
||||
if (param.type === ParamType.AUTO_TRIGGER) {
|
||||
param.value = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getNumber(key: string): number {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.NUMBER) {
|
||||
return param.value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public getBool(key: string): number {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.BOOLEAN) {
|
||||
return param.value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public getTrigger(key: string): number {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.TRIGGER) {
|
||||
return param.value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public getAutoTrigger(key: string): number {
|
||||
let param: Param = this._paramMap.get(key)!;
|
||||
if (param && param.type === ParamType.AUTO_TRIGGER) {
|
||||
return param.value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c3c16d83-8df0-40ac-ad90-3b3f6ee65355",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import AnimatorController from "./AnimatorController";
|
||||
import AnimatorTransition from "./AnimatorTransition";
|
||||
|
||||
/**
|
||||
* 状态管理类
|
||||
*/
|
||||
export default class AnimatorState {
|
||||
private _name: string = "";
|
||||
private _motion: string = "";
|
||||
private _loop: boolean = false;
|
||||
private _speed: number = 1;
|
||||
private _multi: string = "";
|
||||
|
||||
private _transitions: AnimatorTransition[] = [];
|
||||
private _ac: AnimatorController = null!;
|
||||
|
||||
/** 状态名 */
|
||||
public get name() { return this._name; }
|
||||
/** 动画名 */
|
||||
public get motion() { return this._motion; }
|
||||
/** 动画是否循环播放 */
|
||||
public get loop() { return this._loop; }
|
||||
/** 动画播放速度 */
|
||||
public get speed() { return this._speed; }
|
||||
/** 动画播放速度的混合参数 */
|
||||
public get multi() { return this._multi; }
|
||||
|
||||
constructor(data: any, ac: AnimatorController) {
|
||||
this._name = data.state;
|
||||
this._motion = data.motion || '';
|
||||
this._loop = data.loop || false;
|
||||
this._speed = data.speed || 1;
|
||||
this._multi = data.multiplier || '';
|
||||
|
||||
this._ac = ac;
|
||||
|
||||
for (let i = 0; i < data.transitions.length; i++) {
|
||||
let transition: AnimatorTransition = new AnimatorTransition(data.transitions[i], ac);
|
||||
transition.isValid() && this._transitions.push(transition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断各个分支是否满足条件,满足则转换状态
|
||||
*/
|
||||
public checkAndTrans() {
|
||||
for (let i = 0; i < this._transitions.length; i++) {
|
||||
let transition: AnimatorTransition = this._transitions[i];
|
||||
if (transition && transition.check()) {
|
||||
transition.doTrans();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e2d00cfd-edf4-4049-be71-56efdad8caa3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 状态逻辑基类
|
||||
*/
|
||||
export default class AnimatorStateLogic {
|
||||
/**
|
||||
* 进入状态时调用
|
||||
* @virtual
|
||||
*/
|
||||
public onEntry() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次状态机逻辑更新时调用
|
||||
* @virtual
|
||||
*/
|
||||
public onUpdate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 离开状态时调用
|
||||
* @virtual
|
||||
*/
|
||||
public onExit() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c621576a-d46f-4e27-abc3-4e5d0648bd3b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
import AnimatorCondition, { ParamType } from "./AnimatorCondition";
|
||||
import AnimatorController from "./AnimatorController";
|
||||
|
||||
/**
|
||||
* 状态过渡类
|
||||
*/
|
||||
export default class AnimatorTransition {
|
||||
private _toStateName: string = '';
|
||||
private _hasExitTime: boolean = false;
|
||||
private _conditions: AnimatorCondition[] = [];
|
||||
private _ac: AnimatorController = null!;
|
||||
|
||||
constructor(data: any, ac: AnimatorController) {
|
||||
this._toStateName = data.toState;
|
||||
this._hasExitTime = data.hasExitTime;
|
||||
this._ac = ac;
|
||||
for (let i = 0; i < data.conditions.length; i++) {
|
||||
let condition: AnimatorCondition = new AnimatorCondition(data.conditions[i], ac);
|
||||
this._conditions.push(condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回该transition是否有效,当未勾选hasExitTime以及没有添加任何condition时此transition无效并忽略
|
||||
*/
|
||||
public isValid(): boolean {
|
||||
return this._hasExitTime || this._conditions.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否满足所有转换条件
|
||||
*/
|
||||
public check(): boolean {
|
||||
if (this._toStateName === this._ac.curState.name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._hasExitTime && (this._ac.curState !== this._ac.animCompleteState || !this._ac.animComplete)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._conditions.length; i++) {
|
||||
if (!this._conditions[i].check()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换状态
|
||||
*/
|
||||
public doTrans() {
|
||||
// 满足条件时重置动画播完标记
|
||||
if (this._hasExitTime) {
|
||||
this._ac.animComplete = false;
|
||||
}
|
||||
// 满足状态转换条件时重置trigger和autoTrigger
|
||||
for (let i = 0; i < this._conditions.length; i++) {
|
||||
let type = this._conditions[i].getParamType();
|
||||
let name = this._conditions[i].getParamName();
|
||||
if (type === ParamType.TRIGGER) {
|
||||
this._ac.params.resetTrigger(name);
|
||||
} else if (type === ParamType.AUTO_TRIGGER) {
|
||||
this._ac.params.resetAutoTrigger(name);
|
||||
}
|
||||
}
|
||||
|
||||
this._ac.changeState(this._toStateName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.21",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "be88dd41-10ce-41df-bc2d-f675b373af64",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
Reference in New Issue
Block a user