阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

使用 HTML5 开发体感游戏——VeloMaze 的开发简介

  • 2013-03-19
  • 本文字数:4637 字

    阅读完需:约 15 分钟

HTML5 现在越来越像一个游戏开发平台。但有时候,游戏领域对于如何应用 HTML5 的特性设置了相当多的限制条件,尤其是对那些访问硬件设备的接口更是如此。

在 2012 年 11 月初,我加入了 copypastel 小组,并决定分享我在第三届年度 NodeKO 竞赛中开发游戏的经历。尽管由于时间限制无法详解全项目,我相信结果仍旧值得与爱好游戏相关技术的读者们分享。接下来,我打算公开该游戏的技术背景,及其如何在多种网络技术基础之上构建整个项目。应用在该游戏中的技术有: Node.js express (静态内容服务), Socket.io (处理客户端和服务器端关于小球往复运动的通讯), Sylvester.js (物理引擎的矢量库)和 jQuery

那什么是 VeloMaze 呢?VeloMaze 是被许多点状恐龙(迅猛龙)占据的迷宫。迅猛龙希望小球能一直在迷宫中移动。由于迷宫的连续性,它可以说是没有终点的。但是每当你通过一级关卡,就会给你之后的玩家造成更多麻烦,因为他(她)会获得另一个小球!是不是很有趣?这就是迷宫中的生活。

这个游戏非常适合那些在同一个地方,而且每个人都有手机的团队。这在当今是很常见的。这里还有一段解说游戏系统要求的视频

系统运行最重要的条件就是加速计。加速计是测量加速度的设备。带有加速计的设备通常返回重力的角度或者重力的矢量数据。这在某些浏览器中有可能做到,比如在下列网贴中所提及的:

从描述系统要求的视频中可以注意到,某些笔记本电脑中也配有加速计。相当多新式的 MacBook Pro 笔记本为防止跌落时造成硬件损伤也安装了加速度计(我那台 2009 年买的笔记本中就安装着一个)。我觉得以笔记本旋转为基础的游戏开发领域目前还是少有人涉足的地带!下面的图表演示了应用程序架构在上层是如何搭建的。

游戏本身的开发相当容易,但全面支持所有的浏览器和加速度计组合需要做更多的工作,而我们的小组只拥有 48 小时的时间。因此,有些测试我们是没有做的,比如对最新版 Android 系统的测试;但是我惊喜的发现,我们的游戏在其中却运行的非常好!然而运气只是成功的一部分。在下面的篇幅中,我打算解析游戏玩法的编写,并解释究竟怎样使该游戏具有可玩性。

读取加速度计数据非常简单,不过标准的缺失使得该过程比预想的更加难以实现。首先,我们快速调查了小组内现有的各种不同的平台和浏览器组合,为适应各种组合方式,编写了如下代码:

复制代码
/* 这里检查游览器是否支持 DeviceOrientationEvent 事件(链接到 W3C)。*/
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', function(e) {
// 我们从事件“e”中获取角度值并转化成弧度值。
leftRightAngle = e.gamma /90.0*Math.PI/2;
frontBackAngle = e.beta /90.0*Math.PI/2;
}, false);
} else if (window.OrientationEvent) { // 另一个选项是 Mozilla 版本同样的东西
window.addEventListener('MozOrientation', function(e) {
// 在这里将长度值当做一个单位,并转换成角度值,看起来运行的不错。
leftRightAngle = e.x * Math.PI/2;
frontBackAngle = e.y * Math.PI/2;
}, false);
} else {
// 自然地,没有浏览器支持的大多数人会获取这个。
setStatus('Your device does not support orientation reading. Please use Android 4.0 or later, iOS (MBP laptop
is fine) or similar platform.');
}

结果是,代码可以在版本较新的 Chrome 中正常运行,也有人反馈说说它也可以运行在较新版本的 iOS 上的 Safari 浏览器当中(但是我手头上的 Safari 并不支持)。我决定不再试图寻找那种能读取所有可能用的浏览器中加速度计数据的普适性解决方案,因为现实是我们在 Node 淘汰赛的编码环节中个只有 48 小时的时间,而当时游戏的架构还没有完成。

我决定使用 Sylvester,它是一个碰撞检测的向量和矩阵数学库。其实我也可以使用 Box2D JS 来节省时间,但是由于有过 Sylvester 的使用经验,并且所需的碰撞检测比较简单,我还是决定使用 Sylvester。检查小球是否落到洞里去的代码如下所示:

复制代码
function checkBallHole(ball, hole, dropped) {
// 用 Sylvester 定义洞和求的位置为矢量对象
var holeVector = $V([hole.x, hole.y]);
var ballVector = $V([ball.x, ball.y]);
// 在 Sylvester 中用向量简单的计算距离
if (ballVector.distanceFrom(holeVector) < hole.r) {
// 用球的位置作为变量执行回调函数
dropped(ballVector);
}
}

所以事实上这里没有什么复杂的:如果你的小球的中心位于洞内,那么就会触发“dropped”的函数。这段代码在每帧运行一次,那么以前开发过游戏的朋友都知道,这种实现方式可能会造成小球在这一帧内飞跃洞穴而没有掉进去。然而,在日常生活中我们知道,如果你用足够快的速度将小球推向洞穴,它是可以滑过而不掉落的,所以这不是个问题。

这个游戏中也有墙体,所以碰撞检测也是必须要做的。Sylvester 提供了一种目标与计算线状对象的放发,我用的就是这个。简单的代码如下:

复制代码
// 计算球和墙壁碰撞时的冲击矢量数据
function impactBallByWall(ball, wall) {
var ballVector = $V([ball.x, ball.y]);
// 定义墙体为线段 (x1,y1) (x2,y2)
var wallSegment = Line.Segment.create(
              $V([wall.sx, wall.sy]),
              $V([wall.dx, wall.dy]));
// 计算墙与球的最近点(几乎就要撞上的那个位置)
var collisionPoint = wallSegment.pointClosestTo(ballVector)
              .to2D(); // needed by sylvester to convert 3D to 2D vector
//sylvester 将矢量数据从 3D 转化成 2D 所需的变量,然后看这个距离在当前框架内为多少(并不是在两个框架之间差距多少)
var dist = collisionPoint.distanceFrom(ballVector);
// 天真的假设碰撞只发生在球和墙的距离小于球的半径的情况下
if (dist < ball.r) {
// 调整到一个合适的值。较大的逆质量值意味着更大的影响(和较小的质量)
var inverseMassSum = 1/100.0;
// 从球心到碰撞点的向量
var differenceVector = collisionPoint.subtract(ballVector);
var collisionNormal = differenceVector.multiply(1.0/dist);
// 球陷下去的部分相当于在墙内
var penetrationDistance = ball.r-dist;
// 碰撞时球的速率
var collisionVelocity = $V([ball.vx, ball.vy]);
// 从点属性中我们获得冲击速度
var impactSpeed = collisionVelocity.dot(collisionNormal);
if (impactSpeed >= 0) {
    // 计算冲击量。运动能量在每次碰撞是以 2-1-0.4=0.6 的倍率递减
    var impulse = collisionNormal.multiply(
               (-1.4)*impactSpeed/(inverseMassSum));
    // 冲击只会作用在球上,因为墙被设计为固定的
    var newBallVelocity = $V([ball.vx, ball.vy]).add(
               impulse.multiply(inverseMassSum));
    // 把值传回原来的对象
    ball.vx = newBallVelocity.e(1);
    ball.vy = newBallVelocity.e(2);
}
}
}

在实现小球和墙体的碰撞过程时我做了许多并非真实的假设(但是跟现实足够接近)。首先,墙体的厚度为零(而不是实际上的 5 像素),而且,我没有计算两帧之间发生了什么。很明显,这会导致游戏中球体有能力穿越墙体。通过创建球体在不同帧之间的运动线段并找出球体三角与墙体之间是否有交叉,就很可以容易的测试到是否会发生碰撞。那么我们就必须要计算小球和墙体发生碰撞的位置。在上文的代码段中,这个位置数据就存在变量“collisionPoint”内(见下图)。

我很喜欢 Ganvas 和 WebGL,但是我们计划使用 DOM 和 jQuery 来做渲染,因为我们除了制作球体滚动之外,不需要任何 Ganvas 和 WebGL 的特效(如果这样实现,其实是很优雅的,真可惜)。使用 DOM 渲染的场景在缩放时有点生硬,但它很容易实现。我写了下面的函数用于绘制游戏中的子画面。

复制代码
// 设置 DOM 元素属性以反映 sprite 对象
setElementPosition: function(element, sprite) {
// 同步 sprite 维数
sprite.width = (maze.getSquareWidth() * sprite.r * 2);
sprite.height = (maze.getSquareHeigth() * sprite.r * 2);
var x = sprite.x;
var y = sprite.y;
/* 在绝对定位中计算样式属性 left 和 top 的值
* 从而确保点(x,y)在 sprire 的中心位置(使距离计算更加简单)
*/
var newLeft = (x * maze.getSquareWidth()  - element.width() / 2.0);
var newTop = (y * maze.getSquareHeigth()  - element.height() / 2.0);
// 避免 sprite 因为受到传感器持续输入的影响而产生的颤抖
// 通过一个阈值判断是否显示球在屏幕上的移动。
// 这是一个相当大的阈值,对于某些设备来说应该选择较小的值。
if (thresholded(element.css('left') - newLeft, 5) !== 0) {
   // 设置 DOM 元素的 x 坐标位置
    element.css('left', parseInt(newLeft) + 'px');
}
if (thresholded(element.css('top') - newTop, 5) !== 0) {
    // 设置 DOM 元素的 y 坐标位置
    element.css('top', parseInt(newTop) + 'px');
}
// 设置 DOM 元素的大小。
element.css('width', sprite.width + 'px');
element.css('height', sprite.height + 'px');
// 球状 DOM 元素包含许多层(所有的 div),所以重置所有层。
element.find('div').each(function () {
$(this).css('width', sprite.width + 'px');
$(this).css('height', sprite.height + 'px');
});
// sprite 位置的调试信息。通过点击‘enter’显示调试信息。
element.find('.location').html('('+parseInt(sprite.x*10)/10.0+','+parseInt(sprite.y*10)/10.0+')');
},

我做了一个根据视角实时缩放的功能,因此在每个框架中的宽度和高度都是计算得到的。很不幸在游戏中没有体现出这点,因为我们尝试编程控制浏览器旋转失败了(没有用于此项功能的接口,所以这还需要破解)。所以我们最后决定,通知用户关闭手机浏览器的旋转功能,如下图所示:

所有的加速度计数据的读取,物理引擎的运行和 DOM 渲染都被归拢到一个主循环中了。我将所有的主循环的代码放置到函数“update”中并且每 100 毫秒运行一次(我知道这不够频繁,但是它在我的设备上运行的很好,所以就暂时忽略这个设定值吧),像这样:

复制代码
window.setInterval(function() { update(); }, 100);

客户端的所有源代码可以点击这里获取。

顺便提一句,我对于新式的视网膜MacBook Pros 非常失望,它没有加速计(就像我们某位玩家提到的),因为它们的SSD 驱动器没有可以移动的部件!所以也许以笔记本旋转为基础的游戏看起来要到此为止了。- @raimo_t

关于作者:

Raimo Tuisku 是 UserVoice(一种和用户交流的产品)的 API/ 集成系统开发者。他在去年完成了他的计算机科学硕士学位,是位有 10 年开发经验的资深开发人员。在前往旧金山湾区之前,他为三家芬兰软件公司开发产品和安全接口。除 Web 和集成之外,Raimo 还喜欢开发 3D 游戏,设计软件架构,开发社交移动版游戏,和环游世界会见新的朋友。你可以在
Twitter 上 @raimo_t 来获知他在 APIs、WebGL、HTML5 Canvas 和移动版 HTML5 游戏方面正在进行的工作。

查看英文原文: Developing Motoric Games with HTML5 - The Making of VeloMaze

2013-03-19 17:296101
用户头像

发布了 21 篇内容, 共 64584 次阅读, 收获喜欢 1 次。

关注

评论

发布
暂无评论
发现更多内容

玩转服务器之环境篇:PHP和Python环境部署指南 | 京东云技术团队

京东科技开发者

php Python 京东云 企业号 5 月 PK 榜 轻量云服务器

【保姆级教程】如何用Rust编写一个ChatGPT桌面应用 | 京东云技术团队

京东科技开发者

rust 京东云 桌面应用 企业号 5 月 PK 榜

华为云云原生视窗:一文回顾Q1精彩瞬间

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

MatrixGate 5.0 性能再升级,加载速度提升三倍!

YMatrix 超融合数据库

数据库 开源数据库 超融合数据库

企业应该知道的几种网络安全防护措施!

行云管家

网络安全 网络 信息

架构师日记-从数据库发展历程到数据结构设计探析 | 京东云技术团队

京东科技开发者

数据库 京东云 企业号 5 月 PK 榜

深入理解 synchronized 的锁升级

做梦都在改BUG

Java synchronized 锁升级

Wallys / QCN9074/QCN9024 WIFI 6E 802.11AX 4X4 6GHz wifi module.

Cindy-wallys

手把手教你用代码画架构图 | 京东云技术团队

京东科技开发者

京东云 代码实现 企业号 5 月 PK 榜 C4

前端开发之函数式编程实践 | 京东云技术团队

京东科技开发者

JavaScript 编程 京东云 企业号 5 月 PK 榜

时序数据库中的乱序问题-写不动的老程序员带你解读

Greptime 格睿科技

云原生 时序数据库 国产时序数据库 乱序数据

软件测试 | 配置MySQL

测吧(北京)科技有限公司

测试

软件测试 | 安装PyMySQL

测吧(北京)科技有限公司

【直播回顾】AIGC产业研究报告2023图像生成篇报告解读

易观分析

产业 智能

碉堡了!阿里架构师手打的Java10W字面经,已经助我拿了6个offer

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

Difference between from DR4019 and DR4029 /industrial wifi5 router/support openwrt.

Cindy-wallys

IPQ4019 ipq4029

华为数据中心产业论坛 | 打造低碳、绿色数据中心,构建新型数字产业能源基础设施

Geek_2d6073

程序员之间拉开差距最大的因素

博文视点Broadview

我以为我对Mysql很熟,直到遇到了阿里这份笔记

做梦都在改BUG

Java MySQL 数据库

NLP 入门导论

小付聊测试

AI 入门 nlp

分库分表的 21 条法则,hold 住!

小小怪下士

Java MySQL 程序员 分库分表

软件测试 | SQLite管理工具

测吧(北京)科技有限公司

测试

小微企业运维用哪款软件好?有免费的吗?

行云管家

运维 安全运维 小微企业

背靠香港影视集团星光文化,StarNFT问世了

小哈区块

创建各种类型的3D模型:Rhino 7中文激活版

真大的脸盆

Mac Mac 软件 三维建模 建模软件 3d建模

低代码平台需要什么样的ORM引擎?(1)

canonical

开源 低代码 ORM 低代码平台 Spring JPA

低代码平台需要什么样的ORM引擎?(2)

canonical

开源 mybatis 低代码 jpa ORM

阿里大佬在Github分享的Spring Cloud全栈笔记,你想象不到有多全

做梦都在改BUG

Java 架构 微服务 Spring Cloud

2023升级版Java面试八股文核心笔记,7天内拿下那该死的offer

开心学Java

Java 面试 java面试 Java八股文

什么是声明式编程

canonical

函数式 声明式 命令式

从可逆计算看声明式编程

canonical

开源 低代码 声明式 命令式

使用HTML5开发体感游戏——VeloMaze的开发简介_JavaScript_Raimo Tuisku_InfoQ精选文章