也来山寨一版Flappy Bird (js版)
随着Flappy Bird的火爆,各种实现的版也不断出现,于是也手痒简单实现了一版。
其实本来只是想实现一下这只笨鸟的飞翔运动的,后来没忍住,就直接实现一个完整游戏了……
因为这个游戏本身实现起来就没啥难度,这次就没用任何框架,也没搭就原生手写完了所有代码,不过只对高级浏览器支持,共花了1天半时间,不过至少有半天时间是在玩,这游戏玩起来确实停不下来。。。。。
飞翔的运动代码就是这样,利用了上抛运动公式,比起一般的缓动公式,这样比较有质感吧…
function value(y, h, t, g){ g = g || 0.98; function fn(v, t){ return v * t - 0.5 * g * t * t; } function v0(h){ return Math.sqrt(2 * h * g); } return y - fn(v0(h), t); }
可以在指定时刻获取当前的y坐标值,然后用该值进行对象的渲染(位置更新)就行了。
再说下水管地图的随机生成原理:在一个范围内生成随机数,这个数就是下半部水管的y坐标,然后以这个y坐标渲染下半部的水管,还是以这个y坐标减去上下水管的距离值,就可以得到上半部水管的最大y值,再根据这个y值渲染上半部的水管就行了,文字描述比较抽象,大家看完整代码就能明白了……
最后将水管序列进行位移,再跟这个二货鸟的矩形进行水管上下半部两个矩形的碰撞检测就行了。
好了,很简单的一个游戏和实现,就不多说了,这里是完整js代码和演(zi)示(nue)地址,代码没太去组织,有点乱,大家将就看吧。。。
function Timer(n){ var itvID; n = n || 1000 / 60; return { start : function(cb){ itvID = window.setInterval(cb, n); }, stop : function(){ window.clearInterval(itvID); } }; } function overlay(x1, y1, w1,h1, x2, y2, w2, h2){ return x2 < x1 + w1 && x2 + w2 > x1 && y2 < y1 + h1 && y2 + h2 > y1; } function value(y, h, t, g){ g = g || 0.98; function fn(v, t){ return v * t - 0.5 * g * t * t; } function v0(h){ return Math.sqrt(2 * h * g); } return y - fn(v0(h), t); } function rotate(nd, v){ nd.style.mozTransform = "rotate(" + v + "deg)"; nd.style.oTransform = "rotate(" + v + "deg)"; nd.style.webkitTransform = "rotate(" + v + "deg)"; nd.style.transform = "rotate(" + v + "deg)"; } function Pipe(sw, sh, pnd, y, d){ var et = pnd.cloneNode(true); var h = 42; var pb = et.querySelector("[data-node='bottomPipe']"); var pt = et.querySelector("[data-node='topPipe']"); var x = sw; var pbh; var pth; var speed = 5; var baseY; et.removeAttribute("id"); et.style.display=""; function updateY(y){ pbh = sh - y + h; pth = y - d - h; pb.style.top = y + "px"; pb.querySelector(".pipe").style.height = pbh + "px"; pt.querySelector(".pipe").style.height = pth + "px"; baseY = y; } updateY(y); return { entity : et, updateY : updateY, preX : function(){ return preX; }, x : function(v){ if(typeof v !== "undefined"){ preX = x; x = v; et.style.left = x + "px"; } return x; }, w : function(){ return 93; }, topH : function(){ return pth + h; }, bottomH : function(){ return pbh + h; }, baseY : function(){ return baseY; } } } function check(b, p){ var pipeDeltaH = 200; var v = 3; var pr1 = { x : p.x() + v, y : p.baseY() + v, w : p.w() - 2 * v, h : p.bottomH() + pipeDeltaH - 2 * v }; var pr2 = { x : p.x() + v, y : -pipeDeltaH - v, w : p.w() - 2 * v, h : p.topH() + pipeDeltaH - 2 * v }; return overlay(b.x, b.y, b.w, b.h, pr1.x, pr1.y, pr1.w, pr1.h) || overlay(b.x, b.y, b.w, b.h, pr2.x, pr2.y, pr2.w, pr2.h); } function checkScore(b, p, s){ if(b.x <= p.preX() && b.x > p.x()){ s.add(1); s.update(); } } function PipeManager(stage, sw, sh, bird, n, timer, cb){ var list=[]; var pcNd = document.getElementById("pipeContainer"); var d = 190; var y; var h = 42 * 2; var pipe; var timer; var speed = 3.5; var i; var right = 250; n = n || 3; function getMaxX(){ var i = list.length; var x = 0; while(i--){ list[i].x() > x && (x = list[i].x()); } return x; } function startHandle(){ var l = list.length; var p; for(i = 0; i< l; i++){ p = list[i]; p.x(p.x() - speed); if(p.x() <= -100){ p.updateY(Math.floor(Math.random() * (sh - 2 * h - d) + h + d)); p.x(getMaxX() + 300); } if(check(bird, p)){ cb && cb(); } checkScore(bird, p, Score); } } for(i = 0; i < n; i++){ y = Math.floor(Math.random() * (sh - 2 * h - d) + h + d); pipe = Pipe(sw, sh, pcNd, y, d); list.length ? pipe.x(getMaxX() + 300) : pipe.x(sw + right); list.push(pipe); stage.appendChild(pipe.entity); } return { reset : function(){ var l = list.length; var i; list[0].x(sw + right); for(i = 1; i < l; i++){ list[i].x(getMaxX() + 300); } timer.stop(); }, start : function(){ timer.start(startHandle); } } } var Bird = { x : 100, y : 300, w : 64, h : 45, fly : function(y, h, dt, btm, timer, cb){ var t = 0; var v = 0; var ag = 0; var agv = 4; var pv; var me = this; timer.stop(); timer.start(function(){ t += dt; v = value(y, h, t); if(v >= btm + 30){ me.died(timer, btm); } if(typeof pv !=="undefined"){ ag = Math.min(Math.max(ag + (v - pv) / agv, angleArr[0]), angleArr[1]); } cb(v, ag); pv=v; }); }, enabled : true, died : function(timer, btm){ var et = this.entity; var t = 0; rotate(et, 90); et.style.left = parseInt(et.style.left, 10) + 10 + "px"; et.style.top = parseInt(et.style.top, 10) + 20 + "px"; this.enabled = false; timer.stop(); pipeTimer.stop(); timer.start(function(){ var v; t += 0.5; v = value(parseInt(et.style.top, 10), 0, t, 0.7); if(v >= btm + 30){ timer.stop(); v = btm + 30; } et.style.top = v + "px"; }); state = "end"; }, reset : function(){ this.y = 300; this.x = 100; this.entity.style.left = this.x + "px"; this.entity.style.top = this.y + "px"; this.enabled = true; rotate(this.entity, 0); }, entity : document.getElementById("bird") }; var Score = { v : 0, entity : document.getElementById("score"), add : function(n){ this.v += n; }, update : function(){ this.entity.innerHTML = this.v; }, reset : function(){ this.v = 0; this.update(); } }; var Stage = { w : 520, h : 600, entity : document.getElementById("stage") } var angleArr=[-25, 90]; var birdTimer = Timer(); var pipeTimer = Timer(); var isStart = false; var state = "start"; var pm = PipeManager(Stage.entity, Stage.w, Stage.h, Bird, 3, pipeTimer, function(){ Bird.died(birdTimer, Stage.h); }); Bird.x = parseInt(Bird.entity.style.left, 10); window.onmousedown = function(evt){ var y; switch(state){ case "hold" : pm.reset(); Bird.reset(); Score.reset(); state = "start"; break; case "end" : state = ""; setTimeout(function(){ state = "hold"; },1000); break; case "start" : pm.start(); state = "play"; case "play" : y = parseInt(Bird.entity.style.top, 10); Bird.enabled && Bird.y >= 0 && Bird.fly(y, 100, 0.9, Stage.h, birdTimer, function(v, ag){ Bird.entity.style.top = v + "px"; Bird.y = v; rotate(Bird.entity, ag); }); } evt && evt.preventDefault(); window.event && (window.event.returnValue = false); };