canvas画一个h5小游戏

  这两年,随着移动互联网的发展,h5小游戏也大火了一把。这里我就来讲讲实现一个躲水果的小游戏。如截图所示

  

  再附上demo二维码~

  

  目前游戏的套路,基本上是先载入游戏,然后触发游戏开始,游戏结束时记录游戏分数,诱导你转发盆友圈炫耀,或者再来一次。

  这里我们可以定义一个game_control 对象来控制这一系列流程,同时我们还需要一个人物对象,3个水果对象。

  我们可以定义一个基础原型对象ship作为类,可以记录位置,大小,x,y轴的移动速度,相应的图片,然后人物对象和水果对象就是它的几个实例啦。然后通过canvas的drawImage方法将它们画出在画布中。

function ship(options){
                if (options) {
                    var width=options.width,
                        height=options.height;
                    this.x=options.x;
                    this.y=options.y;
                    this.width=width;
                    this.height=height;
                    this.first_x=options.x;
                    this.first_y=options.y;
                    this.speedx=options.speedx;
                    this.speedy=options.speedy;
                    this.csspeedx=options.speedx;
                    this.csspeedy=options.speedy;
                    this.xDirection=options.xDirection||1;//x轴移动方向
                    this.yDirection=options.yDirection||1;//y轴移动方向
                    var canvasOffscreen = document.createElement('canvas');
                    canvasOffscreen.width =width;
                    canvasOffscreen.height =height;
                    canvasOffscreen.getContext('2d').drawImage(options.image, options.sourcex, options.sourcey, options.sourcewidth, options.sourceheight, 0, 0, width, height);
                    this.canvasOffscreen=canvasOffscreen;
                    this.init();
                }
            }
 
            ship.prototype={
                init:function(){
                     
                },
                reset:function(){
 
                },
                draw:function(ctx){
                    //let canvasOffscreen=this.canvasOffscreen;
                    ctx.drawImage(this.canvasOffscreen, this.x, this.y);
                },
                move:function(modifier){
                    this.x+=this.xDirection*this.speedx * modifier;
                    this.y+=this.yDirection*this.speedy * modifier;
                    if(this.x>winwidth-this.width){
                        this.x=winwidth-this.width;
                        this.xDirection=-1;
                    }else if(this.x<0){
                        this.x=0;
                        this.xDirection=1
                    }
                    if(this.y>winheight-this.height){
                        this.y=winheight-this.height;
                        this.yDirection=-1;
                    }else if(this.y<0){
                        this.y=0;
                        this.yDirection=1;
                    }
                }
            }

  这个对象有个移动方法需要注意一下,我们需要判断物体到边界时,物体的移动方向需要转变(也就是将yDirection或者xDirection反向)。

  在这里我们可以将3个水果图片,和人物图片做成雪碧图。然后先将他们用canvas画出来,然后游戏进行中的每一帧将这些canvas再画出来,这样效率比每次都从雪碧图截取要高。

var canvasOffscreen = document.createElement('canvas');
canvasOffscreen.width = dw;
canvasOffscreen.height = dh;
canvasOffscreen.getContext('2d').drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);

// 在绘制每一帧的时候,绘制这个图形
context.drawImage(canvasOffscreen, x, y);

  然后通过game_control中的draw方法画出

draw:function(obj,ctx){
					var ctx=this.ctx,
						monsters=this.monsters;
					var now=Date.now(),
					def=now-this.then;
					this.clear();
					for(var i=0,len=monsters.length;i<len;i++){
						if (this.then) {
							monsters[i].move(def/1000);
						}
						monsters[i].draw(ctx);
					}
					if (this.then) {
						this.check();
						this.then = now;
					}
					this.objs.draw(ctx);
				},

  game_control 对象定义几个方法,init,start,draw,end,reset等方法。

  init方法主要是初始化游戏的,主要处理图片加载,图片加载完后,几个对象的初始化。

  

init:function(){
                    var canvas=document.getElementById('game-canvas'),
                        self=this,
                        ctx=canvas.getContext('2d');
                    self.ctx=ctx;
                    canvas.width=winwidth;
                    canvas.height=winheight;
                    let img=new Image();
                    img.onload=function(){
                        var zjb=new hero({
                            image:img,
                            x:gettruesize(250),
                            y:gettruesize(56),
                            width:gettruesize(50),
                            height:gettruesize(50),
                            sourcewidth:104,
                            sourceheight:104,
                            sourcex:0,
                            sourcey:0
                        });
                        for(var i=0;i<3;i++){
                            var x=60,y=110;
                            if(i==1){x=38,y=330;}
                            if(i==2){x=218,y=338;}
                            var monster=new ship({
                                                image:img,
                                                x:gettruesize(x),
                                                y:gettruesize(y),
                                                width:gettruesize(50),
                                                height:gettruesize(50),
                                                sourcewidth:104,
                                                sourceheight:104,
                                                speedx:gettruesize(getrandom(60,100)),
                                                speedy:gettruesize(getrandom(60,100)),
                                                sourcex:104*(i+1),
                                                sourcey:0
                                            });
                            self.monsters.push(monster);
                        }
                        self.objs=zjb;
                        self.draw();
                        self.bindmove(canvas,zjb);
                    }
                    img.src="all.png";                 
                }

  然后调用给bindmove方法给canvas注册手指触摸事件,如果手机触摸在人物对象这个位置,那么代表游戏开始,调用start方法。

  

bindmove:function(canvas,hero){
                    let self=this;
                    canvas.addEventListener('touchstart', function(e) {
                        var event = e||window.event,
                            csx = event.touches[0].clientX,
                            csy = event.touches[0].clientY,
                            nanshengcsx = hero.x,
                            nanshengcsy = hero.y;
                        if (csx <= hero.x + hero.width && csx >= hero.x && csy <= hero.y + hero.height && csy >= hero.y) {
                            if (!self.startstate) {
                                self.start();
                                timer = setInterval(function(){
                                    self.draw();
                                }, 1);
                            }
                            document.addEventListener('touchmove', move,false);
                            function move(e){
                                e.preventDefault();
                                var event = e||window.event,
                                    nowx = event.touches[0].clientX,
                                    nowy = event.touches[0].clientY;
                                hero.x = nanshengcsx + nowx - csx;
                                hero.y = nanshengcsy + nowy - csy;
                                if(hero.x<0){
                                    hero.x=0;
                                }else if(hero.x+hero.width>winwidth){
                                    hero.x=winwidth-hero.width;
                                }
                                if(hero.y<0){
                                    hero.y=0;
                                }else if(hero.y+hero.height>winheight){
                                    hero.y=winheight-hero.height;
                                }
                            }
                            function moveend(e){
                                document.removeEventListener('touchend',moveend);
                                document.removeEventListener('touchmove',move);
                            }
                            document.addEventListener('touchend', moveend ,false);
                        }
                    },false);
                }

  

   start方法主要是实现每一帧的循环方法。

var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
					window.setTimeout(callback, 20);
				};

  不断的去调用raf(fun)就可以啦,这里我直接用了setInterval方法

start:function(){
                    var self=this;
                    this.startstate=true;
                    this.then=Date.now();
                    this.starttime=this.then;
                    document.getElementById('tips').style.display="none";
                    timer = setInterval(function(){
                        self.draw();//在画布中画出
                    }, 1);
                }

   在循环方法中,通过手指的移动来计算 人物的x,y 轴的位置,通过时间 和 水果x,y 轴的速度计算出相应的位置,然后再进行一一比较,看是否发生触碰,同时,随着时间的推移,物体移动的速度慢慢新增,这里我们定了每5秒增加速度,这里我们将模型当作矩形(复杂模型我也不造怎么算~~),当发生触碰时,则判定游戏结束,将坚持了的时间输出。

check:function(){
                    var last=this.then-this.starttime;
 
                    var monsters=this.monsters;
                    var nansheng=this.objs;
                    if (this.monsterSum != Math.floor(last / 5000)){//如果时间经过5秒就增加速度
                        this.monsterSum ++;  
                        for(var i=0;i<monsters.length;i++){
                            monsters[i].speedx+=60;
                            monsters[i].speedy+=60;
                        }
                    }  
                    for(var i=0;i<monsters.length;i++){
                        var monster1=monsters[i];
                        if ((monster1.x - nansheng.width) <= nansheng.x && nansheng.x <= (monster1.x + monster1.width) && (monster1.y - nansheng.height) <= nansheng.y && nansheng.y <= (monster1.y + monster1.height)) {
                            this.end();
                        }
                    }
                }

  

    至于持续性的与后台的数据交互可以通过websocket来实现(不过我没有试过~~,当时做项目的时候只是结束的时候去保存了一下),游戏结束时提示 分享页面或者在玩一次即可。

  

  

  

posted @ 2016-11-03 21:21  放羊的阿波罗  阅读(1206)  评论(0编辑  收藏  举报