也来山寨一版Flappy Bird (js版)
随着Flappy Bird的火爆,各种实现的版也不断出现,于是也手痒简单实现了一版。
其实本来只是想实现一下这只笨鸟的飞翔运动的,后来没忍住,就直接实现一个完整游戏了……
因为这个游戏本身实现起来就没啥难度,这次就没用任何框架,也没搭就原生手写完了所有代码,不过只对高级浏览器支持,共花了1天半时间,不过至少有半天时间是在玩,这游戏玩起来确实停不下来。。。。。
飞翔的运动代码就是这样,利用了上抛运动公式,比起一般的缓动公式,这样比较有质感吧…
1 2 3 4 5 6 7 8 9 10 11 | 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 ); }; |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?