在遊戲中,遊戲角色的動畫效果是一個遊戲必不可少的一部分。這節我們以構造超級瑪利歐的角色為例,講解cnGameJS裡動畫的實作。
1.原理:
一個動畫如果要實現一連串動作,我們可以把每個動作的快照保留起來,並放在一個大圖上面,然後每次幀更新的時候,就在每個動作的快照之間循環顯示,最終得出一個動畫。因此我們首先要準備一個類似下面的這種圖片:
看到不?把每個動作放在圖片的不同位置上,之後就可以透過改變顯示位置來實現動畫效果了。
當cnGameJS呼叫start方法開始遊戲後,將會呼叫傳入的gameObj的initialize方法進行初始化,並且產生一個遊戲循環,循環裡每次調用gameObj的update和#date
draw方法。因此我們可以把動畫的初始化放在gameObj的initialize中,update和draw分別放在gameObj的update和draw中,實現動畫播放。效果:
#程式碼:
<body> <canvas id="gameCanvas">请使用支持canvas的浏览器查看</canvas> </body> <script src="http://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>2.實現 正如上面看到的,我們只需要用很少的程式碼量,就可以實現一個幀動畫的播放,接下來將介紹cnGameJS裡的幀動畫是怎樣封裝的。
大家很容易可以發現,cnGameJS都遵循一個特定的模式,把物件的階段分為三個:initialize(初始化),update(幀更新)和draw (繪製)
。這樣我們可以很方便地把不同功能的程式碼寫在對應的階段內。 spriteSheet幀動畫也不例外,同樣按照這種模式來寫。初始化:
使用者對一些必要的資訊進行設定。spriteSheet.prototype={ /** *初始化 **/ init:function(id,src,options){ /** *默认对象 **/ 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; //动画X位置 this.y=options.y; //动画Y位置 this.width=options.width; //图片的宽度 this.height=options.height; //图片的高度 this.image=cg.loader.loadedImgs[this.src]; //图片对象 this.frameSize=options.frameSize; //每帧尺寸 this.frameDuration=options.frameDuration; //每帧持续时间 this.direction=options.direction; //读取帧的方向(从做到右或从上到下) this.currentIndex=0; //目前帧索引 this.beginX=options.beginX; //截取图片的起始位置X this.beginY=options.beginY; //截图图片的起始位置Y this.loop=options.loop; //是否循环播放 this.bounce=options.bounce; //是否往返播放 this.onFinsh=options.onFinsh; //播放完毕后的回调函数 this.frames=caculateFrames(options); //帧信息集合 this.now=new Date().getTime(); //当前时间 this.last=new Date().getTime(); //上一帧开始时间 },上面的參數比較多,都是一些對幀動畫屬性的預先設定。需要注意的是我們呼叫了私有方法caculateFrames來計算每個幀的信息,並保存到frames內,為幀繪製做準備。
幀更新:
在每一幀的更新過程中,我們首先獲取當前時間作為幀的開始時間,並且和上一次幀的開始時間相減,就得出上一次幀的用時。 如果用時超過先前設定的每幀的用時,則可以進行幀更新
。然後判斷是否循環或往返播放動畫,依情況更新對應的影格索引。在最終確定幀的索引後,就可以從frames數組中獲取該幀的信息,並返回。/** *更新帧 **/ 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){ //循环 return frames[this.currentIndex=0]; } else if(!this.bounce){//没有循环并且没有往返滚动,则停止在最后一帧 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]; },幀繪製: 在幀更新後,已經獲取到當前幀的索引,因此draw方法就可以從保存所有幀資訊的frames獲取到當前幀的資訊(包括影像截取的起始位置等),從而在指定位置截取大圖片,並畫出該圖片區域的影像:
/** *在特定位置绘制该帧 **/ 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); }最後,也提供跳到特定影格等方法。 動畫模組所有原始碼:###
/** *包含多帧图像的大图片 **/ 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){ /** *默认对象 **/ 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; //动画X位置 this.y=options.y; //动画Y位置 this.width=options.width; //图片的宽度 this.height=options.height; //图片的高度 this.image=cg.loader.loadedImgs[this.src]; //图片对象 this.frameSize=options.frameSize; //每帧尺寸 this.frameDuration=options.frameDuration; //每帧持续时间 this.direction=options.direction; //读取帧的方向(从做到右或从上到下) this.currentIndex=0; //目前帧索引 this.beginX=options.beginX; //截取图片的起始位置X this.beginY=options.beginY; //截图图片的起始位置Y this.loop=options.loop; //是否循环播放 this.bounce=options.bounce; //是否往返播放 this.onFinsh=options.onFinsh; //播放完毕后的回调函数 this.frames=caculateFrames(options); //帧信息集合 this.now=new Date().getTime(); //当前时间 this.last=new Date().getTime(); //上一帧开始时间 }, /** *更新帧 **/ 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){ //循环 return frames[this.currentIndex=0]; } else if(!this.bounce){//没有循环并且没有往返滚动,则停止在最后一帧 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]; }, /** *获取现时帧 **/ 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; });
以上是HTML5遊戲框架cnGameJS開發實錄-實作動畫原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!