欢迎喜欢或者从事CocosCreator开发的小伙伴请加入我的大家庭CocosCreator游戏开发Q群:26855530
在CocosCreator的官方文档中,提供播放帧动画的组件是Animation,在一般的情况下是可以满足大部分的游戏需求的.但是有时,在播放动画的时候,比如人物的行走的动作时,一旦方向改变,或者动作改动,该组件便会重第1帧执行你指定的动作,这个并不符合我们的需求.
我们的需求是:当前播放到第n帧时,切换下个动作应该从第n+1帧续播.
于是乎就有了今天这篇博客.在浏览官方的源码之后,再结合我自身的需求,我自己写了一个组件,代码如下:
import DynamicAssetManager from "../manager/DynamicAssetManager";
const {ccclass} = cc._decorator;
/**
* 可续播帧动画组件
*/
@ccclass
export default class FrameAnimation extends cc.Component {
@property(cc.Node)
frameNode: cc.Node = null;
private _onPlay: Function;
private _onFinish: Function;
//增加对象池引用
reuse() {
this.frameNode.getComponent(cc.Sprite).spriteFrame = null;
this.frameNode.getComponent(cc.Animation).stop();
}
unuse() {
this.frameNode.getComponent(cc.Sprite).spriteFrame = null;
this.frameNode.getComponent(cc.Animation).stop();
DynamicAssetManager.pullAsset(this.node, this.constructor.name);
}
//增加开始动画和结束动画的函数回调
onEnable() {
let animation = this.frameNode.getComponent(cc.Animation);
animation.on("play", this.onEvent, this);
animation.on("finished", this.onEvent, this);
}
/**
* 入口函数
* @param value 动画帧集路径
* @param speed 动画播放速度
* @param onPlay 开始播放回调函数
* @param onFinish 结束播放回调函数
*/
public showEffect(value: string, speed: number, onPlay?: Function, onFinish?: Function) {
if (!this.frameNode.active) {
SysLog.error("showEffect:????????????????????????");
return;
}
if (onPlay && "function" == typeof (onPlay)) {
this._onPlay = onPlay;
}
if (onFinish && "function" == typeof (onFinish)) {
this._onFinish = onFinish;
}
this.playAni(this.frameNode, value, speed);
}
private playAni(node: cc.Node, url: string, speed: number) {
if (!node) {
SysLog.debug("必须有结点才能播放动画", url);
return;
}
if (!url || url.length < 1) {
let sprite = node.getComponent(cc.Sprite);
sprite.spriteFrame = null;
let ani = node.getComponent(cc.Animation);
ani.stop();
return;
}
DynamicAssetManager.load(this.node, url, cc.SpriteAtlas, (atlas: cc.SpriteAtlas) => {
this.playOfAtlas(node, atlas, url, speed);
});
}
// 通过纹理图集播放动画
private playOfAtlas(node: cc.Node, atlas: cc.SpriteAtlas, url: string, speed?: number) {
let frames: cc.SpriteFrame[] = atlas.getSpriteFrames();
let clip: cc.AnimationClip = cc.AnimationClip.createWithSpriteFrames(frames, 12);
clip.name = url;
clip.wrapMode = cc.WrapMode.Normal;
let sprite = node.getComponent(cc.Sprite);
sprite.spriteFrame = frames[0];
sprite.sizeMode = cc.Sprite.SizeMode.RAW;
sprite.trim = false;
let ani: cc.Animation = node.getComponent(cc.Animation);
let index = ani.getClips().indexOf(clip);
if (index == -1) {
ani.addClip(clip);
}
let state: cc.AnimationState = ani.play(clip.name);
state.speed = speed > 0 ? speed : 1;
}
onEvent(type: string, state: cc.AnimationState) {
// SysLog.debug(`11111111type:${type},state:${state}`)
if ("play" == type && this._onPlay) {
this._onPlay(state);
}
if ("finished" == type && this._onFinish) {
this._onFinish();
}
}
onDisable() {
this._onPlay = this._onFinish = null;
let animation = this.frameNode.getComponent(cc.Animation);
animation.off("play", this.onEvent, this);
animation.off("finished", this.onEvent, this);
}
onDestroy(): void {
DynamicAssetManager.pullAsset(this.node, this.constructor.name);
}
}
嗯......想了一下也没有需要说明的,注释都很清晰使用也非常方便,哪里要播动画挂哪里,
非常好用~
Tips:二次修改之后,把该代码插件化,只要直接调用入口函数即可,另外也增加了动画事件的监听,考虑常用的只监听开始和结束事件,稍微优化了代码的结构....最后,感谢大家支持!