【CSON原創(chuàng)】HTML5游戲框架cnGameJS開(kāi)發(fā)實(shí)錄(動(dòng)畫(huà)篇)
在游戲中,游戲角色的動(dòng)畫(huà)效果是一個(gè)游戲必不可少的一部分。這節(jié)我們以構(gòu)造超級(jí)馬里奧的角色為例,講解cnGameJS里動(dòng)畫(huà)的實(shí)現(xiàn)。
1.原理:
一個(gè)動(dòng)畫(huà)如果要實(shí)現(xiàn)一連串動(dòng)作,我們可以把每個(gè)動(dòng)作的快照保留起來(lái),并放在一個(gè)大圖上面,然后每次幀更新的時(shí)候,就在每個(gè)動(dòng)作的快照之間循環(huán)顯示,最終得出一個(gè)動(dòng)畫(huà)。因此我們首先要準(zhǔn)備一個(gè)類似下面的這種圖片:

看到不?把每個(gè)動(dòng)作放在圖片的不同位置上,之后就可以通過(guò)改變顯示位置實(shí)現(xiàn)動(dòng)畫(huà)效果了。
當(dāng)cnGameJS調(diào)用start方法開(kāi)始游戲后,將會(huì)調(diào)用傳入的gameObj的initialize方法進(jìn)行初始化,并且生成一個(gè)游戲循環(huán),循環(huán)里每次調(diào)用gameObj的update和draw方法。(詳見(jiàn)(HTML5游戲框架cnGameJS開(kāi)發(fā)實(shí)錄(資源加載篇)和 HTML5游戲框架cnGameJS開(kāi)發(fā)實(shí)錄(游戲循環(huán)篇)))因此我們可以把動(dòng)畫(huà)的初始化放在gameObj的initialize中,update和draw分別放在gameObj的update和draw中,實(shí)現(xiàn)動(dòng)畫(huà)播放。
效果:
代碼:
<body>
<canvas id="gameCanvas">請(qǐng)使用支持canvas的瀏覽器查看</canvas>
</body>
<script src="https://files.cnblogs.com/Cson/cnGame_v1.0.js"></script>
<script>
var Src="http://pic002.cnblogs.com/images/2012/273330/2012021312050269.png";
/* 初始化 */
cnGame.init('gameCanvas',{width:50,height:60});
var gameObj={
initialize:function(){
this.marie=cnGame.SpriteSheet("marie",Src,{frameSize:[50,60],width:150,height:60,loop:true});
},
update:function(){
this.marie.update();
},
draw:function(){
this.marie.draw();
}
}
cnGame.loader.start([Src],gameObj);
</script>
spriteSheet.prototype={
/**
*初始化
**/
init:function(id,src,options){
/**
*默認(rèn)對(duì)象
**/
var defaultObj={
x:0,
y:0,
width:120,
height:40,
frameSize:[40,40],
frameDuration:100,
direction:"right", //從左到右
beginX:0,
beginY:0,
loop:false,
bounce:false
};
options=options||{};
options=cg.core.extend(defaultObj,options);
this.id=id; //spriteSheet的id
this.src=src; //圖片地址
this.x=options.x; //動(dòng)畫(huà)X位置
this.y=options.y; //動(dòng)畫(huà)Y位置
this.width=options.width; //圖片的寬度
this.height=options.height; //圖片的高度
this.image=cg.loader.loadedImgs[this.src]; //圖片對(duì)象
this.frameSize=options.frameSize; //每幀尺寸
this.frameDuration=options.frameDuration; //每幀持續(xù)時(shí)間
this.direction=options.direction; //讀取幀的方向(從做到右或從上到下)
this.currentIndex=0; //目前幀索引
this.beginX=options.beginX; //截取圖片的起始位置X
this.beginY=options.beginY; //截圖圖片的起始位置Y
this.loop=options.loop; //是否循環(huán)播放
this.bounce=options.bounce; //是否往返播放
this.onFinsh=options.onFinsh; //播放完畢后的回調(diào)函數(shù)
this.frames=caculateFrames(options); //幀信息集合
this.now=new Date().getTime(); //當(dāng)前時(shí)間
this.last=new Date().getTime(); //上一幀開(kāi)始時(shí)間
},
/**
*更新幀
**/
update:function(){
this.now=new Date().getTime();
var frames=this.frames;
if((this.now-this.last)>this.frameDuration){//如果間隔大于幀間間隔,則update
var currentIndex=this.currentIndex;
var length=this.frames.length;
this.last=this.now;
if(currentIndex>=length-1){
if(this.loop){ //循環(huán)
return frames[this.currentIndex=0];
}
else if(!this.bounce){//沒(méi)有循環(huán)并且沒(méi)有往返滾動(dòng),則停止在最后一幀
this.onFinsh&&this.onFinsh();
this.onFinsh=undefined;
return frames[currentIndex];
}
}
if((this.bounce)&&((currentIndex>=length-1&&path>0)||(currentIndex<=0&&path<0))){ //往返
path*=(-1);
}
this.currentIndex+=path;
}
return frames[this.currentIndex];
},
在幀更新后,已經(jīng)獲取到當(dāng)前幀的索引,因此draw方法就可以從保存所有幀信息的frames獲取到當(dāng)前幀的信息(包括圖像截取的起始位置等),從而在指定位置截取大圖片,并畫(huà)出該圖片區(qū)域的圖像:
/**
*在特定位置繪制該幀
**/
draw:function(){
var currentFrame=this.getCurrentFrame();
var width=this.frameSize[0];
var height=this.frameSize[1];
cg.context.drawImage(this.image,currentFrame.x,currentFrame.y,width,height,this.x,this.y,width,height);
}
最后,還提供跳到特定幀等方法。
動(dòng)畫(huà)模塊所有源碼:
/**
*包含多幀圖像的大圖片
**/
spriteSheet=function(id,src,options){
if(!(this instanceof arguments.callee)){
return new arguments.callee(id,src,options);
}
this.init(id,src,options);
}
spriteSheet.prototype={
/**
*初始化
**/
init:function(id,src,options){
/**
*默認(rèn)對(duì)象
**/
var defaultObj={
x:0,
y:0,
width:120,
height:40,
frameSize:[40,40],
frameDuration:100,
direction:"right", //從左到右
beginX:0,
beginY:0,
loop:false,
bounce:false
};
options=options||{};
options=cg.core.extend(defaultObj,options);
this.id=id; //spriteSheet的id
this.src=src; //圖片地址
this.x=options.x; //動(dòng)畫(huà)X位置
this.y=options.y; //動(dòng)畫(huà)Y位置
this.width=options.width; //圖片的寬度
this.height=options.height; //圖片的高度
this.image=cg.loader.loadedImgs[this.src]; //圖片對(duì)象
this.frameSize=options.frameSize; //每幀尺寸
this.frameDuration=options.frameDuration; //每幀持續(xù)時(shí)間
this.direction=options.direction; //讀取幀的方向(從做到右或從上到下)
this.currentIndex=0; //目前幀索引
this.beginX=options.beginX; //截取圖片的起始位置X
this.beginY=options.beginY; //截圖圖片的起始位置Y
this.loop=options.loop; //是否循環(huán)播放
this.bounce=options.bounce; //是否往返播放
this.onFinsh=options.onFinsh; //播放完畢后的回調(diào)函數(shù)
this.frames=caculateFrames(options); //幀信息集合
this.now=new Date().getTime(); //當(dāng)前時(shí)間
this.last=new Date().getTime(); //上一幀開(kāi)始時(shí)間
},
/**
*更新幀
**/
update:function(){
this.now=new Date().getTime();
var frames=this.frames;
if((this.now-this.last)>this.frameDuration){//如果間隔大于幀間間隔,則update
var currentIndex=this.currentIndex;
var length=this.frames.length;
this.last=this.now;
if(currentIndex>=length-1){
if(this.loop){ //循環(huán)
return frames[this.currentIndex=0];
}
else if(!this.bounce){//沒(méi)有循環(huán)并且沒(méi)有往返滾動(dòng),則停止在最后一幀
this.onFinsh&&this.onFinsh();
this.onFinsh=undefined;
return frames[currentIndex];
}
}
if((this.bounce)&&((currentIndex>=length-1&&path>0)||(currentIndex<=0&&path<0))){ //往返
path*=(-1);
}
this.currentIndex+=path;
}
return frames[this.currentIndex];
},
/**
*跳到特定幀
**/
index:function(index){
this.currentIndex=index;
return this.frames[this.currentIndex];
},
/**
*獲取現(xiàn)時(shí)幀
**/
getCurrentFrame:function(){
return this.frames[this.currentIndex];
},
/**
*在特定位置繪制該幀
**/
draw:function(){
var currentFrame=this.getCurrentFrame();
var width=this.frameSize[0];
var height=this.frameSize[1];
cg.context.drawImage(this.image,currentFrame.x,currentFrame.y,width,height,this.x,this.y,width,height);
}
}
this.SpriteSheet=spriteSheet;
});
浙公網(wǎng)安備 33010602011771號(hào)