0

0

学会这7个步骤 制作HTML5手游就简单啦_html/css_WEB-ITnose

php中文网

php中文网

发布时间:2016-06-21 08:49:02

|

1673人浏览过

|

来源于php中文网

原创

想用html5制作跨平台的手机游戏?不必理会java或objective-c?也不用管应用商店?听起来真不可思议!

现在有许多游戏开发者都在挖掘手机HTML手机游戏的潜能。如《Nutmeg》和《Lunch Bug》就是优秀的案例。HTML5游戏的优势在于,使用相同的代码就能让游戏在手机和电脑上运行得一样好。这是否意味着HTML5能够让游戏代码编写成为一件一劳永逸的事?

准备

在你开始编写你自己的“神庙逃亡”或“愤怒的小鸟”以前,以下几点可能会浇灭你的创作热情:

表现:

一般说来,手机浏览器的JavaScript引擎表现并不出众。尽管从iOS 6和Chrome的Android测试版的情况上看,它进步得很快。

立即学习前端免费学习笔记(深入)”;

分辨率:

Android设备的分辨率已经五花八门了,更别说iPhone 4和iPad 3的分辨率和像素密度也在不断提高。

声音:

但愿你不介意没有声音的游戏——手机浏览器的声音支持很差。延迟是主要问题,大部分设备只支持一种声道。iOS甚至要等到用户主动开启才会加载声音。

现在,作为网页开发者的你已经习惯于处理浏览器的这些缺陷。所以,一些技术问题是难不倒你的,对吧?另外,这些表现和声音问题都是暂时的。毕竟手机浏览器进步飞快,这些问题很快就会变成历史。

在本教程中,你将通过制作一款比较简单的游戏来了解这些基本问题以及解决办法。

iphone(from smashingmagazine)

这是一款相当简单的游戏:玩家要做的就是点击从屏幕底部浮起来的白色圆形,不要让它们通过。你可以把这些白色圆形想象成漂浮上升的泡泡,你要在它们飞上天以前刺破它们。所以,我把这款小游戏叫作《POP》。

我们的制作过程可以分成如下7个步骤:

1、设置视图,以适合大多数手机屏幕的尺寸;

2、使用canvas API在屏幕上绘制图形;

3、捕捉触击事件;

4、制作基本的游戏循环;

5、引入游戏“实体”;

6、添加碰撞检测和一些简单的数学计算;

7、修饰外观,即添加少量特效。

_ueditor_page_break_tag_

1、设置视图

背景故事就随便了。

正如前面提到的,不同的设备具有不同的分辨率和像素密度。这意味着我们必须调整画布以适应不同的视图。这就可能损失游戏的外观质量,但我们有一个小技巧,也就是先使用小画布,然后按比例放大,这么做后的画面效果就好多了。

我们先写一段基本的HTML代码:

              

这个meta视图标签的作用是,命令浏览器禁止用户缩放画面,以及按完全尺寸渲染,而不是收缩页面。随后的带apple-前缀的meta标签允许游戏被加入书签。在iPhone上,加入书签的应用不会显示页面底部的在工具条上,因此节省了大片空间。

看看以下代码:

// namespace our game var POP = { // set up some initial values WIDTH: 320, HEIGHT:  480, // we’ll set the rest of these // in the init function RATIO:  null, currentWidth:  null, currentHeight:  null, canvas: null, ctx:  null, init: function() { // the proportion of width to height POPPOP.RATIO = POP.WIDTH / POP.HEIGHT; // these will change when the screen is resized POPPOP.currentWidth = POP.WIDTH; POPPOP.currentHeight = POP.HEIGHT; // this is our canvas element POP.canvas = document.getElementsByTagName(‘canvas’)[0]; // setting this is important // otherwise the browser will // default to 320 x 200 POPPOP.canvas.width = POP.WIDTH; POPPOP.canvas.height = POP.HEIGHT; // the canvas context enables us to // interact with the canvas api POPPOP.ctx = POP.canvas.getContext(’2d’); // we’re ready to resize POP.resize(); }, resize: function() { POP.currentHeight = window.innerHeight; // resize the width in proportion // to the new height POPPOP.currentWidth = POP.currentHeight * POP.RATIO; // this will create some extra space on the // page, allowing us to scroll past // the address bar, thus hiding it. if (POP.android || POP.ios) { document.body.style.height = (window.innerHeight + 50) + ‘px’; } // set the new canvas style width and height // note: our canvas is still 320 x 480, but // we’re essentially scaling it with CSS POPPOP.canvas.style.width = POP.currentWidth + ‘px’; POPPOP.canvas.style.height = POP.currentHeight + ‘px’; // we use a timeout here because some mobile // browsers don’t fire if there is not // a short delay window.setTimeout(function() { window.scrollTo(0,1); }, 1); } }; window.addEventListener(‘load’, POP.init, false); window.addEventListener(‘resize’, POP.resize, false);

首先,我们要给游戏创建一个命名空间“POP”。优秀的开发者不会污染整个命名空间的。比较好的做法是在程序的开头部分就声明所有变量。大部分变量 是很容易理解的:canvas表示HTML中的canvas元素;ctx使我们可以通过JavaScript canvas API来访问它。

在POP.init,我们获得canvas元素的引用,调整canvas元素的尺寸为480 × 320。resize函数会在调整大小和加载事件时启用,从而按比例调整canvas的style属性(游戏邦注:高度和宽度)。实际上,canvas仍 然是相同的尺寸,只是已经通过CSS放大了。试一试调整你的浏览器大小,看看canvas的缩放效果。

如果你在你的手机上实验,你会发现地址栏仍然可见。解决这个问题的做法是:对文件添加额外像素,然后向下滚动以隐藏地址栏,如下:

// we need to sniff out Android and iOS // so that we can hide the address bar in // our resize function POP.ua = navigator.userAgent.toLowerCase(); POPPOP.android = POP.ua.indexOf(‘android’) > -1 ? true : false; POP.ios = ( POP.ua.indexOf(‘iphone’) > -1 || POP.ua.indexOf(‘ipad’) > -1  ) ? true : false;

以上代码搜索userAgent,如果存在则标记。在调用POP.resize()以前,把它添加到POP.init后面。

然后,在resize函数中,如果android或ios为true,我们就把文件的高度再增加50像素——这就足以隐藏地址栏了。

// this will create some extra space on the // page, enabling us to scroll past // the address bar, thus hiding it. if (POP.android || POP.ios) { document.body.style.height = (window.innerHeight + 50) + ‘px’; }

注意,我们的做法只适用于Android和iOS设备;否则,讨厌的滚动条就会出现。另外,我们必须延迟scrollTo,以确保Safari设备不会忽略它。

2、在画布上绘制

我们已经根据视图调整好画布了,接下来我们该在上面画点什么了。

注:在本教程中,我们只使用基本的几何形状。iOS 5和Chrome的Android测试版可以用很高的帧率处理大量子画面。在Android 3.2或以下版本中实验一下,你会发现前者的帧率确实大大提高了。幸运地是,绘制圆形并不需要占用太多内存,所以我们的游戏中可以大量使用圆形,即使是在 老设备上,表现也不会太差。

以下,我们已经添加了一个基本的Draw对象,使我们可以清除屏幕,绘制矩形和圆形,然后添加文本。

// abstracts various canvas operations into // standalone functions POP.Draw = { clear: function() { POP.ctx.clearRect(0, 0, POP.WIDTH, POP.HEIGHT); }, rect: function(x, y, w, h, col) { POP.ctx.fillStyle = col; POP.ctx.fillRect(x, y, w, h); }, circle: function(x, y, r, col) { POP.ctx.fillStyle = col; POP.ctx.beginPath(); POP.ctx.arc(x + 5, y + 5, r, 0,  Math.PI * 2, true); POP.ctx.closePath(); POP.ctx.fill(); }, text: function(string, x, y, size, col) { POP.ctx.font = ‘bold ‘+size+’px Monospace’; POP.ctx.fillStyle = col; POP.ctx.fillText(string, x, y); } };

我们的Draw对象有清除屏幕和绘制矩形、圆形及文本的方法。抽象这些操作的好处是,我们不必记忆确切的canvas API调用,而且绘制圆形的代码简单到只有一句。

代码如下:

// include this at the end of POP.init function POP.Draw.clear(); POP.Draw.rect(120,120,150,150, ‘green’); POP.Draw.circle(100, 100, 50, ‘rgba(255,0,0,0.5)’); POP.Draw.text(‘Hello World’, 100, 100, 10, ‘#000′);

把上述代码放在POP.init函数之后。你应该可以看到画布上绘制出许多图形。

3、触击事件

与click事件一样,手机浏览器有捕捉触击事件的方法。

以下代码的重点是touchstart、touchmove和touchend事件。对于标准的click事件,我们可以从e.pageX的 e.pageY中获得座标。触击事件则稍有不同,它们有一个touches集合,其中的各个元素都包含触击座标和其他数据。我们只想要第一次触击,所以我 们要设置一个e.touches[0]。

注:只有版本4以后, Android才支持访问多次触击动作的JavaScript。

当禁用滚动、缩放和其他会中断游戏的活动时,我们还要调用e.preventDefault(); 。

添加以下代码到POP.init函数:

// listen for clicks window.addEventListener(‘click’, function(e) { e.preventDefault(); POP.Input.set(e); }, false); // listen for touches window.addEventListener(‘touchstart’, function(e) { e.preventDefault(); // the event object has an array // named touches; we just want // the first touch POP.Input.set(e.touches[0]); }, false); window.addEventListener(‘touchmove’, function(e) { // we’re not interested in this, // but prevent default behaviour // so the screen doesn’t scroll // or zoom e.preventDefault(); }, false); window.addEventListener(‘touchend’, function(e) { // as above e.preventDefault(); }, false); 你可能已注意到,以上代码把事件数据传输给Input对象。但我们现在要先定义一下它: // + add this at the bottom of your code, // before the window.addEventListeners POP.Input = { x: 0, y: 0, tapped :false, set: function(data) { this.x = data.pageX; this.y = data.pageY; this.tapped = true; POP.Draw.circle(this.x, this.y, 10, ‘red’); } };

现在,测试一下。圆形没有出现。这是为什么?有了!因为我们已经缩放画布了,当映射触击到屏幕的位置时,我们必须考虑到这一点。

首先,我们必须从座标中扣除偏移值。

var offsetTop = POP.canvas.offsetTop, offsetLeft = POP.canvas.offsetLeft; this.x = data.pageX – offsetLeft; this.y = data.pageY – offsetTop;

offset_diagram(from smashingmagazine)

然后,考虑到画布已经缩放过了,我们得计算一下实际画布(游戏邦注:仍然是320 × 480)。

var offsetTop = POP.canvas.offsetTop, offsetLeft = POP.canvas.offsetLeft; scale = POP.currentWidth / POP.WIDTH; this.x = ( data.pageX – offsetLeft ) / scale; this.y = ( data.pageY – offsetTop ) / scale;

scaled_canvas_diagram(from smashingmagazine)

你开始觉得头疼了吧?那我就给你举个例子。想象一下玩家轻击500 × 750的画布上的座标400,400。我们必须调整这个座标,因为画布的实际尺寸是480 × 320。所以,真正的X座标是400除以比例,即400 ÷ 1.56 = 320.5。

我们当然不是在每一个触击事件发生时计算,而是在调整完画布尺寸后计算座标。在程序的开头部分添加如下代码,以及其他变量声明:

// let’s keep track of scale // along with all initial declarations // at the start of the program scale:  1, // the position of the canvas // in relation to the screen offset = {top: 0, left: 0},

在我们的调整大小函数中,调整画布的宽高后,我们要记录一下当前的尺寸和偏移量:

魔珐星云
魔珐星云

无需昂贵GPU,一键解锁超写实/二次元等多风格3D数字人,跨端适配千万级并发的具身智能平台。

下载
// add this to the resize function. POPPOP.scale = POP.currentWidth / POP.WIDTH; POPPOP.offset.top = POP.canvas.offsetTop; POPPOP.offset.left = POP.canvas.offsetLeft;

现在,我们可以在POP.Input类的set方法中使用它们了:

this.x = (data.pageX – POP.offset.left) / POP.scale; this.y = (data.pageY – POP.offset.top) / POP.scale;

_ueditor_page_break_tag_

4、循环

典型的游戏循环如下:

1、用户输入,

2、更新和处理碰撞,

3、在屏幕上渲染,

4、重复。

我们当然可以使用setInterval,但在requestAnimationFrame中有一个新玩意儿。它能保证动画更流畅,并且能节省电池量。不幸地是,并非所有浏览器都支持它。但Paul Irish已经想到一个方便的解决办法。

我们也可以借鉴他的办法,代码如下:

// http://paulirish.com/2011/requestanimationframe-for-smart-animating // shim layer with setTimeout fallback window.requestAnimFrame = (function(){ return  window.requestAnimationFrame       || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame    || window.oRequestAnimationFrame      || window.msRequestAnimationFrame     || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })();

接着我们来制作初步的游戏循环:

// Add this at the end of POP.init; // it will then repeat continuously POP.loop(); // Add the following functions after POP.init: // this is where all entities will be moved // and checked for collisions, etc. update: function() { }, // this is where we draw all the entities render: function() { POP.Draw.clear(); }, // the actual loop // requests animation frame, // then proceeds to update // and render loop: function() { requestAnimFrame( POP.loop ); POP.update(); POP.render(); }

我们在POP.init之后调用这个循环。POP.loop接着调用我们的POP.update和POP.render方法。 requestAnimFrame保证这个循环被再次调用,最好是以60帧每秒的速度。注意,我们不必担心查看循环中的输入,因为我们已经在注意通过 POP.Input类可以访问到的触击和点击事件。

现在的问题是,我们的最后一次触击在屏幕上消失得太快了。我们必须想办法让屏幕更好地记忆和显示触击位置。

5、触击

首先,我们添加一个实体集合。这个集合包含游戏中出现的所有触击点、泡泡、粒子和其他动态物品。

// put this at start of programentities: [],

我们来做一个Touch类,它将在接触点处绘制一个圆点,这个圆点之后会慢慢消褪。

POP.Touch = function(x, y) { this.type = ‘touch’;    // we’ll need this later this.x = x;             // the x coordinate this.y = y;             // the y coordinate this.r = 5;             // the radius this.opacity = 1;       // initial opacity; the dot will fade out this.fade = 0.05;       // amount by which to fade on each game tick this.remove = false;    // flag for removing this entity. POP.update // will take care of this this.update = function() { // reduce the opacity accordingly this.opacity -= this.fade; // if opacity if 0 or less, flag for removal this.remove = (this.opacity < 0) ? true : false; }; this.render = function() { POP.Draw.circle(this.x, this.y, this.r, ‘rgba(255,0,0,’+this.opacity+’)'); }; };

Touch类具有一系列属性。x和y座标是参数,半径this.r为5像素,初始不透明度为1,触击点消裉速率为0.05,remove标记告诉主游戏循环是否将触击圆点从该实体集合中移除。

关键是,这个类有两个主要方法:update和render。我们将从游戏循环的相应部分调用它们。

我们先在游戏循环中刷出一个新的Touch实例,再通过update方法移除:

// POP.update function update: function() { var i; // spawn a new instance of Touch // if the user has tapped the screen if (POP.Input.tapped) { POP.entities.push(new POP.Touch(POP.Input.x, POP.Input.y)); // set tapped back to false // to avoid spawning a new touch // in the next cycle POP.Input.tapped = false; } // cycle through all entities and update as necessary for (i = 0; i < POP.entities.length; i += 1) { POP.entities[i].update(); // delete from array if remove property // flag is set to true if (POP.entities[i].remove) { POP.entities.splice(i, 1); } } },

基本上,如果POP.Input.tapped是true,那么我们就添加一个新的POP.Touch实例到我们的实体集合。循环这个实体集合,即调用各个实体的update方法。最后,如果实体被标记为移除,它就会从该集合中删除。

接着,我们在POP.render函数中渲染它们。

// POP.render function render: function() { var i; POP.Draw.rect(0, 0, POP.WIDTH, POP.HEIGHT, ‘#036′); // cycle through all entities and render to canvas for (i = 0; i < POP.entities.length; i += 1) { POP.entities[i].render(); } },

类似于update函数,循环实体和调用它们的render方法,在屏幕上绘制它们。

到目前为止,一切进展顺利。现在我们要添加Bubble类,它的作用是生产漂浮上升的泡泡。

POP.Bubble = function() { this.type = ‘bubble’; this.x = 100; this.r = 5;                // the radius of the bubble this.y = POP.HEIGHT + 100; // make sure it starts off screen this.remove = false; this.update = function() { // move up the screen by 1 pixel this.y -= 1; // if off screen, flag for removal if (this.y < -10) { this.remove = true; } }; this.render = function() { POP.Draw.circle(this.x, this.y, this.r, ‘rgba(255,255,255,1)’); }; };

POP.Bubble类非常接近于Touch类,主要的区别是它并不是像触击点一样消褪,而是向上移动。这个活动是通过在update函数中改变y位置即 this.y实现的。这里,我们也要查看泡泡是否离开屏幕;如果是,我们就要把它标记为移除。

注:我们已经制作了基本Entity类,Touch和Bubble都包含在内。但是,我现在不想比较JavaScript原型的继承和类。

// Add at the start of the program // the amount of game ticks until // we spawn a bubble nextBubble: 100, // at the start of POP.update // decrease our nextBubble counter POP.nextBubble -= 1; // if the counter is less than zero if (POP.nextBubble < 0) { // put a new instance of bubble into our entities array POP.entities.push(new POP.Bubble()); // reset the counter with a random value POP.nextBubble = ( Math.random() * 100 ) + 100; }

以上,我们已经为游戏循环添加了随机计时器。游戏循环会在随机位置刷出Bubble实例。在游戏开始时,我们设置nextBubble(下一个泡泡)的出现间隔为100,即当100减少到0时,游戏就会刷出新泡泡,并重置nextBubble计数器。

_ueditor_page_break_tag_

6、整合

首先,我们还没使用到任何碰撞检测。我们可以用简单地函数实现它。

// this function checks if two circles overlap POP.collides = function(a, b) { var distance_squared = ( ((a.x – b.x) * (a.x – b.x)) + ((a.y – b.y) * (a.y – b.y))); var radii_squared = (a.r + b.r) * (a.r + b.r); if (distance_squared < radii_squared) { return true; } else { return false; } }; // at the start of POP.update, we set a flag for checking collisions var i, checkCollision = false; // we only need to check for a collision // if the user tapped on this game tick // and then incorporate into the main logic if (POP.Input.tapped) { POP.entities.push(new POP.Touch(POP.Input.x, POP.Input.y)); // set tapped back to false // to avoid spawning a new touch // in the next cycle POP.Input.tapped = false; checkCollision = true; } // cycle through all entities and update as necessary for (i = 0; i < POP.entities.length; i += 1) { POP.entities[i].update(); if (POP.entities[i].type === ‘bubble’ && checkCollision) { hit = POP.collides(POP.entities[i], {x: POP.Input.x, y: POP.Input.y, r: 7}); POP.entities[i].remove = hit; } // delete from array if remove property // is set to true if (POP.entities[i].remove) { POP.entities.splice(i, 1); } }

现在的泡泡比较无趣,移动速度和轨迹都一样。我们可以通过下面这段简单的代码来随机化泡泡的运动:

POP.Bubble = function() { this.type = ‘bubble’; this.r = (Math.random() * 20) + 10; this.speed = (Math.random() * 3) + 1; this.x = (Math.random() * (POP.WIDTH) – this.r); this.y = POP.HEIGHT + (Math.random() * 100) + 100; this.remove = false; this.update = function() { this.y -= this.speed; // the rest of the class is unchanged

我们要让泡泡左右摆,使玩家更难触击到它们:

// the amount by which the bubble // will move from side to side this.waveSize = 5 + this.r; // we need to remember the original // x position for our sine wave calculation thisthis.xConstant = this.x; this.remove = false; this.update = function() { // a sine wave is commonly a function of time var time = new Date().getTime() * 0.002; this.y -= this.speed; // the x coordinate to follow a sine wave thisthis.x = this.waveSize * Math.sin(time) + this.xConstant; // the rest of the class is unchanged

我们使用一些基本的几何学知识就能达到这个效果,也就是正弦波。做游戏不一定要精通数学,基本的知识就非常够用了。

游戏屏幕上还应该显示计数。为此,我们要追踪游戏过程的各种活动。

将以下代码与所有其他变量声明一起放在程序的开头部分:

// this goes at the start of the program // to track players’s progress POP.score = { taps: 0, hit: 0, escaped: 0, accuracy: 0 },

现在,在Bubble类,当泡泡离开屏幕,我们可以用POP.score.escaped记录。

// in the bubble class, when a bubble makes it to // the top of the screen if (this.y < -10) { POP.score.escaped += 1; // update score this.remove = true; }

在主要更新循环中,我们相应地增加POP.score.hit:

// in the update loop if (POP.entities[i].type === ‘bubble’ && checkCollision) { hit = POP.collides(POP.entities[i], {x: POP.Input.x, y: POP.Input.y, r: 7}); if (hit) { POP.score.hit += 1; } POP.entities[i].remove = hit; }

为了得出命中率,我们必须记录玩家的所有触击动作:

// and record all taps if (POP.Input.tapped) { // keep track of taps; needed to // calculate accuracy POP.score.taps += 1;

命中率的算法就是,触击数乘上100。注意,~~(POP.score.accuracy)把约数变成整数。

// Add at the end of the update loop // to calculate accuracy POP.score.accuracy = (POP.score.hit / POP.score.taps) * 100; POP.score.accuracy = isNaN(POP.score.accuracy) ? 0 : ~~(POP.score.accuracy); // a handy way to round floats

最后,我们使用POP.Draw.text来显示得分。

// and finally in the draw function POP.Draw.text(‘Hit: ‘ + POP.score.hit, 20, 30, 14, ‘#fff’); POP.Draw.text(‘Escaped: ‘ + POP.score.escaped, 20, 50, 14, ‘#fff’); POP.Draw.text(‘Accuracy: ‘ + POP.score.accuracy + ‘%’, 20, 70, 14, ‘#fff’);

7、修饰

我们都知道,制作一个可玩的demo只需要若干小时,但一款漂亮的游戏却要耗费数天、数月甚至数年!

我们可以通过以下做法增加这款小游戏的视觉吸引力。

颗粒效果

大多数游戏都会使用颗粒效果,特别是对于爆炸。当玩家触击泡泡时,泡泡就碎成若干小泡泡,而不是立即消失,效果会不会更好呢?

看看我们的Particle类:

POP.Particle = function(x, y,r, col) { this.x = x; this.y = y; this.r = r; this.col = col; // determines whether particle will // travel to the right of left // 50% chance of either happening this.dir = (Math.random() * 2 > 1) ? 1 : -1; // random values so particles do not // travel at the same speeds this.vx = ~~(Math.random() * 4) * this.dir; this.vy = ~~(Math.random() * 7); this.remove = false; this.update = function() { // update coordinates this.x += this.vx; this.y += this.vy; // increase velocity so particle // accelerates off screen this.vx *= 0.99; this.vy *= 0.99; // adding this negative amount to the // y velocity exerts an upward pull on // the particle, as if drawn to the // surface this.vy -= 0.25; // off screen if (this.y < 0) { this.remove = true; } }; this.render = function() { POP.Draw.circle(this.x, this.y, this.r, this.col); }; };

以上代码的作用显而易见。当泡泡被击中时,它会碎成若干加速上升到水面的颗粒。不过,本文不会探讨这个效果的算术和物理学。

为了制作颗粒效果,我们我们要在entities集合中添加若干泡泡被击中时会出现的颗粒:

// modify the main update function like so: if (hit) { // spawn an explosion for (var n = 0; n < 5; n +=1 ) { POP.entities.push(new POP.Particle( POP.entities[i].x, POP.entities[i].y, 2, // random opacity to spice it up a bit ‘rgba(255,255,255,’+Math.random()*1+’)’ )); } POP.score.hit += 1; }

水波

考虑到游戏发生在水下,有必要在屏幕顶部添加水波效果。我们可以通过绘制大量重叠的圆形来制造水波的错觉:

// set up our wave effect; // basically, a series of overlapping circles // across the top of screen POP.wave = { x: -25, // x coordinate of first circle y: -40, // y coordinate of first circle r: 50, // circle radius time: 0, // we’ll use this in calculating the sine wave offset: 0 // this will be the sine wave offset }; // calculate how many circles we need to // cover the screen’s width POP.wave.total = Math.ceil(POP.WIDTH / POP.wave.r) + 1;

把以上代码添加到POP.init函数前面。POP.wave有许多值。

添加以下代码到主要更新函数中。它作用正统波来调整水波的位置,从而产生水面运动的错觉。

// update wave offset // feel free to play with these values for // either slower or faster waves POP.wave.time = new Date().getTime() * 0.002; POP.wave.offset = Math.sin(POP.wave.time * 0.8) * 5;

最后要做的就是让render函数绘制水波:

// display snazzy wave effect for (i = 0; i < POP.wave.total; i++) { POP.Draw.circle( POP.wave.x + POP.wave.offset +  (i * POP.wave.r), POP.wave.y, POP.wave.r, ‘#fff’); }

这里,我们对泡泡重复使用正弦波,使水波活动更加温和。

结语

终于完工了。希望你通过这个粗糙的教程能学习到一些制作HTML5游戏的技巧。我们已经制作了一款非常简单的游戏,它可以在大多数智能手机和浏览器上运行。你还可以考虑从以下几个方面进一步改进游戏:

1、使用本地存储器保存最高得分。

2、添加载入画面和结束画面。

3、添加增益道具。

4、添加声音。与我在本文开头部分所说的相反,这并非不可能,只是会有一点麻烦。技术之一是使用声音精灵(相当于CSS中的图像精灵)。

5、尽情发挥你的想像力!

如果你有兴趣进一步探索手机HTML5游戏的潜力,我建议你多多测试框架,看看什么对你有用。如果你愿意花一点钱,Impact引擎是一个好起点,它附带详尽的说明文件和实用的论坛。《X-Type》效果不错吧?

原文链接

文章来源于网络,如果有侵犯到您的权益,请及时联系QQ:123464386,将会在第一时间进行处理!

每周好玩新游推荐
每周好玩新游推荐

最近有什么好玩的新游戏?最近哪些游戏比较好玩?这里为大家带来本周热门新游合集,汇聚了最新一周的高分爆款新游,还在为不知道玩什么游戏而烦恼的玩家,快来保存下载体验吧!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

4

2026.01.15

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

28

2026.01.15

公务员调剂条件 2026调剂公告时间
公务员调剂条件 2026调剂公告时间

(一)符合拟调剂职位所要求的资格条件。 (二)公共科目笔试成绩同时达到拟调剂职位和原报考职位的合格分数线,且考试类别相同。 拟调剂职位设置了专业科目笔试条件的,专业科目笔试成绩还须同时达到合格分数线,且考试类别相同。 (三)未进入原报考职位面试人员名单。

36

2026.01.15

国考成绩查询入口 国考分数公布时间2026
国考成绩查询入口 国考分数公布时间2026

笔试成绩查询入口已开通,考生可登录国家公务员局中央机关及其直属机构2026年度考试录用公务员专题网站http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/examResult/written_result.html,查询笔试成绩和合格分数线,点击“笔试成绩查询”按钮,凭借身份证及准考证进行查询。

6

2026.01.15

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

63

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

34

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

73

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

20

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

31

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号