原先在网上查询了比比皆是有关那个小游戏的资料,创新意识不断的小游戏有着赏心悦目的经营销售效益

flappy bird制作全流程:

图片 1

flappy bird制作全流程:

图片 2

一、前言

像素小鸟这些简单的游乐于2015年在互连网上爆红,游戏上线一段时间内appleStore上的下载量一度达到6000万次,风靡临时,

近年来移动web的普及为那样没有复杂逻辑和精巧动画效果,但是趣味十足的小游戏提供了美好的条件,

再正是借助各大社交软件平台的传播效应,创新意识不断的小游戏有着美艳的经营销售效益,得到了众多的关心。

在此在此以前在网上查询了很多关于这一个小游戏的资料,可是基本上杂乱无章,本人的组成相关学科将这些游乐的重庆大学框架整理出来,供我们一起上学。

一、前言

像素小鸟那几个简单的游玩于二零一四年在网络上爆红,游戏上线一段时间内appleStore上的下载量一度高达六千万次,风靡一时半刻,

近些年移动web的普及为这么没有复杂逻辑和迷你动画效果,可是趣味十足的小游戏提供了完美的环境,

再者凭借各大社交软件平台的传播效应,创新意识不断的小游戏有着美观的经营销售效益,得到了无数的关切。

在此在此以前在网上查询了不可胜道有关那几个小游戏的材质,但是大多非常不好,本身的组合有关课程将以此娱乐的基本点框架整理出来,供我们一起上学。

② 、技术中心

 基本JavaScript基础 ,canvas 基础, 面向对象的思索;

② 、技术中央

 基本JavaScript基础 ,canvas 基础, 面向对象的盘算;

叁 、思路整理

叁 、思路整理

总体游戏的逻辑相比较简单:

第贰游戏规则:鸟撞到管道上,地上要驾鹤归西,飞到显示屏外要过逝。

说不上:鸟在飞翔的进程中,会落下,类似落体运动,必要玩家不断点击显示器让鸟向上海飞机创造厂。

再也正是:鸟和背景元素的相对移动的历程,鸟不动,背景左移。

一体娱乐的逻辑比较简单:

首先游戏规则:鸟撞到管道上,地上要完蛋,飞到荧屏外要完蛋。

协理:鸟在飞翔的过程中,会落下,类似落体运动,要求玩家不断点击显示器让鸟向上海飞机创立厂。

再相当于:鸟和背景成分的相对移动的进度,鸟不动,背景左移。

将一切娱乐细化:

咱俩应用面向对象的思绪来营造,具体的事物用构造函数来创立,方法放到构造函数的原形对象中。

游戏细化那几个进程不是轻易的,就算在没有相关指点的状态下,本人要时时刻刻的组成自身的想法去试错。

自小编使用的措施是运用Xmind将流程以脑图的格局绘制下来,分块去做,不断细化记录自身的思绪,最终表现的功用如下:

(顺序依据图片中的序号去看  脑图、素材、及全部源码下载地址:http://pan.baidu.com/s/1c130V7M
想演练的同窗可以点那里)

脑图分为三大块:① 、准备阶段 贰 、主函数 ③ 、游戏优化。

图片 3

图片 4

 

 

将全体游戏细化:

作者们采纳面向对象的思路来创设,具体的事物用构造函数来成立,方法放到构造函数的面目对象中。

娱乐细化那些历程不是一蹴即至的,假若在未曾有关引导的场馆下,本身要持续的重组自个儿的想法去试错。

本身使用的法门是应用Xmind将流程以脑图的款式绘制下来,分块去做,不断细化记录自个儿的思路,最终突显的功力如下:

(顺序根据图片中的序号去看  脑图、素材、及全体源码下载地址:http://pan.baidu.com/s/1c130V7M
想练习的同桌能够点这里)

脑图分为三大块:壹 、准备阶段 二 、主函数 三 、游戏优化。

图片 5

图片 6

 

 

 ④ 、游戏实现:

于今组合脑图来日趋落到实处大家的娱乐。

1.装置canvas画布,准备图片数据,当图片加载成功后实施回调函数;

图片 7图片 8

<canvas id="cvs" width="800" height="600"></canvas>
<script>
    var imglist = [
        { "name":"birds","src":"res/birds.png"},
        { "name":"land","src":"res/land.png"},
        { "name":"pipe1","src":"res/pipe1.png"},
        { "name":"pipe2","src":"res/pipe2.png"},
        { "name":"sky","src":"res/sky.png"}
    ];

    var cvs = document.getElementById("cvs");
    var ctx = cvs.getContext("2d");
</script>

画布准备 ,图片数据准备

此地这些入口函数的设置要留心,必须保障图片能源加载成功后再实践其它操作,每加载一张图片大家让imgCount–,减到0的时候再履行主函数;

图片 9图片 10

function  load (source, callback ){
        var imgEls={};
        var imgCount=source.length;
        for (var i = 0; i < imgCount; i++) {
            var name =  source[i].name;
            var newImg = new Image ();
            newImg.src = source[i].src;
            imgEls[name] = newImg;
            imgEls[name].addEventListener("load",function(){
                imgCount--;
                if(imgCount==0){
                    callback(imgEls);
                };
            })
        };
    };

入口函数设置

主循环的装置:那里大家不利用setInterval来控制循环次数,大家选取三个叫requestAnimationFrame()的定时器

       因为setInterval会发生时间误差,setInterval只可以根据时间来运动固定距离。

       那对于轮播图一类几千微秒切换一次的动作来说并没有怎么关联,可是对于我们16-18微秒绘制一回的动画是不行不纯粹的;

       requestAnimationFrame()那一个定时器的利益是依照浏览器的属性来施行三个函数,大家用来获取一遍绘制的间隔时间;

       移动距离的盘算改变成速度×间隔时间的不二法门,来化解绘图不确切的标题。

图片 11图片 12

var preTime= Date.now();             //获取当前时间
    function run(){
           var now = Date.now();         //获取最新时间
           dt = now - preTime;            //获取时间间隔
           preTime = now;                  //更新当前时间
           ctx.clearRect(0,0,800,600);    //清空画布
 //---------------------------------------------
                  绘制代码执行区域
//-----------------------------------------------
           requestAnimationFrame(run);    //再次执行run函数
     }
 requestAnimationFrame(run);   //首次执行run函数;

设置绘制格局

二 、主函数分为两片段作用,简单说正是把图画上去,然后处理动态效果,再判断一下是否违犯禁令。

2.1 小鸟的绘图:

  小鸟自己有二个翅膀扇动的意义,和叁个降落的经过。

  翅膀扇动的进度是一张天使图三幅画面包车型大巴的切换(设置3个index属性,控制天使图的职责),下降过程是其y坐标在画布上的位移();

  所以小鸟的构造函数中应该蕴含(图源,x坐标,y坐标,速度,下降加快度,ctx(context画布))等参数。

  那里供给小心几点:

  •  小鸟的绘图选拔canvas
    drawImage的九参数形式(分别是图表,原图的裁切源点,原图的宽高,贴到画布上的地点,贴到画布上的宽高);
  •  小鸟的膀子扇动不能够太快,所以大家设置二个阀门函数,当累计计时超过100ms的时候切换一下图片,然后在让一起计时减去100ms;
  •  小鸟的减退要求选取一定物理知识,不过都很简单啦。
    大家都以由此速度×时间来落实;

图片 13图片 14

var Bird = function (img,x,y,speed,a,ctx){
    this.img = img;
    this.x = x;
    this.y = y;
    this.speed = speed;
    this.a =a ;
    this.ctx = ctx;
    this.index = 0;    //用于制作小鸟扇翅膀的动作
}

Bird.prototype.draw = function (){
    this.ctx.drawImage(
        this.img,52*this.index,0,52,45,
        this.x,this.y,52,45
    )
}

var durgather=0;       
Bird.prototype.update = function(dur){
    //小鸟翅膀扇动每100ms切换一张图片
    durgather+=dur;
    if(durgather>100){
        this.index++;
        if(this.index===2){
             this.index=0;
        }
      durgather -= 100;
    }
    //小鸟下落动作
    this.speed = this.speed + this.a *dur;
    this.y = this.y + this.speed * dur;
}

鸟类的构造函数及动作控制

 
构造三个小鸟,并且将其动作刷新函数和制图函数放置在我们地点提到的绘图区域,此后结构出的好像对象都是那般的操作步骤:

 
那里须要留意的一点是,怎样让鸟儿顺畅的前进飞翔,其实依然物理知识,由于加速度的意义,大家给小鸟一个上扬的顺时速度就能够了。

图片 15图片 16

load(imglist ,function(imgEls){
            //创建对象
            //在主函数中创建一个小鸟
            var bird = new Bird(imgEls["birds"],150,100,0.0003,0.0006,ctx);
            //主循环
            var preTime= Date.now();
            function run(){
                var now = Date.now();
                dt = now - preTime;
                preTime = now;
                ctx.clearRect(0,0,800,600);
                //--------图片绘制区域-------
                bird.update(dt)
                bird.draw();
                //-------------------------

                requestAnimationFrame(run);
            }
            requestAnimationFrame(run);

            //设置点击事件。给小鸟一个瞬时的向上速度
            cvs.addEventListener("click",function(){
                bird.speed =  -0.3;
            } )
        })

制图小鸟,点击小鸟上海飞机创制厂

效能如下:

图片 17

2.2天上的绘图:

  天空的绘图相比较不难了,只要采纳canvas
drawImage的三参数方式就足以(图源,画布上的坐标)。

  那里唯一专注的有个别是,无缝滚动的完毕,对于800*600分辨率这种情景大家创造多少个天空对象就足以了,但是为了适配越来越多的景观,大家将那个效应写活

  在天宇的构造函数上加三个count属性设置多少个天空图片,count属性让实例通过原形中的方法访问。前面涉及到再现的本土和管道,都给它们拉长那种考虑。

图片 18图片 19

var Sky = function(img,x,speed,ctx) {
    this.img = img ;
    this.ctx = ctx;
    this.x = x;
    this.speed = speed;
}
Sky.prototype.draw = function(){
    this.ctx.drawImage(
        this.img ,this.x,0
    )
}
Sky.prototype.setCount = function(count){
    Sky.count = count;
}
Sky.prototype.update = function(dur){
    this.x = this.x+ this.speed * dur;
    if(this.x<-800){  //天空图片的宽度是800
        this.x = Sky.count * 800 + this.x;  //当向左移动了一整张图片后立刻切回第一张图片
    }
}

天上构造函数及运动函数

  同理在主函数中制造二个天空对象,并将履新函数和制图函数放置在主循环的绘图区域;

  setcount是用来设置无缝滚动的

  注意一点:绘制上的图纸是有三个层级关系的,无法把鸟画到天空的上面,那自然最后画鸟了,上面涉及到的覆盖难点不再专门提到。

  那里仅插入部分连锁代码

图片 20图片 21

var bird = new Bird(imgEls["birds"],150,100,0.0003,0.0006,ctx);
            var sky1 = new Sky(imgEls["sky"],0,-0.3,ctx);
            var sky2 = new Sky(imgEls["sky"],800,-0.3,ctx);
            //主循环
            var preTime= Date.now();
            function run(){
                var now = Date.now();
                dt = now - preTime;
                preTime = now;
                ctx.clearRect(0,0,800,600);
                //--------图片绘制区域-------
                sky1.update(dt);
                sky1.draw()
                sky2.update(dt);
                sky2.draw()
                sky1.setCount(2);

                bird.update(dt)
                bird.draw();
                //-------------------------

绘图天空

2.3 地面包车型地铁绘图

  和天幕的绘图完全一致,由于地面图片尺寸较小,所以大家要多画多少个

图片 22图片 23

var Land = function(img,x,speed,ctx){
    this.img = img ;
    this.x = x;
    this.speed = speed;
    this.ctx = ctx ;
}
Land.prototype.draw = function(){
    this.ctx.drawImage (
        this.img , this.x ,488
    )
}
Land.prototype.setCount= function(count){
    Land.count = count;
}
Land.prototype.update = function(dur){
    this.x =  this.x + this.speed * dur;
    if (this.x <- 336){
        this.x = this.x + Land.count * 336; //无缝滚动的实现
    }
}

本地的构造函数及活动函数

图片 24图片 25

//创建----放置在创建区域
var land1 = new Land(imgEls["land"],0,-0.3,ctx);
var land2 = new Land(imgEls["land"],336*1,-0.3,ctx);
var land3 = new Land(imgEls["land"],336*2,-0.3,ctx);
var land4 = new Land(imgEls["land"],336*3,-0.3,ctx);

//绘制 ----放置在绘制区域
 land1.update(dt);
 land1.draw();
 land2.update(dt);
 land2.draw();
 land3.update(dt);
 land3.draw();
 land4.update(dt);
 land4.draw();
 land1.setCount(4);  //设置无缝滚动

绘图地面首要代码

2.4绘制管道

  管道的绘图有二个难题是管道中度的规定

  要点:

  •  为了保全游戏可玩性,管道必须有2个一定中度+一个自由中度,且上下管道之间的留白是一贯的上涨幅度。
  • 管道不是三番五次的,三个相邻的管道之间有距离
  • 小心管道在无缝播放,抽回后务必提交三个新的随机高度,给用户一种错觉,以为又1个管道飘了苏醒。

  

图片 26图片 27

var  Pipe =  function(upImg,downImg,x,speed,ctx){
    this.x = x;
    this.upImg = upImg ;
    this.downImg = downImg;
    this.speed = speed;
    this.ctx = ctx;
    this.r = Math.random() *200 + 100;  //随机高度+固定高度
}
Pipe.prototype.draw = function(){
    this.ctx.drawImage(
        this.upImg, this.x , this.r - 420    //管道图片的长度是420
    )
    this.ctx.drawImage(
        this.downImg, this.x , this.r +150    //管道中建的留白是150px
    )
}
Pipe.prototype.setCount = function( count,gap ){
    Pipe.count = count;
    Pipe.gap = gap;        //这里是这次绘制的特别之处,加入了间隔
}
Pipe.prototype.update =function( dur ){
    this.x = this.x + this.speed*dur;
    if(this.x <- 52){    //管道宽度52px
        this.x = this.x + Pipe.count * Pipe.gap;   //无缝滚动
        this.r = Math.random() *200 + 150;     //切换后的管道必须重新设置一个高度,给用户一个新管道的错觉
    }
}    

管道的构造函数及运动函数

图片 28图片 29

//创建区域
            var pipe1 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],400, -0.1,ctx);
            var pipe2 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],600, -0.1,ctx);
            var pipe3 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],800, -0.1,ctx);
            var pipe4 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],1000,-0.1,ctx);
            var pipe5 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],1200,-0.1,ctx);

//绘制区域
                pipe1.update(dt);
                pipe1.draw();
                pipe2.update(dt);
                pipe2.draw();
                pipe3.update(dt);
                pipe3.draw();
                pipe4.update(dt);
                pipe4.draw();
                pipe5.update(dt);
                pipe5.draw();
                pipe1.setCount(5,200);   //设置管道数量和间隔

管道的绘图首要代码

到这一步我们的重大画面就制作出来了,是否很简短呢O(∩_∩)O~

2.5 判断游戏是或不是违禁

  1. 接触到地面和天空顶部,甘休游戏

图片 30图片 31

//我们改造一下主循环,设置一个gameover为false来控制函数的执行
//任何违规都会触发gameover=true;
               var gameover = false;

                if(bird.y < 0 || bird.y > 488 -45/2 ){ //碰到天和地
                    gameover = true ;
                }
                if(!gameover){    //如果没有结束游戏则继续游戏
                    requestAnimationFrame(run);
                }

差不离判读gameover

  2. 会师管道甘休游戏

图片 32图片 33

//x和y到时候我们传入小鸟的运动轨迹,每次重绘管道都有判断
Pipe.prototype.hitTest = function(x,y){
    return (x > this.x && x < this.x + 52)    //在管子横向中间
        &&(! (y >this.r  && y < this.r +150));  //在管子竖向中间
}

判断是或不是遇到管仲

图片 34图片 35

 var gameover = false;
                gameover = gameover || pipe1.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe2.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe3.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe4.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe5.hitTest(bird.x ,bird.y);
                //逻辑终端
                if(bird.y < 0 || bird.y > 488 -45/2 ){
                    gameover = true ;
                }
                if(!gameover){
                    requestAnimationFrame(run);
                }        

主循环的判定标准构成

图片 36

到这一步大家的娱乐形成的大半了,剩下的即是有的数码的匡正

要害须要改良的一个点是撞倒的持筹握算,因为大家全体的碰撞都以比照小鸟图片的左上角总括的,这样就会有不准确的题材,通过测试很简单将那个距离加减修正了

 

3.游戏的优化

 小鸟游戏的小鸟在前后的经过中会随着点击,抬头飞翔,或退让冲刺,怎样成功这一个功用啊?

 答案就是活动canvas 坐标系和挑选坐标系的角度
 ctx.translate()和ctx.rotate();

 为了防止万一全部坐标系的完全旋转运动

 需求在小鸟绘制函数Bird.prototype.draw里前面后端插手ctx.save()
和ctx.restore()来单独主宰小鸟画布

图片 37图片 38

Bird.prototype.draw = function (){
    this.ctx.save();
    this.ctx.translate(this.x ,this.y);  //坐标移动到小鸟的中心点上
    this.ctx.rotate((Math.PI /6) * this.speed / 0.3 );
    //小鸟最大旋转30度,并随着速度实时改变角度
    this.ctx.drawImage(
        this.img,52*this.index,0,52,45,
        -52/2,-45/2,52,45  //这里很重要的一点是,整个小鸟坐标系开始移动
    )
    this.ctx.restore();
}

进入小鸟旋转效果

当然最后不要遗忘对管道碰撞的判断,在这里再纠正2次。

实际上假若打算插手旋转效果,上一回的校正不必要,你会意识许多重复工。

最后做出的功效如下:

图片 39

 主体功能和逻辑已经全副达成。越多的职能能够自行添加。

 要是想本人演习一下,请点击游戏细化部分的链接下载相关资料和任何源码。

 四 、游戏实现:

近来重组脑图来逐步落到实处大家的游艺。

1.设置canvas画布,准备图片数据,当图片加载成功后进行回调函数;

图片 40图片 41

<canvas id="cvs" width="800" height="600"></canvas>
<script>
    var imglist = [
        { "name":"birds","src":"res/birds.png"},
        { "name":"land","src":"res/land.png"},
        { "name":"pipe1","src":"res/pipe1.png"},
        { "name":"pipe2","src":"res/pipe2.png"},
        { "name":"sky","src":"res/sky.png"}
    ];

    var cvs = document.getElementById("cvs");
    var ctx = cvs.getContext("2d");
</script>

画布准备 ,图片数据准备

那里那些入口函数的安装要注意,必须保障图片财富加载成功后再举办其余操作,每加载一张图纸大家让imgCount–,减到0的时候再实施主函数;

图片 42图片 43

function  load (source, callback ){
        var imgEls={};
        var imgCount=source.length;
        for (var i = 0; i < imgCount; i++) {
            var name =  source[i].name;
            var newImg = new Image ();
            newImg.src = source[i].src;
            imgEls[name] = newImg;
            imgEls[name].addEventListener("load",function(){
                imgCount--;
                if(imgCount==0){
                    callback(imgEls);
                };
            })
        };
    };

入口函数设置

主循环的设置:这里大家不选拔setInterval来支配循环次数,我们使用3个叫requestAnimationFrame()的定时器

       因为setInterval会发出时间误差,setInterval只好依据时间来移动固定距离。

       这对于轮播图一类几千飞秒切换一遍的动作来说并没有怎么关联,但是对于大家16-18阿秒绘制一回的卡通片是充裕不规范的;

       requestAnimationFrame()那么些定时器的好处是依据浏览器的性质来进行3个函数,大家用来赢得两次绘制的间隔时间;

       移动距离的估测计算改变成速度×间隔时间的艺术,来消除绘图不准确的标题。

图片 44图片 45

var preTime= Date.now();             //获取当前时间
    function run(){
           var now = Date.now();         //获取最新时间
           dt = now - preTime;            //获取时间间隔
           preTime = now;                  //更新当前时间
           ctx.clearRect(0,0,800,600);    //清空画布
 //---------------------------------------------
                  绘制代码执行区域
//-----------------------------------------------
           requestAnimationFrame(run);    //再次执行run函数
     }
 requestAnimationFrame(run);   //首次执行run函数;

设置绘制形式

贰 、主函数分为两部分作用,简单说便是把图画上去,然后处理动态效果,再判断一下是或不是违犯禁令。

2.1 小鸟的绘图:

  小鸟本人有一个翅膀扇动的作用,和三个下挫的历程。

  翅膀扇动的经过是一张天使图三幅画面包车型客车的切换(设置三个index属性,控制天使图的职分),下跌进程是其y坐标在画布上的移位();

  所以小鸟的构造函数中应有包含(图源,x坐标,y坐标,速度,降低加快度,ctx(context画布))等参数。

  那里供给注意几点:

  •  小鸟的绘图选用canvas
    drawImage的九参数形式(分别是图片,原图的裁切起源,原图的宽高,贴到画布上的地点,贴到画布上的宽高);
  •  小鸟的翅膀扇动无法太快,所以我们设置多少个阀门函数,当累计计时超越100ms的时候切换一下图形,然后在让一起计时减去100ms;
  •  小鸟的下落要求动用一定物理知识,可是都非常粗略啦。
    大家都以经过速度×时间来完毕;

图片 46图片 47

var Bird = function (img,x,y,speed,a,ctx){
    this.img = img;
    this.x = x;
    this.y = y;
    this.speed = speed;
    this.a =a ;
    this.ctx = ctx;
    this.index = 0;    //用于制作小鸟扇翅膀的动作
}

Bird.prototype.draw = function (){
    this.ctx.drawImage(
        this.img,52*this.index,0,52,45,
        this.x,this.y,52,45
    )
}

var durgather=0;       
Bird.prototype.update = function(dur){
    //小鸟翅膀扇动每100ms切换一张图片
    durgather+=dur;
    if(durgather>100){
        this.index++;
        if(this.index===2){
             this.index=0;
        }
      durgather -= 100;
    }
    //小鸟下落动作
    this.speed = this.speed + this.a *dur;
    this.y = this.y + this.speed * dur;
}

鸟类的构造函数及动作控制

 
构造1个小鸟,并且将其动作刷新函数和制图函数放置在咱们地点提到的绘图区域,此后协会出的接近对象都以如此的操作步骤:

 
那里需求小心的一些是,如何让鸟儿顺畅的开拓进取飞翔,其实照旧物理知识,由于加快度的成效,大家给小鸟3个提升的顺时速度就足以了。

图片 48图片 49

load(imglist ,function(imgEls){
            //创建对象
            //在主函数中创建一个小鸟
            var bird = new Bird(imgEls["birds"],150,100,0.0003,0.0006,ctx);
            //主循环
            var preTime= Date.now();
            function run(){
                var now = Date.now();
                dt = now - preTime;
                preTime = now;
                ctx.clearRect(0,0,800,600);
                //--------图片绘制区域-------
                bird.update(dt)
                bird.draw();
                //-------------------------

                requestAnimationFrame(run);
            }
            requestAnimationFrame(run);

            //设置点击事件。给小鸟一个瞬时的向上速度
            cvs.addEventListener("click",function(){
                bird.speed =  -0.3;
            } )
        })

绘制小鸟,点击小鸟上海飞机创建厂

意义如下:

图片 50

2.2天空的绘图:

  天空的绘图比较不难了,只要使用canvas
drawImage的三参数方式就足以(图源,画布上的坐标)。

  那里唯一专注的一些是,无缝滚动的落实,对于800*600分辨率那种情况大家创立四个天空对象就足以了,不过为了适配越来越多的情况,大家将以此作用写活

  在天空的构造函数上加贰个count属性设置多少个天空图片,count属性让实例通过原形中的方法访问。前边涉及到再现的本土和管道,都给它们增加那种设想。

图片 51图片 52

var Sky = function(img,x,speed,ctx) {
    this.img = img ;
    this.ctx = ctx;
    this.x = x;
    this.speed = speed;
}
Sky.prototype.draw = function(){
    this.ctx.drawImage(
        this.img ,this.x,0
    )
}
Sky.prototype.setCount = function(count){
    Sky.count = count;
}
Sky.prototype.update = function(dur){
    this.x = this.x+ this.speed * dur;
    if(this.x<-800){  //天空图片的宽度是800
        this.x = Sky.count * 800 + this.x;  //当向左移动了一整张图片后立刻切回第一张图片
    }
}

天上构造函数及活动函数

  同理在主函数中创立一个天空对象,并将履新函数和制图函数放置在主循环的绘图区域;

  setcount是用来安装无缝滚动的

  注意一点:绘制上的图样是有贰个层级关系的,不可能把鸟画到天空的底下,那本来最终画鸟了,上面涉及到的遮盖难点不再专门提到。

  那里仅插入部分连锁代码

图片 53图片 54

var bird = new Bird(imgEls["birds"],150,100,0.0003,0.0006,ctx);
            var sky1 = new Sky(imgEls["sky"],0,-0.3,ctx);
            var sky2 = new Sky(imgEls["sky"],800,-0.3,ctx);
            //主循环
            var preTime= Date.now();
            function run(){
                var now = Date.now();
                dt = now - preTime;
                preTime = now;
                ctx.clearRect(0,0,800,600);
                //--------图片绘制区域-------
                sky1.update(dt);
                sky1.draw()
                sky2.update(dt);
                sky2.draw()
                sky1.setCount(2);

                bird.update(dt)
                bird.draw();
                //-------------------------

绘制天空

2.3 地面包车型客车绘图

  和天空的绘图完全一致,由于当地图片尺寸较小,所以我们要多画多少个

图片 55图片 56

var Land = function(img,x,speed,ctx){
    this.img = img ;
    this.x = x;
    this.speed = speed;
    this.ctx = ctx ;
}
Land.prototype.draw = function(){
    this.ctx.drawImage (
        this.img , this.x ,488
    )
}
Land.prototype.setCount= function(count){
    Land.count = count;
}
Land.prototype.update = function(dur){
    this.x =  this.x + this.speed * dur;
    if (this.x <- 336){
        this.x = this.x + Land.count * 336; //无缝滚动的实现
    }
}

本地的构造函数及移动函数

图片 57图片 58

//创建----放置在创建区域
var land1 = new Land(imgEls["land"],0,-0.3,ctx);
var land2 = new Land(imgEls["land"],336*1,-0.3,ctx);
var land3 = new Land(imgEls["land"],336*2,-0.3,ctx);
var land4 = new Land(imgEls["land"],336*3,-0.3,ctx);

//绘制 ----放置在绘制区域
 land1.update(dt);
 land1.draw();
 land2.update(dt);
 land2.draw();
 land3.update(dt);
 land3.draw();
 land4.update(dt);
 land4.draw();
 land1.setCount(4);  //设置无缝滚动

绘制地面首要代码

2.4制图管道

  管道的绘图有贰个难题是管道高度的明确

  要点:

  •  为了维持游戏可玩性,管道必须有多个稳定中度+三个私下中度,且上下管道之间的留白是稳定的幅度。
  • 管道不是三番五次的,七个相邻的管道之间有距离
  • 在意管道在无缝播放,抽回后务必交给二个新的即兴中度,给用户一种错觉,以为又三个管道飘了还原。

  

图片 59图片 60

var  Pipe =  function(upImg,downImg,x,speed,ctx){
    this.x = x;
    this.upImg = upImg ;
    this.downImg = downImg;
    this.speed = speed;
    this.ctx = ctx;
    this.r = Math.random() *200 + 100;  //随机高度+固定高度
}
Pipe.prototype.draw = function(){
    this.ctx.drawImage(
        this.upImg, this.x , this.r - 420    //管道图片的长度是420
    )
    this.ctx.drawImage(
        this.downImg, this.x , this.r +150    //管道中建的留白是150px
    )
}
Pipe.prototype.setCount = function( count,gap ){
    Pipe.count = count;
    Pipe.gap = gap;        //这里是这次绘制的特别之处,加入了间隔
}
Pipe.prototype.update =function( dur ){
    this.x = this.x + this.speed*dur;
    if(this.x <- 52){    //管道宽度52px
        this.x = this.x + Pipe.count * Pipe.gap;   //无缝滚动
        this.r = Math.random() *200 + 150;     //切换后的管道必须重新设置一个高度,给用户一个新管道的错觉
    }
}    

管道的构造函数及活动函数

图片 61图片 62

//创建区域
            var pipe1 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],400, -0.1,ctx);
            var pipe2 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],600, -0.1,ctx);
            var pipe3 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],800, -0.1,ctx);
            var pipe4 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],1000,-0.1,ctx);
            var pipe5 = new Pipe(imgEls["pipe2"],imgEls["pipe1"],1200,-0.1,ctx);

//绘制区域
                pipe1.update(dt);
                pipe1.draw();
                pipe2.update(dt);
                pipe2.draw();
                pipe3.update(dt);
                pipe3.draw();
                pipe4.update(dt);
                pipe4.draw();
                pipe5.update(dt);
                pipe5.draw();
                pipe1.setCount(5,200);   //设置管道数量和间隔

管道的绘图重要代码

到这一步大家的主要画面就塑造出来了,是或不是很简短呢O(∩_∩)O~

2.5 判断游戏是或不是违犯禁令

  1. 接触到地面和天空顶部,停止游戏

图片 63图片 64

//我们改造一下主循环,设置一个gameover为false来控制函数的执行
//任何违规都会触发gameover=true;
               var gameover = false;

                if(bird.y < 0 || bird.y > 488 -45/2 ){ //碰到天和地
                    gameover = true ;
                }
                if(!gameover){    //如果没有结束游戏则继续游戏
                    requestAnimationFrame(run);
                }

差不离判读gameover

  2. 蒙受管道结束游戏

图片 65图片 66

//x和y到时候我们传入小鸟的运动轨迹,每次重绘管道都有判断
Pipe.prototype.hitTest = function(x,y){
    return (x > this.x && x < this.x + 52)    //在管子横向中间
        &&(! (y >this.r  && y < this.r +150));  //在管子竖向中间
}

认清是不是境遇管仲

图片 67图片 68

 var gameover = false;
                gameover = gameover || pipe1.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe2.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe3.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe4.hitTest(bird.x ,bird.y);
                gameover = gameover || pipe5.hitTest(bird.x ,bird.y);
                //逻辑终端
                if(bird.y < 0 || bird.y > 488 -45/2 ){
                    gameover = true ;
                }
                if(!gameover){
                    requestAnimationFrame(run);
                }        

主循环的判定标准构成

图片 69

到这一步大家的游艺完毕的大都了,剩下的便是有的数额的改进

要害需求改正的三个点是碰撞的一个钱打二十六个结,因为我们具有的撞击都以比照小鸟图片的左上角计算的,那样就会有不精确的标题,通过测试很不难将以此距离加减改正了

 

3.游戏的优化

 小鸟游戏的鸟儿在左右的历程中会随着点击,抬头飞翔,或投降冲刺,如何完结这么些职能呢?

 答案正是移动canvas 坐标系和挑选坐标系的角度
 ctx.translate()和ctx.rotate();

 为了防范全部坐标系的完好旋转运动

 供给在小鸟绘制函数Bird.prototype.draw里前面后端参加ctx.save()
和ctx.restore()来单独主宰小鸟画布

图片 70图片 71

Bird.prototype.draw = function (){
    this.ctx.save();
    this.ctx.translate(this.x ,this.y);  //坐标移动到小鸟的中心点上
    this.ctx.rotate((Math.PI /6) * this.speed / 0.3 );
    //小鸟最大旋转30度,并随着速度实时改变角度
    this.ctx.drawImage(
        this.img,52*this.index,0,52,45,
        -52/2,-45/2,52,45  //这里很重要的一点是,整个小鸟坐标系开始移动
    )
    this.ctx.restore();
}

投入小鸟旋转效果

当然最终不要遗忘对管道碰撞的论断,在此间再校正贰遍。

实则假如打算参与旋转效果,上2次的校对不须要,你会发现众多重复工。

末段做出的意义如下:

图片 72

 主体成效和逻辑已经整整完结。愈多的成效能够自行添加。

 假若想协调集会演练一下,请点击游戏细化部分的链接下载相关材质和成套源码。

相关文章