首頁  >  文章  >  web前端  >  HTML5遊戲框架cnGameJS開發實錄-實作動畫原理

HTML5遊戲框架cnGameJS開發實錄-實作動畫原理

黄舟
黄舟原創
2017-03-24 16:08:512368瀏覽

 

 在遊戲中,遊戲角色的動畫效果是一個遊戲必不可少的一部分。這節我們以構造超級瑪利歐的角色為例,講解cnGameJS裡動畫的實作。

1.原理:

  一個動畫如果要實現一連串動作,我們可以把每個動作的快照保留起來,並放在一個大圖上面,然後每次幀更新的時候,就在每個動作的快照之間循環顯示,最終得出一個動畫。因此我們首先要準備一個類似下面的這種圖片:

  看到不?把每個動作放在圖片的不同位置上,之後就可以透過改變顯示位置來實現動畫效果了。

  當cnGameJS呼叫start方法開始遊戲後,將會呼叫傳入的gameObj的initialize方法進行初始化,並且產生一個遊戲循環,循環裡每次調用gameObj的update和#date

draw方法。因此我們可以把動畫的初始化放在gameObj的initialize中,update和draw分別放在gameObj的update和draw中,實現動畫播放。

效果:

HTML5遊戲框架cnGameJS開發實錄-實作動畫原理 

#程式碼:

<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(&#39;gameCanvas&#39;,{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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn