,前提是这款游戏存在对应的Android版本。下载之后将apk扩展名修改为rar并解压,这样你就可以从解压后的目录中找到所有的图片和音频资源。注意:有些游戏对资源进行了压缩和处理,这样你就很难获取到了。)
在游戏源码的根目录,当你打开index.html就可以快速预览游戏的效果了(当然,这需要你的浏览器支持HTML5部分特性)。
游戏的核心框架是myEngine目录,这也是本节介绍的重要。myEngine目录中包含了与游戏业务逻辑无关的核心库,它与你所接触过的jQuery、Ext等框架一样,封装了一系列常用的处理逻辑;在它的基础上,你可以更简单地编写少量的代码,来完成你的游戏。
按照游戏index.html文件中引入的脚本顺序,我将依次说明游戏框架中每个文件的含义:
首先是core目录下的my.js,这是框架的核心文件,它包含了一些常用的公共函数,如果你已经了解了JavaScript面向对象相关的知识,那么很容易可以看懂文件中所有的代码。
component/Component.js:Component是所有游戏组件的基类,所有的游戏组件均继承自它,Component类提供了组件对象的创建、初始化、销毁等公共方法。
component/DisplayObject.js:DisplayObject是所有可显示组件的基类,游戏中所有能显示的组件,都应该继承它,因为它提供了一套统一的处理对象显示的方法:显示隐藏控制、透明度、旋转、翻转、缩放、渲染控制等。
component/DisplayObjectContainer.js:DisplayObjectContainer是一个组件容器类,它本身也是一个DisplayObject对象,但它可以容纳其它更多的DisplayObject和DisplayObjectContainer对象,便于统一管理。例如:一个房间内的场景包含了桌子、椅子、衣柜、书架等DisplayObject(或DisplayObjectContainer)对象,而这个房间就是一个DisplayObjectContainer对象,房间被销毁,房间内的所有东西也不会再存在;这样你就不需要再把房间内的东西一个个地销毁掉。
component/Bitmap.js:图像对象,如果你要在游戏中显示一副图像,可以创建一个Bitmap对象。
component/Animation.js:帧动画控制类,由多幅图像组成,在固定的频率中不断切换,形成动画的效果。使用它,可以方便地创建一个帧动画,并控制它的显示、隐藏和播放等行为。
component/Sprite.js:游戏中所有精灵的基类,如果对“精灵”的概念还不是很清楚,建议按照第一节中介绍的学习过程再巩固一下。它提供了一个精灵的基础方法和属性,包括移动、动画控制和碰撞检测。
component/Viewport.js:视口对象,游戏地图可能会很大,但能够同时看得到的区域可能只是一个固定的尺寸,当游戏角色或场景移动时,地图也会移动,玩家会感觉是通过一个视口在观察角色和地图的移动。这里的视口对象,就是用来处理视口的移动,以及保存视口状态的。
component/Layer.js:游戏的分层,一个游戏中可能包含许多内容,比如主角、玩家、NPC、怪兽、道具、效果、地图和其它场景,我们没必要把它们堆到一起,这样你无法进行管理和维护,因此我将它们放到多个层级,就可以方便地对每个层级内的元素进行统一控制。
这里的分层不仅仅是在逻辑上将不同类型的元素分离开,不同的Layer对象还可以配置到不同的Canvas画布上,这是提升性能的一种重要的方式,后面会详细讨论。
component/Game.js:游戏基类,用于控制游戏的帧频、画布的刷新、开始和结束游戏,以及游戏时间相关的记录。
event/KeyEvent.js:监听用户按键事件,如果你查看过这个文件的源码,相信我不用作太多介绍你就会明白。它不会绑定具体的事件在用户的某个按键上,它只提供一些控制和查询当前按键状态的方法。
utils/ImageManager.js:图像管理类,使用Canvas绘图前,首先需要保证图片已经被完全加载完毕,而游戏中使用的图片非常多,因此如何统一控制加载和管理,就由ImageManager来发挥了。
utils/DOM.js:提供了一套类似于jQuery的DOM操作方法,之所以不使用jQuery,是因为对这个游戏来说,jQuery显得有些大材小用,没有必要为了使用其中的几个方法,加载整个库。
utils/Math.js:提供一些基础的算数运算方法。
utils/buzz.js:这是一个第三方音频管理库,作用和ImageManager类似,不过是用于控制音频文件的加载和播放。其实框架中是包含了一个我自己写的音频管理类,这里之所以使用第三方库,是因为我还不能完全确保那个音频管理类的稳定性。
另外,框架中还包含了一些游戏中没有使用到的文件,比如:ScriptManager(脚本动态加载和管理)和Astar(自动寻路算法),这里我就不作介绍了。
以上是游戏框架中的全部内容,具体细节建议阅读源码,源码中标注有详细的注释。
如果你接触过Ext框架或Flex,你会很容易理解这里大部分类的作用和功能,因为它们有许多相似之处。
这套框架我一共重写了4次,每一次都会有许多提升。这里所说的提升并非是指功能的增强,相反,这个版本和第1次完成时相比,功能已经被删减掉了一大部分,之所以这样做,仅仅是为了做到需求、工作量和性能之间的平衡(性能相关的问题,我们放到后面再讨论),我认为在能实现需求的前提下,最重要的就是尽可能的简单、快速。
我在这里回顾一下之前删减掉的一些功能:
类似DOM Level 2中的事件模型,包含Event、EventDispatcher、EventManager和Component下具体子类的事件模型类。之所以删掉它们,是因为我完全可以使用类似DOM Level 1中的事件模型来代替它们,而这样做会更简单,更高效。当然也会牺牲一些扩展性和耦合度,但我认为这是值得的。
以前的Animation类,是一个继承自DisplayObjectContainer的子类,它包含了一系列Frame(单帧)对象,而Frame对象中又包含了Bitmap(图像)对象和CollRect(碰撞区域)对象;当初之所以有这么多东西,是因为考虑到动画对图像文件和绘制图形之间的转换,以及支持多种碰撞检测方式。这种做法后来也被我否决了,原因与和前面一点一样,出于简单开发和高效性能的考虑。
而现在优化后的Animation类十分简单,它本身只是一个Component组件,通过简单配置图像URL和帧数据,就可以实现一个帧动画。当然,这种做法就受到许多约束,例如:一个动画中所有的帧图像必须是同一个Image对象,还有Animation本身不提供渲染的方法,它必须依附在一个Sprite(精灵)对象中。
删除了Level和Scene类,Level类是用来做游戏关卡控制和调度关卡数据的,而Scene类用作场景的切换、初始化、缓存场景数据以及处理Layer(分层)的视差效果。
现在我把分层的视差效果直接放到了Layer本身,通过定义Layer对象的distance属性(即定义分层与视口之间的距离),就会自动形成视差效果。
例如:在这个游戏中,驴子刚开始跳跃时,驴子身后的房子、山丘、高山、和天空在视觉上的移动速度是不一样的,物体距离相对驴子越远,视觉上移动距离越小;这些背景实际上是存储在多个Layer中,而我只需要通过定义每个Layer的distance属性,就可以实现这个效果。
如果你想开发一套成熟的游戏框架,这些功能或许是非常必要的。因此,我并不能保证删减这些功能是正确的做法,但对于具体需求来说,我认为这样做会更合适。
(如果你也需要这些代码,可以联系我。)
-----------------------------------------------------------------------------------
三、开发一个“驴子跳”游戏
到目前为止,我已经介绍了学习方法、和基础框架的搭建;此时你应该花更多的时间去编写和优化你的框架,让它更简单、更稳定以及更加“坚固”。如果你还有不明白的地方,可能是我描述的不够清楚和准确,也可能是你所花的时间还不够多;无论如何,你都可以联系我一起研究学习。
如果你对前面讲解的内容没有太多的困惑,本节将着重讨论如何在框架库的基础上,开始开发游戏。这个过程会让你非常心动,因为不久你就可以看到游戏的样子了。
我第一步要做的就是将整理游戏资源,提取游戏中图片和音频文件的方法可以参考第二点中的内容。
首先是整理图片资源,我使用Photoshop对图片尺寸进行调整,更重要的是将图片进行合并;这将减少资源的请求数,提高加载效率;另外,这也是我框架库中Animation类所强制要求的(前面提到过,Animation要求动画中所有的帧图像为同一个Image对象)
其次是整理音频文件,目前各浏览器还没有统一支持的音频格式,因此我们必须使用两套同样的音频文件,关于目前各浏览器所支持的音频格式如下:
浏览器 支持的音频格式
IE9 mp3, aac wav
Firefox ogg, wav
Chrome ogg, mp3, aac, wav
Safari mp3, aac, wav
Opera ogg, wav
游戏中的音频文件是mp3格式,为了兼容Firefox和Opera,我同时还选择了wav格式。wav格式的文件非常大,为了减小文件大小,我将音频进行了压缩,减小比特率,但音质受到了明显的影响。可能你会疑惑,为什么我不选择ogg格式呢?这是因为我购买的虚拟主机竟然不支持访问ogg格式的文件,Why?
这里,我建议你可以仍然使用ogg格式,在保持和mp3同样音质的情况下,文件大小会比wav小很多。
资源的整理可能需要花上好几天的时间,不过幸运的是,这些资源我已经整理过一遍,你可以copy过去直接使用。
资源整理完毕后,我创建一个了项目,分好目录结构,并将资源放在对应的目录下。然后创建游戏页面index.html。
经过分析,我将尽量能使用HTML和CSS来做的内容独立开来,使用HTML和CSS进行开发(index.html文件中包含了所有的HTML,css目录下包含了所有的样式);还缺少什么呢?对了,还缺少对DOM的控制逻辑,这么多的HTML,在游戏进行到什么时候显示?什么时候隐藏?如何控制?这些具体的逻辑我们都用不管,先编写一个统一的UI控制类,代码存放在js/classes/UI.js,UI类不包含任何游戏逻辑,它只负责处理DOM相关的UI展现和控制,在游戏中需要使用到它的时候,调用相应的方法即可。
另外在UI类中,也包含了类似于DOM Level 1的事件管理方式,这是为了降低DOM对象与游戏逻辑间的耦合度。
当这部分内容完成的时候,游戏就有了一个可以看到的雏形,但我们还没有真正开始呢,因为我们编写的游戏框架库还没有派上用场。
我先在js目录下创建一个main.js,用作游戏主逻辑的入口文件(在上一节中,我们介绍过,js目录只用于存放游戏相关的业务逻辑)。
在main.js中,我先加载了游戏资源,在资源加载完毕后,创建游戏对象,并开始监听和处理DOM相关的事件;当你点击“开始”按钮后,游戏就正式开始了。
游戏开始后,具体是怎么运行的,这里就不做描述,源码中有详细的注释,但我觉得还是有必要简单描述下js目录下的所有文件的结构,便于大家查找。
js/main.js:这个文件刚才介绍过,是游戏的主逻辑入口文件。
js/classes:游戏中所有的逻辑代码都存放在这里。
js/classes/Audio.js:Audio类提供一些基础方法,用于控制游戏中音效的播放。
js/classes/Cloud.js:当驴子踩到云朵上时,会产生践踏效果,Cloud就是践踏效果类,用于产生践踏效果对象。
js/classes/Donkey.js:驴子类,用于创建驴子实例,提供控制驴子状态相关的方法。Donkey类继承自Sprite(精灵)类。
js/classes/DonkeyJump.js:DonkeyJump(驴子跳),该类继承自Game类,是整个游戏的核心类,主导游戏整体逻辑和状态,用于创建游戏中的分层,负责游戏初始化、开始、暂停等操作。
js/classes/Prop.js:游戏道具类,游戏中一共有7种道具,但它们都是Prop类的生成对象。
js/classes/Stair.js:云朵类,游戏中的云朵也有7种,包括5种普通云、脆弱的云和会移动的云。云朵的类型是随机的,在云朵被创建时,有一定的几率会同时出现一个道具(道具的类型也是随机选择的)。
js/classes/UI.js:UI类用于控制DOM的展现逻辑,上文中已经进行过讨论。
js/frames:游戏中所有精灵的帧动画配置数据都存放在这里;每个文件存放不同精灵的帧动画数据,此处不一一介绍,只以donke.js为例,当你打开这个文件,会发现里面存储的内容几乎完全一样,它定义了每个动画需要使用的所有帧数据:
Js代码
{ // 这是某一帧的配置数据
x : 0, // 当前帧在动画的Image对象出现的x轴位置
y : 0, // 当前帧在动画的Image对象出现的y轴位置
duration : 10, // 当前帧播放的时间(ms)
collRect : [[50, 93, 28, 15]] // 当前帧的矩形碰撞区域
}
单帧配置中的duration表示该帧在动画过程中所播放的时间,而collRect表示动画被播放到该帧时,精灵对象的碰撞区域。
目前我只支持了矩形碰撞,通过分割定义多个矩形碰撞区域,可以提高碰撞监测的准确度。如果需要更加精确的碰撞检测,就需要使用其它的方式实现,例如像素检测。
js/resources:定义游戏所需要的图片和音频资源路径和配置。
js/resources/audios.js:对游戏中所有的音频资源进行定义
js/resources/images.js:对游戏中所有图片资源进行定义
当你了解了这个游戏的组成,你可以参考游戏的源码,并试着自己也写一遍。当然,我的源码写的不一定好,至少我目前觉得在游戏各对象的耦合关系上处理地还不太好,这也是因为开始写的之前没有全面规划。
到目前为止,你通过阅读以上全文,并付之实践,相信一定可以开发出一个你喜欢的HTML5游戏了。
-----------------------------------------------------------------------------------------------
四、性能优化
在本节,我将分享游戏过程中遇到的种种性能问题,和解决方案,以及一些性能测试数据。在分享这些测试数据之前,我得先说明一下我的机器配置:
ThinkPad SL410K(属于ThinkPad系列中最低端的一款了,伤心ing)
操作系统:Windows 7旗舰版
CPU:Intel奔腾双核T44主频2.2GHz
内存:4GB(标配2GB,自己加了2GB)
硬盘:320G SAT 5400转
显卡:Intel GMA X4500集显 显存256M
(说明:因为只在我一个客户端环境中做测试,因此以下结论可能并非完全正确。)
说到前端的性能优化,不得不提到《高性能JavaScript》一书,书中对网络性能和代码结构优化等方面都有非常全面的解说,在这里我就不班门弄斧了,仅仅说一下在我这个游戏中所涉及到部分内容:
图片合并:这会减少HTTP请求数,提高加载效率,你懂的。
代码合并:这会减少网络传输大小,同样用于提高加载效率,有一些工具可以快速压缩合并你的多个脚本文件,我使用的是JsMinGUI: