闲扯游戏编程之html5篇--山寨版《flappy bird》源码
新年新气象,最近事情不多,继续闲暇学习记点随笔,欢迎拍砖。之前的〈简单游戏学编程语言python篇〉写的比较幼稚和粗糙,且告一段落。开启新的一篇关于javascript+html5的从零开始的学习。仍然以咱们有兴趣写的小游戏开始,〈flappy bird〉最近真是火的离谱,我也是昨天才开始找这个游戏试玩一下,果然难度不小,只能玩到33分了 ,哈哈。这游戏的评论网上已经铺天盖地了,这里不做过多评论,毕竟个人属于这个移动游戏圈子之外的。不过还是忍不住说一下,这游戏创意已经不算新颖,像素级的入门游戏精美度更是差上很多,开发难度也就是入门级的水平(相对来说)。不过作为菜鸟的门外汉来说,这游戏的设计思路和开发细节还是比较值得新手去研究和作为练手的案例研究一下。于是找到网上牛人放出的山寨版《flappy bird》之clumsy-bird,来简单研究一下源码吧,顺便从零学习一下canvas和Js一些东西,作为记录。
clumsy-bird的github地址为:https://github.com/ellisonleao/clumsy-bird
在线试玩地址:http://ellisonleao.github.io/clumsy-bird/(保证你的浏览器支持html5哟)
一、源码框架介绍
这个游戏呢,采用开源的html5游戏引擎melonJS作为框架,这个引擎比较轻量级,比较简单易懂。了解完源码整体框架就明白了整个引擎的框架了。
首先看一下游戏入口这里(game.js):大部分是框架相关的,非框架代码则是data的补充定义,用户按键事件绑定这些。
- var game = {
- data : {
- score : 0,
- timer: 0,
- start: false
- },
- "onload" : function () {
- if (!me.video.init("screen", 900, 600, true, 'auto')) {
- alert("Your browser does not support HTML5 canvas.");
- return;
- }
- me.audio.init("mp3,ogg");
- me.loader.onload = this.loaded.bind(this);
- me.loader.preload(game.resources);
- me.state.change(me.state.LOADING);
- },
- "loaded" : function () {
- me.state.set(me.state.MENU, new game.TitleScreen());
- me.state.set(me.state.PLAY, new game.PlayScreen());
- me.state.set(me.state.GAME_OVER, new game.GameOverScreen());
- me.state.transition("fade", "#000", 100);
- me.input.bindKey(me.input.KEY.SPACE, "fly", true);
- me.input.bindTouch(me.input.KEY.SPACE);
- me.state.change(me.state.MENU);
- }
- };
onload 预加载的game.resources主要是图片如下的一些素材。
从界面加载完"loaded"函数看起,有三个状态 Menu 对应game.TitleScreen()是我们的标题界面处理,PLAY是我们的game.PlayScreen(),这个就是游戏开始的相关部分,即我们主要研究的部分--screens/play.js
这里面主要继承重写了ScreenObject的init初始化函数onResetEvent状态改变函数 及刷新界面函数update。
init定义了主要的变量管道长度this.pipeHoleSize = 1240;左右相邻管道出现的间隔时间this.pipeFrequency = 92;
update函数中处理逻辑即每隔pipeFrequency生成上下两个管道和碰撞体(这个实际并不渲染,后面代码中实体的alpha渲染做透明出现,只作为碰撞检测用),两个管道的位置简单画一下应该可求出(pipe1是下管道,保证中间距离是100,且最短管道要保证有100)
- if (this.generate++ % this.pipeFrequency == 0){
- var posY = this.getRandomInt(
- me.video.getHeight() - 100,
- 200
- );
- var posY2 = posY - me.video.getHeight() - this.pipeHoleSize;
- var pipe1 = new me.entityPool.newInstanceOf("pipe", this.posX, posY);
- var pipe2 = new me.entityPool.newInstanceOf("pipe", this.posX, posY2);
- var hitPos = posY - 100;
- var hit = new me.entityPool.newInstanceOf("hit", this.posX, hitPos);
- pipe1.renderable.flipY();
- me.game.add(pipe1, 10);
- me.game.add(pipe2, 10);
- me.game.add(hit, 11);
- }
接下来是游戏界面状态处理函数onResetEvent
- me.input.bindKey(me.input.KEY.SPACE, "fly", true);
- //this.start = false;
- game.data.score = 0;
- game.data.timer = 0;
- game.data.start = false;
- me.game.add(new BackgroundLayer('bg', 1));
- var groundImage = me.loader.getImage('ground');
- this.ground = new me.SpriteObject(
- 0,
- me.video.getHeight() - groundImage.height,
- groundImage
- );
- me.game.add(this.ground, 11);
- this.HUD = new game.HUD.Container();
- me.game.world.addChild(this.HUD);
- me.entityPool.add("clumsy", BirdEntity);
- me.entityPool.add("pipe", PipeEntity, true);
- me.entityPool.add("hit", HitEntity, true);
- this.bird = me.entityPool.newInstanceOf("clumsy", 60,
- me.game.viewport.height/2 - 100);
- me.game.add(this.bird, 10);
- this.posX = me.game.viewport.width;
- //inputs
- me.input.bindMouse(me.input.mouse.LEFT, me.input.KEY.SPACE);
- me.state.transition("fade", "#fff", 100);
- this.getReady = new me.SpriteObject(
- me.video.getWidth()/2 - 200,
- me.video.getHeight()/2 - 100,
- me.loader.getImage('getready')
- );
- me.game.add(this.getReady, 11);
- var popOut = new me.Tween(this.getReady.pos).to({y: -132}, 2000)
- .easing(me.Tween.Easing.Linear.None)
- .onComplete(function(){ game.data.start = true;}).start();
- },
这里面主要完成界面背景层的加载,HUd作为分数显示,及重要游戏对象生成。
me.entityPool.add("clumsy", BirdEntity); 小鸟实体类
me.entityPool.add("pipe", PipeEntity, true); 管道实体类
me.entityPool.add("hit", HitEntity, true); 碰撞体类
this.bird = me.entityPool.newInstanceOf("clumsy", 60,
me.game.viewport.height/2 - 100);
me.game.add(this.bird, 10); 首先只有小鸟新实例对象生成,游戏正式开始才有管道等。EntityPool 就是作为引擎管理游戏中实例化对象而存在的。
二、游戏对象类的实现
这块者重介绍主重要的上面提到的那三个游戏对象类。(entities.js)
实现还是比较简单的,重写ObjectEntity重要的几个函数就行了。就是 init 和 update这两个函数,分别完成对象初始化和每帧的刷新。主要学习的是update里面逻辑的处理。这里主要介绍小鸟的处理,那两个基本上没多少代码处理。
- var BirdEntity = me.ObjectEntity.extend({
- init: function(x, y){
- var settings = {};
- settings.image = me.loader.getImage('clumsy');
- settings.spritewidth = 85;
- settings.spriteheight= 60;
- this.parent(x, y, settings);
- this.alwaysUpdate = true;
- this.gravity = 0.2;
- this.gravityForce = 0.01;
- this.maxAngleRotation = Number.prototype.degToRad(30);
- this.maxAngleRotationDown = Number.prototype.degToRad(90);
- this.renderable.addAnimation("flying", [0, 1, 2]);
- this.renderable.addAnimation("idle", [0]);
- this.renderable.setCurrentAnimation("flying");
- this.animationController = 0;
- this.updateColRect(10, 70, 2, 58);
- },
- update: function(x, y){
- // mechanics
- if (game.data.start) {
- if (me.input.isKeyPressed('fly')){
- this.gravityForce = 0.01;
- var currentPos = this.pos.y;
- tween = new me.Tween(this.pos).to({y: currentPos - 72}, 100);
- tween.easing(me.Tween.Easing.Exponential.InOut);
- tween.start();
- this.renderable.angle = -this.maxAngleRotation;
- }else{
- this.renderable.setCurrentAnimation("flying");
- this.gravityForce += 0.2;
- this.pos.add(new me.Vector2d(0, me.timer.tick * this.gravityForce));
- this.renderable.angle += Number.prototype.degToRad(3) * me.timer.tick;
- if (this.renderable.angle > this.maxAngleRotationDown)
- this.renderable.angle = this.maxAngleRotationDown;
- }
- }
- //manual animation
- var actual = this.renderable.getCurrentAnimationFrame();
- if (this.animationController++ % 2){
- actual++;
- this.renderable.setAnimationFrame(actual);
- }
- res = this.collide();
- var hitGround = me.game.viewport.height - (96 + 60);
- var hitSky = -80; // bird height + 20px
- if (res) {
- if (res.obj.type != 'hit'){
- me.state.change(me.state.GAME_OVER);
- return false;
- }
- me.game.remove(res.obj);
- game.data.timer++;
- return true;
- }else if (this.pos.y >= hitGround || this.pos.y <= hitSky){
- me.state.change(me.state.GAME_OVER);
- return false;
- }
- var updated = (this.vel.x != 0 || this.vel.y != 0);
- if (updated){
- this.parent();
- return true;
- }
- return false;
- },
- });
在小鸟初始化函数完成了动画帧的加载 三个序列动画[0,1,2]作为一次动画过程,
this.renderable.addAnimation("flying", [0, 1, 2]);this.renderable.addAnimation("idle", [0]);this.renderable.setCurrentAnimation("flying");
this.gravity = 0.2;this.gravityForce = 0.01;完成重力设定。
update函数中小鸟动画实现为
//manual animation
var actual = this.renderable.getCurrentAnimationFrame();
if (this.animationController++ % 2){
actual++;
this.renderable.setAnimationFrame(actual);
} 如上变实现小鸟的动画。
update函数主要功能除了上面,还主要负责完成用户按键处理和碰撞检测等处理逻辑:
当按下space键或者点击鼠标执行如下动作 (好像是利用tween增强了物理效果,具体没深究这里。)
this.gravityForce = 0.01;var currentPos = this.pos.y;
tween = new me.Tween(this.pos).to({y: currentPos - 72}, 100);
tween.easing(me.Tween.Easing.Exponential.InOut);
tween.start();
this.renderable.angle = -this.maxAngleRotation;
当没有按下相应键处理为
this.renderable.setCurrentAnimation("flying");
this.gravityForce += 0.2;
this.pos.add(new me.Vector2d(0, me.timer.tick * this.gravityForce));
this.renderable.angle += Number.prototype.degToRad(3) * me.timer.tick; 每帧增加0.2重力加速度值,并且y值每秒增加g*t位移,角度加大,就是实现小鸟下坠效果。
- res = this.collide();
- var hitGround = me.game.viewport.height - (96 + 60);
- var hitSky = -80; // bird height + 20px
- if (res) {
- if (res.obj.type != 'hit'){
- me.state.change(me.state.GAME_OVER);
- return false;
- }
- me.game.remove(res.obj);
- game.data.timer++;
- return true;
- }else if (this.pos.y >= hitGround || this.pos.y <= hitSky){
- me.state.change(me.state.GAME_OVER);
- return false;
- }
判断当前发生碰撞的对象是不是‘hit’,当不是之前的"hit"类型时说明发生碰撞对象改变,即小鸟撞管子上了 ,报游戏结束,并清理对象。
上面就是主要的代码逻辑了,感兴趣的话可以从git上fork一份自己研究一下,这里不做过多探讨了,闲扯之余顺便学习一下Js和html5也是收获。
以上素材及代码均来自网络,仅供学习研究,转载请注明出处,谢谢支持。
闲扯游戏编程之html5篇--山寨版《flappy bird》源码的更多相关文章
- Flappy Bird 源码走读
参考:https://github.com/kirualex/SprityBird 该项目基于spritekit,代码的结构很清楚,感觉用来学习spritekit非常不错. 1.项目只有一个viewC ...
- 【教程】HTML5+JavaScript编写flappy bird
作者: 风小锐 新浪微博ID:永远de风小锐 QQ:547953539 转载请注明出处 PS:新修复了两个bug,已下载代码的同学请查看一下 大学立即要毕业了. ...
- 8个前沿的 HTML5 & CSS3 效果【附源码下载】
作为一个前沿的 Web 开发者,对于 HTML5 和 CSS3 技术或多或少都有掌握.前几年这些新技术刚萌芽的时候,开发者们已经使用它们来小试牛刀了,如今这些先进技术已经遍地开发,特别是在移动端大显身 ...
- 网狐6603 cocos2dx 棋牌、捕鱼、休闲类游戏《李逵捕鱼》手机端完整源码分析及分享
该资源说明: cocos2d 棋牌.捕鱼.休闲类游戏<李逵捕鱼>手机端完整源码,网狐6603配套手机版源码,可以选桌子,适合新手学习参考,小编已亲测试,绝对完整可编译手机端,下载后将文件考 ...
- 8个超震撼的HTML5和纯CSS3动画源码
HTML5和CSS3之所以强大,不仅因为现在大量的浏览器的支持,更是因为它们已经越来越能满足现代开发的需要.Flash在几年之后肯定会消亡,那么HTML5和CSS3将会替代Flash.今天我们要给大家 ...
- 第五篇:白话tornado源码之褪去模板的外衣
上一篇<白话tornado源码之请求来了>介绍了客户端请求在tornado框架中的生命周期,其本质就是利用epoll和socket来获取并处理请求.在上一篇的内容中,我们只是给客户端返回了 ...
- 第三篇:白话tornado源码之请求来了
上一篇<白话tornado源码之待请求阶段>中介绍了tornado框架在客户端请求之前所做的准备(下图1.2部分),本质上就是创建了一个socket服务端,并进行了IP和端口的绑定,但是未 ...
- 让你心动的 HTML5 & CSS3 效果【附源码下载】
这里集合的这组 HTML5 & CSS3 效果,有的是网站开发中常用的.实用的功能,有的是先进的 Web 技术的应用演示.不管哪一种,这些案例中的技术都值得我们去探究和学习. 超炫的 HTML ...
- 动态方式破解apk进阶篇(IDA调试so源码)
动态方式破解apk进阶篇(IDA调试so源码) 来源 https://blog.csdn.net/qq_21051503/article/details/74907449 下面就说关于在IDA中And ...
随机推荐
- Python’s SQLAlchemy vs Other ORMs[转发 4]peewee
peewee peewee is a small, expressive ORM. Compared to other ORMs, peewee focuses on the principal of ...
- Python’s SQLAlchemy vs Other ORMs[转发 1]SQLObject
SQLObject SQLObject is a Python ORM that maps objects between a SQL database and Python. It is becom ...
- 使用 Sublime Text 2 开发 Unity3D 项目
用 Sublime 已经有很长一段时间,很舒适,很贴心,根本停不下来.之前因为是开发页游,所以是用 AS3 开发,近段时间,新开了个手游项目,引擎方面选定了 Unity3D,老实说,之前没有太多的 3 ...
- CSS轮廓outline
http://www.cnblogs.com/xiaohuochai/p/5277416.html
- java selenium (十四) 处理Iframe 中的元素
有时候我们定位元素的时候,发现怎么都定位不了. 这时候你需要查一查你要定位的元素是否在iframe里面 阅读目录 什么是iframe iframe 就是HTML 中,用于网页嵌套网页的. 一个网页可以 ...
- [原创]WPF应用MediaPlayer播放声音断续、不全解决方案
1.检查扬声器和驱动程序. 测试方法:首先,应用Windows Media Player播放器播放,看是否有问题,如果有问题,基本断定是驱动程序问题.其次,点击扬声器,选择测试,查看扬声器是否好用,如 ...
- 荣品四核4412开发板的USB摄像头问题
RP4412开发板是荣品电子研发的一款三星四核Exynos4412评估板开发板,支持WIFI+LAN上网.蓝牙4.0.4G上网.500万自动对焦摄像头.GPS.网卡.音频,1080P HDMI音视频同 ...
- web开发学习之Http协议
web入门 请求: request 浏览器向服务器发信息 响应:response 服务器回复浏览器 一个请求发出,一定有且仅有一个响应 http协议:对浏览器客户端余服务器的数据传输规范 ht ...
- TPS40305 ——开关电源芯片20160901
TPS4030X芯片共有3款,区别在于开关频率不同. 学习笔记: 1.当开关频率越高,所使用的电感的容量越小,电路越稳定. 2.对于mos管源极的地,尽量和mos管漏极的电源输入的地接近,并且与最终的 ...
- java多线程之 基本概念
一.线程的五种状态 1. 新建状态(New) : 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread().2. 就绪状态(Runnable) ...