背景:原本是做白鹭项目的,后续考虑换成creator,但是由于creator的动画编辑器比较难受,而在白鹭那边已经有了一些脚本工具,因此希望在creator上实现一个组件,直接能够播放白鹭的movieClip。
在cocos社区找了一圈,没有找到可以直接伸手的组件,故想着自己也试着来造造轮子。
期待的样子:
1.实现movieClip控件(继承Node节点),直接new出来,添加到对应节点下,然后可以调用load(fileName)接口直接加载资源。playMovie/loadAndPlay等直接播放或者加载播放对应的动画/动作;一些额外回调操作;
2.实现movieClipComponent组件,可以挂载到节点上。具体是实现上述的需求。
首先做的一些准备:
1.封装了读取mc资源的接口,主要是来读取json跟texture2d;
加载json,加载texture,保证两步都加载完成了,才进行下一步操作。
解析配置主要做的是把json里的资源resData跟 动画mcData保存下来。
实现思路:
1.当前帧包括当前动画/当前动作/当前帧索引等。将这些作为一个私有变量保存起来,这样可以随时getter/setter。在数据发生变化的时候才重新去计算当前值。
2.基于update帧驱动或者计时器驱动,获取当前帧的配置数据,将纹理渲染出来,然后索引+1,等待下一次刷新。
只是拿到了当前帧的配置数据,怎样才能在雪碧图里裁切出想要的子图呢?
在这里我采取的办法是spriteFrame中的设置rect,来实现子图的效果。
在实现的路上,暂时忽略new过程中的性能损耗。。。。
3.后续又加入了循环以及播放次数的判断。播放结束/完成等的判断。
最后终于能够把动画播放起来了。
延时切换到该文件下的其他动画,结果也是可行。
最后是完整的代码。。。。。
import { Node } from "cc";
import { MovieClipComponent } from "./MovieClipComponent";
export class MovieClip extends Node{
private _mcc:MovieClipComponent;
private get mcc(){
if(!this._mcc)
this._mcc = this.addComponent(MovieClipComponent);
return this._mcc;
}
private _fileName:string;
constructor(){
super();
}
onLoad(){
};
/**
* 加载mc资源文件
*/
public loadFile(fileName:string){
if(fileName === this._fileName){
return;
}
this._fileName = fileName;
this.mcc.loadFile(fileName);
}
/**加载并播放 指定文件名 动画名 动作名 次数 加载完成回调*/
public loadAndPlay(fileName:string,mcName:string,actName?:string,times?:number,compeletCall?:Function,target?:any){
this._fileName = fileName;
this.mcc.loadAndPlay(fileName,mcName,actName,times,compeletCall,target);
}
/**播放指定动画 动作 */
public playMovie(mcName: string, actName?: string, times?: number){
if(!this._fileName)
return;
this.mcc.playMovie(mcName,actName,times);
}
/**跳到指定动作的第几帧 播放次数 播放一轮回调 播放完成回调 ------- 当前动画*/
public gotoAndPlay(actName: string, frame: number = 0, times: number = -1, roundCall?: Function,finishCall?: Function, target?: any) {
if(target){
roundCall = roundCall && roundCall.bind(target);
finishCall = finishCall && finishCall.bind(target);
}
this.mcc.gotoAndPlay(actName, frame, times, roundCall, finishCall);
}
}
import { Component, JsonAsset, Layers, math, Node, Rect, Sprite, SpriteFrame, Texture2D, UITransform, Vec2, _decorator } from "cc";
import { ResourceManage } from "../ResourceManage";
const { ccclass, property } = _decorator;
/**
* 帧动画组件
*
* 读取白鹭.json + .png资源格式
* 利用帧刷新/计时器来刷新纹理
*
*/
@ccclass('MovieClipComponent')
export class MovieClipComponent extends Component {
/**资源文件名 */
private _fileName: string;
/**帧图数据 */
private _resData: any;
/**动画数据 */
private _mcData: any;
/**纹理数据 */
private _texture2d: Texture2D;
/**渲染节点 */
private _sprite: Sprite;
private _spriteUI: UITransform;
/**播放次数 -1为循环播放 */
private _times: number;
/**加载完成回调 */
private _compeletCall: Function;
/**播放一轮回调 */
private _playRoundCall:Function;
/**播放结束回调 */
private _playFinishCall:Function;
/**是否播放中 */
public isPlay: boolean;
private _interval;
/**帧率 */
private _frameRate: number;
/**设置帧率 */
public set frameRate(rate: number) {
if (!rate || this._frameRate == rate) {
return;
}
this._frameRate = rate;
this.play();
}
/**帧率 */
public get frameRate() {
return this._frameRate || this._mcData[this.mcName].frameRate || 24;
}
/**首图基准 */
private _initPos: any;
/**首图基准 */
private get initPos() {
if (!this._initPos)
this._initPos = this._mcData[this.mcName].frames[0];
return this._initPos
}
/**当前动画名 */
private _mcName: string;
/**当前动画名 */
public get mcName(): string {
if (!this._mcName)
this._mcName = this._mcData && Object.keys(this._mcData)[0];
return this._mcName;
}
/**当前动画名 */
public set mcName(name: string) {
let mcs = this._mcData && Object.keys(this._mcData);
if (mcs.indexOf(name) == -1) {
name = mcs[0];
}
this._mcName = name;
}
/**当前动作名 */
private _actName: string;
/**当前动作名 */
public get actName() {
if (this._actName == void 0) {
let labels = this.actNames;
this._actName = labels[0].name;
}
return this._actName;
}
/**设置当前动作名 */
public set actName(name) {
if (this._actName == name)
return;
let actNames = this.actNames;
for (let data of actNames) {
if (data.name == name) {
this._actName = name;
break;
}
}
}
/**当前动画所有动作 */
private _actNames: any[];
/**当前动画所有动作 */
public get actNames() {
if (!this._actNames)
this._actNames = this._mcData[this.mcName].labels || [{ name: "", frame: 1, end: this._mcData[this.mcName].frames.length }];
return this._actNames;
}
onLoad() {
let node = new Node('mc');
node.layer = Layers.Enum.UI_2D;
node.parent = this.node;
this._sprite = node.addComponent(Sprite);
// this._sprite = this.node.getChildByName("Sprite").getComponent(Sprite);
this._spriteUI = this._sprite.getComponent(UITransform);
}
/**加载资源 */
public loadFile(fileName: string,compeletCall?:Function) {
if (fileName == this._fileName)
return;
this._fileName = fileName;
this._compeletCall = compeletCall;
let thiz = this;
ResourceManage.loadMcRes(`${fileName}`, JsonAsset, (res: JsonAsset) => {
thiz.parseJson(res.json);
thiz.checkCompelet();
});
ResourceManage.loadMcRes(`${fileName}/texture`, Texture2D, (res: Texture2D) => {
if (res) {
thiz._texture2d = res;
thiz.checkCompelet();
}
});
}
/**解析配置 */
private parseJson(data: any) {
this._resData = data.res;
this._mcData = data.mc;
}
/**检查加载状态 */
private checkCompelet(): boolean {
if (this._resData && this._mcData) {
this.doLoadCompelet();
return true;
}
return false;
}
/**加载完成 */
private doLoadCompelet() {
this._compeletCall && this._compeletCall()
}
/**加载并播放
* @property fileName 文件名
* @property mcName 动画名
* @property actName 动作名
* @property times 播放次数 -1为循环
* @property compeletCall 加载完成回调
*/
public loadAndPlay(fileName: string, mcName: string, actName: string, times: number = -1, compeletCall?: Function, target?: any) {
let thiz = this;
this.loadFile(fileName,() => {
thiz.playMovie(mcName, actName, times);
compeletCall && compeletCall.call(target);
});
}
/**播放动画 */
public playMovie(mcName: string, actName?: string, times: number = -1) {
this.mcName = mcName;
this.gotoAndPlay(actName, 0, times);
}
/**跳到指定动作 指定帧 */
public gotoAndPlay(actName: string, frame: number = 0, times: number = -1, roundCall?: Function, finishCall?:Function,) {
this.curIndex = void 0;
this.actName = actName;
this._actData = void 0;
this._times = times;
this.curIndex = frame;
this._times = times;
this._playRoundCall = roundCall;
this._playFinishCall = finishCall;
this.play();
}
public play() {
if (this.isPlay)
return;
let thiz = this;
clearInterval(this._interval);
this.isPlay = true;
this._interval = setInterval(() => {
thiz.doTimeUpdate();
}, 1000.0 / this.frameRate);
}
/**停止播放 */
public stop() {
if (!this.isPlay)
return;
this.isPlay = false;
this.curIndex = 0;
clearInterval(this._interval);
}
public pause() {
if (!this.isPlay)
return;
this.isPlay = false;
clearInterval(this._interval);
}
/**重新播放 */
public replay() {
this.isPlay = false;
this.curIndex = 0;
this.play();
}
/**当前帧索引*/
private _curIndex: number;
private get curIndex() {
if (!this._curIndex == void 0 || this._curIndex < this.actData.frame || this._curIndex > this.actData.end) {
this._curIndex = this.actData.frame - 1;
}
return this._curIndex
}
private set curIndex(index) {
if (index == void 0 || index < this.actData.frame - 1 || index > this.actData.end) {
return;
}
this._curIndex = index;
}
/**当前帧数据 */
private _actData: any;
private get actData() {
if (!this._actData) {
let labels = this.actNames;
for (let data of labels) {
if (data.name == this.actName) {
this._actData = data;
break;
}
}
}
return this._actData;
}
/**当前帧数据 */
private get curFrameData() {
return this._mcData[this.mcName].frames[this.curIndex];
}
/**当前纹理数据 */
private getCurResData(res: string) {
return this._resData[res];
}
private doTimeUpdate() {
if (!this.isPlay) {
return;
}
let curFrame = this.curFrameData;
if (!curFrame) {
return;
}
let res = this.getCurResData(curFrame.res);
let spriteFrame = new SpriteFrame();
spriteFrame.texture = this._texture2d;
this._sprite.spriteFrame = spriteFrame;
this._sprite.spriteFrame.rect = new Rect(res.x, res.y, res.w, res.h);
this._sprite.spriteFrame.offset = new Vec2(res.x, res.y);
this._sprite.node.setPosition(curFrame.x - this.initPos.x, curFrame.y - this.initPos.y);
this._spriteUI.width = res.w;
this._spriteUI.height = res.h;
this.curIndex++;
if (this.curIndex >= this.actData.end) {
if (this._times > 0) {
this._times--;
}
this.curIndex = 0;
this.doPlayRound();
if (this._times == 0) {
this.isPlay = false;
this.doPlayFinished();
}
}
}
/**播放结束 */
private doPlayFinished() {
console.log('endddddddddddddddd');
this._playFinishCall && this._playFinishCall();
}
/**播放一轮 */
private doPlayRound() {
console.log('rooooooooooound');
this._playRoundCall && this._playRoundCall();
}
}