用Phaser来制作一个html5游戏——flappy bird (二)

时间:2022-03-13 14:00:34

上一篇教程中我们完成了boot、preload、menu这三个state的制作,下面我们就要进入本游戏最核心的一个state的制作了。play这个state的代码比较多,我不会一一进行说明,只会把一些关键的东西挑出来说。

我们点击游戏菜单中的开始按钮后,首先出现的是这个画面:

用Phaser来制作一个html5游戏——flappy bird (二)

在第一部分的教程中,我们已经制作了一个游戏菜单的场景,这个页面也跟那个差不多,只不过这个页面去除了游戏标题和开始按钮,而多出了Get Ready以及提示点击屏幕来操作游戏的两张图片。在这个state里,我们需要启用物理引擎,并且要能够响应鼠标点击事件。

game.States.play = function(){
this.create = function(){
this.bg = game.add.tileSprite(0,0,game.width,game.height,'background');//背景图,这里先不用移动,游戏开始后再动
this.pipeGroup = game.add.group();//用于存放管道的组,后面会讲到
this.pipeGroup.enableBody = true;
this.ground = game.add.tileSprite(0,game.height-112,game.width,112,'ground'); //地板,这里先不用移动,游戏开始后再动
this.bird = game.add.sprite(50,150,'bird'); //鸟
this.bird.animations.add('fly');//添加动画
this.bird.animations.play('fly',12,true);//播放动画
this.bird.anchor.setTo(0.5, 0.5); //设置中心点
game.physics.enable(this.bird,Phaser.Physics.ARCADE); //开启鸟的物理系统
this.bird.body.gravity.y = 0; //鸟的重力,未开始游戏,先让重力为0,不然鸟会掉下来
game.physics.enable(this.ground,Phaser.Physics.ARCADE);//开启地面的物理系统
this.ground.body.immovable = true; //让地面在物理环境中固定不动 this.readyText = game.add.image(game.width/2, 40, 'ready_text'); //get ready 文字
this.playTip = game.add.image(game.width/2,300,'play_tip'); //提示点击屏幕的图片
this.readyText.anchor.setTo(0.5, 0);
this.playTip.anchor.setTo(0.5, 0); this.hasStarted = false; //游戏是否已开始
game.time.events.loop(900, this.generatePipes, this); //利用时钟事件来循环产生管道
game.time.events.stop(false); //先不要启动时钟
game.input.onDown.addOnce(this.statrGame, this); //点击屏幕后正式开始游戏
};
}

启用物理系统

默认的游戏中的每个对象的物理系统是关闭的,要启用一个对象的物理系统,可以使用 game.physics.enable() 方法

enable(object, system, debug)

object : 要开启物理系统的对象,可以是单个对象,也可以是一个包含多个对象的数组

system : 要启用的物理系统,默认为 Phaser.Physics.ARCADE,Phaser目前支持三种物理引擎,分别是Arcade ,P2 以及 Ninja。

debug : 是否开启调试

只有开启了对象的物理系统,该对象才具有物理特性,开启了物理系统后,对象的body属性指向该对象拥有的物理系统,所有与物理相关的属性或方法都必须在body上进行操作。

鼠标点击事件

Phaser中的鼠标、键盘、触摸等交互事件都统一由Input对象来处理。我们需要鼠标点击屏幕后进行响应,可以使用Input对象的onDown属性,该属性指向一个Phaser.Signal对象,我们可以在这个对象上绑定事件,每当鼠标按键下,就会触发一个onDown的信号,如果这个onDown信号对象上绑定了事件,那么这些事件就会执行。例如:

var input = game.input; //当前游戏的input对象
var signal = input.onDown; //鼠标按下时的 Signal对象
signal.add(function(){}); //给Signal 绑定事件处理函数
signal.add(function(){}); //再绑定一个
signal.addOnce(function(){}); //绑定一个只会执行一次的事件函数

时钟对象

有时我们需要定时或者每隔一段时间就执行一段代码,在原生js中我们可以通过setTimeout和setInterval来实现。Phaser给我们提供了功能更强大的Timer对象来实现这些功能。Timer对象主要有以下几个方法:

loop(delay, callback, callbackContext, arguments); //以指定的时间间隔无限重复执行某一个函数,直到调用了Timer对象的stop()方法才停止

repeat(delay, repeatCount, callback, callbackContext, arguments); //让某个函数重复执行,可以指定重复的次数

当前的Timer对象我们可以通过 game.time.events 来得到,在调用了Timer对象的loop或repeat方法后,还必须调用start方法来启动。但是我使用的Phaser 2.0.4 版本,好像不调用start方法,loop方法就自动起作用了,不知道这是不是一个bug。如上面代码中我们用到的:

game.time.events.loop(900, this.generatePipes, this); //利用时钟对象来重复产生管道
game.time.events.stop(false); //先让他停止,因为即使没调用start方法,它也会自动启动,这应该是一个bug

当点击屏幕后,就可以正式开始游戏了,我们来看看点击屏幕事件绑定的 this.startGame 函数做了什么。

this.statrGame = function(){
this.gameSpeed = 200; //游戏速度
this.gameIsOver = false; //游戏是否已结束的标志
this.hasHitGround = false; //是否已碰撞到地面的标志
this.hasStarted = true; //游戏是否已经开始的标志
this.score = 0; //初始得分
this.bg.autoScroll(-(this.gameSpeed/10),0); //让背景开始移动
this.ground.autoScroll(-this.gameSpeed,0); //让地面开始移动
this.bird.body.gravity.y = 1150; //给鸟设一个重力
this.readyText.destroy(); //去除 'get ready' 图片
this.playTip.destroy(); //去除 '玩法提示 图片
game.input.onDown.add(this.fly, this); //给鼠标按下事件绑定鸟的飞翔动作
game.time.events.start(); //启动时钟事件,开始制造管道
}

我们再来看下鸟的飞翔动作,它由 this.fly 函数来实现

this.fly = function(){
this.bird.body.velocity.y = -350; //飞翔,实质上就是给鸟设一个向上的速度
game.add.tween(this.bird).to({angle:-30}, 100, null, true, 0, 0, false); //上升时头朝上的动画
this.soundFly.play(); //播放飞翔的音效
}

重力和速度

Phaser.Physics.Arcade.Body 对象,也就是当你是用arcade物理引擎时 sprite.body 所指向的对象,拥有很多跟物理相关的属性和方法。其中的 gravity 对象代表重力,它有x和y两个属性,分别代表水平方向和垂直方向的重力。我们可以使用它的 setTo(x,y)方法来同事设置两个方向的重力。设置了重力的物体,它的运动会受到重力的影响,与真实生活中的物理现象是一致的。然后这个body它还有一个 velocity 对象,表示物体的速度,跟重力一样,都分水平和垂直两个方向,也可以用setTo(x,y)方法来设置。一旦给物体设置了合适的速度,它便能动了。

管道的生成

下面再来看一看管道生成函数 this.generatePipes

this.generatePipes = function(gap){ //制造一组上下的管道
gap = gap || 100; //上下管道之间的间隙宽度
var position = (505 - 320 - gap) + Math.floor((505 - 112 - 30 - gap - 505 + 320 + gap) * Math.random());//计算出一个上下管道之间的间隙的随机位置
var topPipeY = position-360; //上方管道的位置
var bottomPipeY = position+gap; //下方管道的位置 if(this.resetPipe(topPipeY,bottomPipeY)) return; //如果有出了边界的管道,则重置他们,不再制造新的管道了,达到循环利用的目的 var topPipe = game.add.sprite(game.width, topPipeY, 'pipe', 0, this.pipeGroup); //上方的管道
var bottomPipe = game.add.sprite(game.width, bottomPipeY, 'pipe', 1, this.pipeGroup); //下方的管道
this.pipeGroup.setAll('checkWorldBounds',true); //边界检测
this.pipeGroup.setAll('outOfBoundsKill',true); //出边界后自动kill
this.pipeGroup.setAll('body.velocity.x', -this.gameSpeed); //设置管道运动的速度
}

管道生成的思路:利用随机数计算出上下管道的位置,然后检查当前是否有管道已经出了边界,如果有,则重置出了边界的那组管道的位置,如果没有,则生成一组新的管道,这样就能避免内存浪费了。所有管道我们都把它放在一个组中,便于集中管理。这里需要掌握的是sprite对象的reset方法:

reset(x, y, health)

这个方法能重置sprite对象的位置,更重要的是,如果在一个已经被杀死了(kill)的sprite对象上执行该方法,那么该sprite的 alive, exists, visible and renderable 等属性都会变回为true。在需要重复利用已经存在的sprite对象时,经常要使用该方法。看下我们这个游戏中是怎么使用这个方法的:

this.resetPipe = function(topPipeY,bottomPipeY){//重置出了边界的管道,做到回收利用
var i = 0;
this.pipeGroup.forEachDead(function(pipe){ //对组调用forEachDead方法来获取那些已经出了边界,也就是“死亡”了的对象
if(pipe.y<=0){ //是上方的管道
pipe.reset(game.width, topPipeY); //重置到初始位置
pipe.hasScored = false; //重置为未得分
}else{//是下方的管道
pipe.reset(game.width, bottomPipeY); //重置到初始位置
}
pipe.body.velocity.x = -this.gameSpeed; //设置管道速度
i++;
}, this);
return i == 2; //如果 i==2 代表有一组管道已经出了边界,可以回收这组管道了
}

碰撞检测

好了,管道和鸟都已经有了,而且它们都能动了,接下来就是,实现鸟撞到管道或地面后游戏结束的功能了。

在Arcade物理引擎中,碰撞检测主要用到两个函数,一个是collide,还有一个是overlap

collide方法与overlap的区别在于collide会影响两个要检测的对象之间的物理状态,比如使用collide函数去检测两个物体,如果物体碰撞了,那么这两个物体之间就会有力的相互作用,可能其中一个会被另一个弹开,或者两个之间相互弹开。但如果使用overlap方法的话,则只会检测两个物体是否已经碰撞了,或者说已经重叠了,并不会产生物理作用,显然,如果只需要知道两个物体是否已经重叠了的话,overlap性能会更好。

碰撞检测可以单个对象与单个对象进行检测、单个对象与组进行检测、组与组进行检测。collide方法必须在每一帧中都进行调用,才能产生碰撞后的物理作用。

this.update = function(){ //每一帧中都要执行的代码可以写在update方法中
if(!this.hasStarted) return; //游戏未开始,先不执行任何东西
game.physics.arcade.collide(this.bird,this.ground, this.hitGround, null, this); //检测与地面的碰撞
game.physics.arcade.overlap(this.bird, this.pipeGroup, this.hitPipe, null, this); //检测与管道的碰撞
if(this.bird.angle < 90) this.bird.angle += 2.5; //下降时鸟的头朝下的动画
this.pipeGroup.forEachExists(this.checkScore,this); //分数检测和更新
}

分数管理

当鸟飞过一组管道后,就得1分。飞过一组管道,是指这组管道已经在鸟的左边的,所以可以通过管道的x坐标来判断是否已经得分。

this.checkScore = function(pipe){//负责分数的检测和更新,pipe表示待检测的管道
//pipe.hasScored 属性用来标识该管道是否已经得过分
//pipe.y<0是指一组管道中的上面那个管道,一组管道中我们只需要检测一个就行了
//当管道的x坐标 加上管道的宽度小于鸟的x坐标的时候,就表示已经飞过了管道,可以得分了
if(!pipe.hasScored && pipe.y<=0 && pipe.x<=this.bird.x-17-54){
pipe.hasScored = true; //标识为已经得过分
this.scoreText.text = ++this.score; //更新分数的显示
this.soundScore.play(); //得分的音效
return true;
}
return false;
}

只需要在每一帧中对每一个管道都调用一次该函数,就可以了。

声音的播放

在Phaser中播放一段声音很简单,只需要事先加载好声音资源。然后调用play方法播放就行了。

首先使用 game.load.audio() 来加载声音资源。我们以本游戏中得分时播放的声音为例,在state的preload方法中预先加载声音资源

game.load.audio('score_sound', 'assets/score.wav');//得分的音效

然后通过 game.add.sound() 来得到一个sound对象

this.soundScore = game.add.sound('score_sound');

sound对象有许多方法用来控制声音的播放暂停等,要播放声音,只需要调用它的play方法即可。

this.soundScore.play(); //播放声音

好了,把这些组合起来,就能做出我们的flappy bird游戏了。我所说的这些都只是些皮毛,是想让大家对用Pharse来做游戏有个最初步的印象,也许你还有许多不明白的地方,pharse是个功能很强大的html5游戏框架,想要掌握它,还是必须多看文档,多看官方给出的例子,然后自己动手去实践,一步一步一点一滴的去学习,去积累。苹果新发布的ios8中对webgl的支持已经大大加强了,无论是在safari浏览器还是webView运行html5游戏,性能都相当好,这也是移动设备发展的一个趋势,所以掌握一个html5游戏框架,无论是自娱自乐,或是对自己能力的提升,甚至是找工作,都是有一定的益处的。

第一部分教程:

用Phaser来制作一个html5游戏——flappy bird (一)

用Phaser来制作一个html5游戏——flappy bird (二)的更多相关文章

  1. 用Phaser来制作一个html5游戏——flappy bird (一)

    Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如何用Phaser来制作一个前段时间很火爆的游戏:Flappy Bird,希 ...

  2. Phaser开源2d引擎 html5游戏框架中文简介

    功能特点(Features) 易维护代码(Easy Asset Loading) Phaser可以加载图片,音频文件,数据文件,文本文件和自动解析精灵图和纹理地图集数据(出口纹理封隔器或Flash C ...

  3. Unity3D游戏开发从零单排&lpar;四&rpar; - 制作一个iOS游戏

    提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...

  4. 技术宅之flappy bird 二逼鸟

    师雪坤和刘阳 风靡一时的虐心小游戏<Flappy Bird>,以玩法简单.难度超高著称,不过,最近这款让全世界玩家几欲怒摔手机的游戏,被两位中国技术宅设计的"玩鸟机器人&quot ...

  5. three&period;js cannon&period;js物理引擎制作一个保龄球游戏

    关于cannon.js我们已经学习了一些知识,今天郭先生就使用已学的cannon.js物理引擎的知识配合three基础知识来做一个保龄球小游戏,效果如下图,在线案例请点击博客原文. 我们需要掌握的技能 ...

  6. 笔记:利用 Cocos2dx 3&period;2 与 Box2D制作一个跑酷游戏(上)

    最近写lua写得没有力气了,所以想让脑袋放松一下,刚好看到有人在用swift做游戏: Swift游戏实战-跑酷熊猫 于是脑子一短路,就想到了利用这些素材来做一个游戏. 本来不想记笔记的,但是由于选择物 ...

  7. 用原生js写小游戏--Flappy Bird

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 如何用angularjs制作一个完整的表格之二&lowbar;&lowbar;表格分页功能

    接上一次,这次主要介绍表格分页功能,由于项目需要这个案例是关于前端分页的方式,现在很少会这么用了,但如有需要可以参考其中的思路 html: 1.通过UL来展示页标,其中每个页标的li是通过异步加载从获 ...

  9. Phaser开源2d引擎 javascript&sol;html5游戏框架

    功能特点(Features) 易维护代码(Easy Asset Loading) Phaser可以加载图片,音频文件,数据文件,文本文件和自动解析精灵图和纹理地图集数据(出口纹理封隔器或Flash C ...

随机推荐

  1. Python模块之configpraser

    Python模块之configpraser   一. configpraser简介 用于处理特定格式的文件,其本质还是利用open来操作文件. 配置文件的格式: 使用"[]"内包含 ...

  2. 使用MAT&lpar;Memory Analyzer Tool&rpar;工具分析dump文件--转

    原文地址:http://gao-xianglong.iteye.com/blog/2173140?utm_source=tuicool&utm_medium=referral 前言 生产环境中 ...

  3. 【vs2010调试】当前不会命中断点 源代码与原始版本不同

    解决方案:全选CPP文件内容,选择 “编辑”-“高级”-“设置选定内容的格式”,保存,重新编译.

  4. SoapUI接口测试之JDBC(三)

    JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口 ...

  5. 《javascript高级程序设计》读书笔记1

    第二章 在HTML中引用javascript 1.<script>标签的位置:为了避免加载过多的JavaScript的脚本导致浏览器窗口一片空白.现代的web程序一般都把全部的 JavaS ...

  6. manjaro备忘录

    updated 2018/4/3 manjaro 使用Linux发行版时需要注意几个方面的问题: 包管理器 包管理器无疑时各家发行版的最大特色之一.软件同时也是一个平台是否能够产生足够的吸引力的来源之 ...

  7. jqGrid 常用 总结 -2

    这次的总结是针对于一次bug,先说下我们遇到的问题,就是后台人员告诉我们添加数据到100条数据的时候,101条就看不到,当时我觉得就是没有分页的原因,所以我就以为在jqgrid中设置一个loadonc ...

  8. source map 的原理探究

    线上产品代码一般是编译过的,前端的编译处理过程包括不限于 转译器/Transpilers (Babel, Traceur) 编译器/Compilers (Closure Compiler, TypeS ...

  9. 我发起并创立了一个 EPWA 的 开源项目

    EPWA ,  是  Easy PWA  的 意思, PWA 取自于 Google 的 PWA, EPWA   是一个用   C#  Cef  Html  js  css   开发 桌面程序 的 架构 ...

  10. NFine框架全选checkBox列错位

    在jqgrid.css里找到 .ui-jqgrid .cbox{margin-left: -1px;position: initial;vertical-align: text-bottom;}.ui ...