目 录
摘 要 I
Abstract II
引 言 1
1 项目背景与相关技术 3
1.1 背景与发展简介 3
1.2 HTML5技术及其优势 4
1.3 JavaScript开发的优势与劣势 4
1.4 CSS样式表在开发中的用处 5
1.5 本章小结 6
2 系统分析 7
2.1 需求分析 7
2.2 问题分析 7
2.3 流程设计 7
2.3 功能分析 8
2.4 本章小结 10
3 系统界面设计与优化 11
3.1 技术支持 11
3.2 初始数据设计 11
3.3 炮台属性设计 11
3.4 怪兽属性设计 12
3.5 游戏初始设定 13
3.6 游戏选项指令设定 13
3.7 游戏失败设计 14
3.8 本章小结 14
4 系统实现 15
4.1 功能实现效果 15
4.1.1 打开游戏界面 15
4.1.2 放置建筑物 15
4.1.3 升级建筑物 16
4.1.4 游戏失败 17
4.2 本章小结 18
5 系统测试 19
5.1 模块功能测试 19
5.2 性能测试 19
5.3 本章小结 20
结 论 21
致 谢 22
参考文献 23
附录 源程序清单 25
摘 要
现如今,HTML5技术的飞速发展,同时也是带动了网页塔防游戏开发行业的飞快发展。本游戏是由个人开发和设计的基于最新HTML5技术的网页塔防游戏,是一款既简单又休闲有趣的益智塔防类的小游戏,主要是利用了最新HTML5的不同标签来设计和构造了主标题页面,使用了最新的CSS3样式标签来进行润饰页面,利用先进的Javascript脚本开发技术去设计和实现网页游戏的主要操作逻辑和算法,采用这些最新的网络前端脚本开发技术,使得游戏运行时无需任何安装,用浏览器就是可以轻松打开,具有良好的安全性和用户网络游戏体验。
随着现代网络自动化技术的发展,轻型的传统网页游戏也越来越多的受到了用户的广泛喜爱,而目前使用HTML5开发的轻型网页游戏已经不再服输于那些使用第三方插件进行开发的中小型传统网页游戏。本文主要的灵感是来源自大火的简易版保卫萝卜,笔者对游戏进行了大量的提炼以及简化,塔防游戏主要通过在塔防游戏的地图上装置特定的炮塔,阻止敌人进攻的策略型塔防游戏。本次的塔防游戏项目就是在塔防地图上的特定位置和地点可以装置多种防御能力不同的塔防炮台以及抵御多种怪兽的攻击和入侵。同时在游戏中玩家每场的战斗将会都会拥有多种的道具可以让玩家的防守更加轻松。
通过设计本次的塔防游戏项目对HTML5的网页游戏应用进行一个常用的标签和脚本的总结研究,通过学习如何使用这些标签和脚本来对于网页类塔防游戏应用在设计过程中时会有可能出现的一些问题和方法进行一个总结性的探究,根据已有的基础知识以及相关的参考文献进行找寻脚本以及更多的相关设计方面的基础知识。
关键词:HTML5; JavaScript; 塔防游戏; CSS
Abstract
Nowadays, the development of HTML5 technology also drives the rapid development of the web game industry. The tower defense game based on HTML5 technology designed by this project is a casual puzzle game.It mainly uses the different tags of HTML5 to construct the main page, uses the CSS3 style to retouch the page, and uses JavaScript to realize the main logical algorithm of the game. Using the latest front-end technology, the game can be opened directly with the browser without installing plug-ins, and users have a good game experience.
With the development of Web Technology, light web games are becoming more and more popular with users. Web Games developed using HTML5 are no less than traditional web games developed using third party plugins. And because of its small size, there have been a lot of simple version of web games. This article is mainly to introduce one of the tower defense game . The developer’s inspiration is from the source of fire defense radish. The developers refined the simplicity and designed a simple version of the guardian radish based on it. Tower defense game mainly represents a kind of strategy game that prevents the enemy from attacking by installing turrets on the game map. This game is a map of specific locations in the installation of multiple capabilities of the battery to resist the invasion of a variety of different monsters, and players will have a different building for each battle to defend.
The purpose of the developer is to design this project through the HTML5 application of a common tag and script to research and application, and to summarize and explore the problems in web game design through tags, and to find more relevant knowledge based on existing knowledge and references.
Keywords: HTML5; JavaScript; tower defense game; CSS
引 言
进入新的时代,HTML5正在成为引领新的时代游戏发展的新一个潮流,随之而来的也必然带动了互联网游戏开发行业的繁荣和飞速发展[1]。与传统客户端高设备开发速度要求网游的开发相比,网页游戏的研发实现难度和成本相对较低[2],设计产品研发和市场经营过程所需要前期投入的大量资金和成本相对较低,研发的周期也相对较短,游戏产品上线之后需要资金回笼的速度较快,使得传统的企业游戏平台可快速的运行,这些优势的组合令得网页企业游戏一直以来备受广大游戏玩家和开发商的喜爱,长期保持着高速的增长。
本项目将真正的将HTML5游戏的设计核心展现出来,抛去以往其他游戏的华而不实,以最基本的操作来展现塔防游戏的精髓。开发时参考一款经典的塔防游戏保卫萝卜进行大致界面设计,抛去界面复杂的UI设计,只采用最基本的设计,通过使用不同的样式来渲染不同角色,让用户的使用体验尽量在不增加开发者工作量的情况下达到最佳[3]。
本次系统设计主要采用的就是JavaScript脚本开发,主要是因为随着JavaScript的出现,web编程的兴起,web的编程技术也因此得到了它的飞速发展。常用的JavaScript脚本语言就是一种基于对象和事件驱动的用于进行客户端和web编程开发的交互式脚本语言,使用这种语言能够给设计者本身静止的页面添加动画效果,代替了原先使用第三方插件生成的动画效果。在本次操作系统中主要使用的JavaScript在客户端HTML的文档中主要实现的就是通过编写了客户端网页脚本的代码,从而可以代替第三方的插件从而实现了客户端和web编程中网页内容的动态变化。当一个用户在网页浏览器中自动打开一个项目的网页时,浏览器就是会自动执行该项目网页内嵌用户事先设计好的的一个JavaScript脚本代码,用户就是可以直接通过交互式的行为来自动更改客户端网页的内容和网页外观,这也就是实现了仅用客户端的HTML和CSS技术就实现的网页动态效果。
而本文主要是为了向用户介绍本系统,从需求分析到运行测试一系列的操作,使用户能够更加清晰了解具体的操作,通过文字说明解释,
让用户了解各种技术的使用背景,通过需求分析来向用户展示系统开发的目的,通过核心代码的解释说明来向用户展示HTML5游戏的核心实现技术,最后再加以图片更加直观让用户了解当前系统的运行实现效果。
本文第一章主要介绍网络大环境下HTML5主要技术发展背景,包括JavaScript和CSS等技术的发展及优点。第二章主要介绍项目的分析过程,包括需求分析和设计的理念及规则,用流程图来结合描述,展示玩家与开发者各自所能实现的功能点,结合用例图进行描述。第三章就是对系统具体实现代码进行详细的解释说明,向用户展示核心技术,通过文字说明、代码和运行抓图相结合的方式让用户更加直观的理解核心技术点。第四章主要是向用户展示项目的整体运行效果。第五章系统测试是对一个项目整体性能系统进行的一个整体综合测试,包括各个系统功能模块的测试以及整体性能的测试。
1 项目背景与相关技术
1.1 背景与发展简介
HTML5作为HTML的最新标准,增添了类似新多媒体(video、audio)、新表单(keygen、output)、画布(canvas)等此类新鲜元素[4],对网页提供更好的页面结构,同时它也有很多新的优点,其中比较重要的一点是具有强大的图形和跨平台特性。HTML5增加了很多新的元素[5],其中包括与Web游戏设计紧密相关的Canvas元素。尽管Canvas元素在各个类型的跨平台浏览器中的运行性能都有所很大的差异,不过总的来说,Canvas的应用程序运行编码速度还是很快的。
HTML5是基于HTML语言的进化过程中的其中一个开发版本,也被广泛认为是适用于下一代移动网页游戏开发的基础语言,它为开发者提供了一系列新的技术元素和新的功能特性,这也让越来越多的游戏开发人员对游戏进入了研究。目前各大主流的智能移动浏览器都已经全面的兼容了HTML5,智能移动浏览器平台和移动互联网的普及和兴起,让下一代HTML5应用更惹人喜爱和关注。而随着HTML5的迅速普及和发展,带动了互联网游戏浏览器产业的迅速崛起。例如磊友网络科技最新推出的《修仙三国》[6],推出当日便已经迎来982位活跃注册玩家,付费率8.3%,这使得很多开发者针对这一现象进行了大范围的研究,国内的HTML5光景一片大好,众多之前沉迷于开发插件游戏的开发者逐渐开始思考是否真的一味的使用插件就会开发出真正适合玩家的游戏。HTML5在网页游戏领域的取得的重大突破,是在新推出的HTML5标签和和开发者不断研究完善后使用js脚本进行编码。
在国外HTML5市场经过5年的长期发展之后,早进入理性的状态,不是炒作概念或者单纯的市场宣讲活动,甚至关注度出现了下降,已经变成了在web技术各个独立领域深度探索和创新[7]。国内传统基于flash网页游戏流行的原因,是flash支持视频播放、音频播放与录制等。如今,HTML5新引入的Canvas标签同样具备了游戏开发的条件[8]。HTML5游戏也迎来了真正的发展高潮,越来越多的开发者加入了开发HTML5游戏的行列,越来越多的大型游戏公司也开始和这些开发者进行合作,国内的发展也开始由这些先行者们领头进入了迅速发展崛起的时代。
1.2 HTML5技术及其优势
HTML5游戏即点即玩的特性非常适合轻度游戏的发展[9],随着玩家日渐苛刻的要求,对于开发者的要求也越来越高,开发者们也开始进行更加深层次的研究,HTML5游戏就此也迎来了真正的春天。已故的乔布斯大神曾经这样说过:“没有人愿意使用Flash,全世界已经开始步入了一个名为H5的时代[10]。”其实,HTML5的Canvas标签是允许设计者进行设计实现很多不同样式和作用的交互动画,和以往人们用Flash动画设计实现生成的效果一样,这样就使用户不用提前安装其他的第三方插件来进行游戏,也避免了出现大多数网页玩家所反映的插件不兼容等等问题。
与其他不同的高技术型开发的游戏相比,基于HTML5的标签开发的游戏有三点主要的优势:一是相关技术免费开放、规范且容易推广,设计者在设计的时候不需要有偿的去获取自己想要的资料,使得HTML5的开发更加的开放化,不为单个公司所有或控制[11];二是开发者不需要安装其他插件,这样从根本上减少了用户在操作时的会产生的麻烦,也提高了安全性,使用插件总是会导致某些病毒的潜在攻击,是众多游戏开发者的优先选择[12];三是HTML5覆盖面广,能够在任何系统上进行操作,不存在兼容性异常的情况,无论是安卓系统还是ios系统都能够很好的实现设计者想要展示的效果,不会因为使用者运行环境的影响而受到影响。
1.3 JavaScript开发的优势与劣势
Javascript作为一种脚本语言与python,shell等脚本语言类似,不需要编译运行,有自己的基本数据类型,语法结构,算术和比较运算符,在帮助用户处理数据和文件实际应用中,提供了四种基本的数据类型,同时利用变量存放信息,采用表达式来完成功能复杂的操作[13]。目前总结出来的JavaScript的优劣势已经十分明显了,有以下几个优势:
JavaScript的优势:
(1)快速的终端反应:Java脚本在客户端进行编写,不需要服务器端提供支持就可以运行,提高速度优势[14]。在用户电脑上直接作为脚本执行测试,几乎是立即就可以完成的。相比起其它开发脚本更加容易学
习和实现。它只需利用DOM模型,提供大量的代码,就可以开发出一个用户需要的脚本[15]。
(2)用途广泛:目前JavaScript和其他大部分语言都可以很好的进行交互,并且被广泛应用于各个领域。现在,有很多的方法可以使用JavaScript,不拘泥于某一种固定的使用途径,使得其更加灵活方便,成为开发者青睐的一种脚本开发。
JavaScript的劣势:
(1)安全性:任何一种开发都不可能是绝对安全的,同样JavaScript被显示添加到用户的浏览器,它就有可以会利用客户系统,人为有可能会察觉不到,导致信息泄露[16]。
(2)浏览器支持:不同的引擎对Javascript也会有不同的渲染结果[17],因为不同的功能和接口之前是存在差别的。如果关闭引擎,那么依赖于浏览器支持的脚本也会被关闭,整个JavaScript代码就不会继续运行了。
1.4 CSS样式表在开发中的用处
样式表CSS的英文全程为Cascading Style Sheets,又可翻译为层叠式样式表[18],通过CSS样式表的定义,可以很简单地定义一个整体网页的样式包括定位显示、字体以及各种格式显示大小、元素定位和超链接的各种样式。既能大大减少网页开发者在导航栏书写的代码和工作量,又甚至能够大大增强导航栏设计的效果,使得设计的页面也更灵活多变。
通常的情况下,CSS脚本文件可以直接嵌入到自己的HTML的脚本页面中,但是一般程序开发者常用的一种做法是首先生成一个文件后缀为.css的脚本文件,然后通过自己的HTML调用该.css的脚本文件,一般的方法为<link href="xxx.css"type="text/css"rel=“stylesheet”>[19]。在结构上有以下几点的优势:
(1)设计手段灵活:通过调用CSS样式表可实现效果与设计分离,网站建设形式更为灵活。开发者可以将页面单独进行CSS设计,标明文字显示调用得当即可展示效果[20];
(2)布局更规范、代码更清晰易懂:当网页的某一部分需要改动时,维护人员只需要找到该模块所对应的CSS样式表就可以进行修改,便捷而且不会出现错误修改其他正常部分;
(3)具备控制排版能力:开发者可以直接通过CSS样式表修改结构框架的定位[21],只要提前规划好各个模块的位置便可以直接编写样式,从而更加便利的解决在传统html中经常会出现元素错位的情况。
CSS样式表在布局页面样式时,又有几种常见的布局方式[22]:
(1)float布局,这种布局方式的兼容性比较好;
(2)absolute布局,该布局简单直接,兼容性好;
(3)flex布局,布局简单、灵活,移动端友好,但IE8以下不兼容;
(4)table布局,兼容性好,有时候布局相对简单,但是它是对Table标签的不正规使用,一直以来被大家所诟病。当需要内容高度不一致时并不适应。
CSS样式表又分为三种不同的使用样式:
(1)简单的一种行内其他元素类型样式:主要缺点:行内元素样式没有很大的资源可用和复用性,仅仅能作用于当前的一个行内其他元素;主要优点:这种行内元素样式具体写法比较简单,适用范围广,使用优先极高;
(2)内部的内容样式:主要缺点:内部内容页面间不能相互交换复用;主要优点:基本上已经实现了内部内容样式相互复用。复用的复杂程度不高但也不能在多个使用页面之间复制使用;
(3)外部样式:缺点:编辑时需要频繁切换;优点:HTML和CSS完全分离,CSS得到最大程度复用。
三种外部属性样式之间又因为不同的类型和特性有着不同的优先级一般的情况下可以采用就近的原则[23],在正常的设计中引用样式下优先级的顺序一般为:行内属性样式>内部属性样式>外部属性样式。本次样式设计中因为采用的是并不复杂的样式设计,所以直接选择采用外部属性样式直接调用的方法来为开发者编写CSS外部属性样式,也很好的避免了开发者在进行样式开发的过程和设计中的各种混淆。
1.5 本章小结
本章主要描述网络大环境下HTML5主要技术发展背景,国内外目前的发展趋势,也在本章详细介绍了本项目使用到的包括JavaScript和CSS等技术各自的发展背景及其优点。
2 系统分析
2.1 需求分析
随着网络科技的发展,人们也在追求更加更富有娱乐性的小游戏,在这种大环境下,现在已经大火的网页游戏HTML5游戏更是以其独特的游戏跨平台性和游戏轻量性优势获得了很好的用户和游戏体验[1],已经迅速的成为了近些年来互联网游戏开发行业最热门的网页游戏关键词之一。
在目前这种市场需求下,本项目应运而生,目的就是为了满足玩家既不想让游戏占据过大内存又想要进行富有乐趣的游戏的需求。本款游戏将实现如下功能:防御塔的创建、售卖、攻击;怪兽的攻击、移动;合理的金币获得设计、获取量设计;道具的设计,仿照保卫萝卜的游戏理念,简化游戏中的图形,着重突出其运行的基本规则,同时进行HTML5中标签和JavaScript脚本编写的实践测试,比较发现JavaScript脚本与Python语言开发的不同之处,并从实际开发中体验到明显差异[24]。
2.2 问题分析
大部分的网路游戏使用的都是安装Flash插件来进行游戏动画效果的实现,但对于环境要求十分严格,更要求服务器的承受能力优越,对于一些小成本游戏显然不划算,所以在设计之初便设想使用技术来代替插件的使用、在分析需求之后,在整个设计过程主要围绕以下两个问题解决:
(1)解决不使用第三方插件问题:以前的第三方插件都是专用插件,而非公共标准,安装这些插件,往往会限制在一个特定的范围内构建游戏,只能在Web浏览器的方框中玩Flash游戏,所以这次的设计决定采用常见的简单算法来进行动作设计,避免使用插件;
(2)解决画面跳转卡顿,动作衔接不流畅问题:为了用户的使用体验良好,本次设计过程中为解决该卡顿不流畅问题打算进行一个细致的设计,减少跳转链接的使用,尽可能采用js固有的算法模块进行设计动作和画面背景,简化人物动作,使画面简单化。
2.3 流程设计
游戏为简化流程避免造成卡顿等不良好的游戏体验,故而将流程简
化,用户打开界面即开始游戏,玩家根据初始金币数选择炮台进行摆放,放置有效攻击建筑后怪兽开始按照预先系统设计的轨迹开始行动,击杀怪兽获得金币继而购买更高级的炮台,持续游戏直至玩家生命值为0。设计整体流程图如图2.1所示。
图2.1 游戏流程图
2.3 功能分析
本次系统主要设计的是一款塔防策略型游戏,主要实现的大功能点有以下几个:玩家放置建筑、释放怪物使其按照轨迹行动、建筑物进行攻击和建筑物升级或出售。
开发者与玩家可使用的功能不同。目前项目暂时开放的玩家功能有限,为了简化玩家进行游戏时的操作步骤,设置了如下几个功能,玩家用户可以选择开始游戏、选择建筑物并放置于地图中某一位置、升级建筑物、出售建筑物、暂停、继续或者重新开始游戏,玩家的用例图如图2.2所示:
图2.2 玩家用例图
开发者是可以通过更改后台数据来更改游戏数据,比如游戏初始金钱数和生命值、怪物的属性包括攻击力和移动速度以及移动的轨迹、建筑物的属性包括攻击力、攻击速度和攻击范围、建筑物升级所需的金钱数、出售建筑物的收入以及游戏地图的设计,用例图如2.3所示:
图2.3 开发者用例图
同时本系统设计保有一定的可拓展性与可维护性。由于系统处理能力的需求不是一成不变的,随着功能的不断拓展,对系统处理能力的要求也会越来越高。而本项目的可拓展性就是在日后数据的不断更新中,为开发者留有一定升级数据的空间,可以根据将来的变化进行实时的更新与升级。
维护管理是指为了保证维护质量、提高维护效率、控制维护成本而进行的维护过程管理,要求对项目的每次“修改”均需经过申请、评估、批准、实施、验证等步骤。维护的核心是维护评估和维护验证。
而针对本游戏的可维护性就是指在日后一旦出现某一些玩家无法克服的问题那么这时候就需要后台人员对该系统进行一个定时定期的维护以保证用户良好的使用体验。
2.4 本章小结
本章主要介绍了关于系统的需求分析以及用例分析,结合图示进行分别介绍,对系统中的不同角色也就是玩家与开发者可以使用的不同功能点进行分析介绍,同时介绍了系统保有的可拓展性和可维护性。
3 系统界面设计与优化
3.1 技术支持
初步设计游戏考虑到技术支持方面的问题,有些浏览器可能会出现不兼容的问题,为了减少开发者前期设计框架的麻烦,故而游戏目前只支持在IE9和Chrome浏览器中进行[13],使用Canvas标签的属性提示用户当前浏览器不支持,其代码设计如下:
HTML5 塔防游戏
加载中... 抱歉,您的浏览器不支持 HTML 5 Canvas 标签,请使用 IE9 / Chrome 等浏览器浏览本页以获得最佳效果。 3.2 初始数据设计 在游戏刚刚开始,为了使玩家有一定的基础储备能够进行第一轮的防守,玩家将会获得最初始设定的基础奖励,主要体现为系统给予玩家一定量足够购买建筑的金钱,也会显示玩家初始的生命值、当前怪物波数、当前积分和目前所在关卡的难度系数,这些开发者都可以在js脚本中可进行更改,目前暂定数据如表3.1所示: 表3.1 初始游戏数据表 名称 含义 设定数据 difficulty 难度系数 1.0 wave 怪物波数 0 max_wave 生命 100 max_monsters_per_wave 最大怪物波数 100 money 金钱 500 score 积分 03.3 炮台属性设计
炮台采用最基础的圆圈形状来渲染描绘建筑物和怪物的形状,通过声明不同变量来设定不同建筑物的属性,通过更改不同的变量来实现不同的难度设定,同时也为后期开发者修改数据提供便利,用变量的形式也使得各种元素清晰可见,例如攻击力、攻击范围、子弹发射速度和建
造所花费的金钱数等等,如表3.2中所示:
表3.2 炮台属性表
名称 攻击力 初级攻击范围 最大攻击范围 攻击速度 子弹发射速度 建造金额
cannon 10 4 8 2 6 300
LMG 3 5 10 3 6 100
HMG 30 3 5 3 5 800
laser_gun 25 6 10 20 0 2000
若炮台被放置在界面中,通过定义并调用不同属性变量例如is_selected、level等来显示当前已放置建筑属性,比如level变量声明的是当前等级,killed声明的是当前击杀怪物的数量等等其他变量,当鼠标放置在建筑物上时显示当前属性。
3.4 怪兽属性设计
在脚本td-cfg-monsters中定义怪兽不同的级别以及它们分别的属性,设定其初始生命值以及攻击力,通过defaultMonsterRender函数来渲染怪物的默认属性,包括显示怪物的名称、移动速度生命值和攻击力等等,通过设置不同的数值来实现区分不同的怪物,是游戏更加的具有娱乐性,增加玩家参与感,具体的设计如表3.3所示,以中等怪物为例:
表3.3 怪物属性表
名称 含义 设定数据
name 名称 monster 2
speed 移动速度 6
max_speed 最快移动速度 20
life 怪物生命值 50
damage 攻击力 5
score 积分 0
前十波的怪物设定为开发者设计,由于每个玩家的游戏进程不定所以后续怪物设计不再使用人为设定,均由系统设定自动生成,开发者设定好既定算法进行有规律生成,同时也会根据玩家游戏的进程来自动更改游戏难度,增加娱乐性,具体设计代码如下:
newWave: function (cfg) {
cfg = cfg || {};
var map = cfg.map,
wave = cfg.wave || 1,
//difficulty = TD.difficulty || 1.0,
wave_damage = TD.wave_damage || 0;
if (wave == 1) {
//pass
} else if (wave_damage == 0) {
if (wave < 5) {
TD.difficulty *= 1.05;
} else if (TD.difficulty > 20) {
TD.difficulty *= 1.05;
} else if (TD.difficulty > 10) {
TD.difficulty *= 1.1;
} else {
TD.difficulty *= 1.2;
}
} else if (TD.wave_damage >= 20) {
TD.difficulty *= 0.8;
} else if (TD.wave_damage >= 10) {
TD.difficulty *= 0.9;
} else {
if (wave >= 10)
TD.difficulty *= 1.05;
}
TD.log("wave " + wave + ", last wave damage = " + wave_damage + ", difficulty = " + TD.difficulty);
}
3.5 游戏初始设定
设计游戏的是当用户打开网页的时候就启动,为了避免出现玩家未及时操作导致游戏还未开始就宣告失败,故设计使用算法系统自动检查地图中是否有武器(具备攻击性的建筑)[13]。调用has_weapon函数来检测当前地图是否有攻击性武器,如果有则启动出动第一波怪物,具体算法如下设计:
checkHasWeapon: function () {
this.has_weapon = (this.anyBuilding(function (obj) {
return obj.is_weapon;
}) != null);
},
3.6 游戏选项指令设定
本游戏并未设置结束游戏按钮,只设定暂停、继续与重新开始[14],用户可选择关闭窗口来结束游戏,,通过定义button按钮的不同函数调用来读取用户当前动作,使用如下算法设计选项:
this.btn_pause = new TD.Button(“panel-btn-pause”, {
scene: this.scene,
x: this.x,
y: this.y + 260,
text: TD._t(“button_pause_text”),
//desc: TD._t(“button_pause_desc_0”),
step_level: this.step_level,
render_level: this.render_level + 1,
onClick: function () {
if (this.scene.state == 1) {
this.scene.pause();
this.text = TD._t(“button_continue_text”);
this.scene.panel.btn_upgrade.hide();
this.scene.panel.btn_sell.hide();
//this.desc = TD._t(“button_pause_desc_1”);
} else if (this.scene.state == 2) {
this.scene.start();
this.text = TD._t(“button_pause_text”);
this.scene.panel.btn_restart.hide();
if (this.scene.map.selected_building) {
this.scene.panel.btn_upgrade.show();
this.scene.panel.btn_sell.show();
}
//this.desc = TD._t(“button_pause_desc_0”);
}
}
});
3.7 游戏失败设计
当怪兽抵御住炮台的攻击并脱离攻击范围后将到达终点,当终点生命值耗尽时则游戏失败,界面将显示“GAME OVER”的字提示用户,定义ctx字段来检测当前游戏进程,当满足变量条件时提示游戏失败,具体设计如下:
var ctx = TD.ctx;
ctx.textAlign = “center”;
ctx.textBaseline = “middle”;
ctx.fillStyle = “#ccc”;
ctx.font = “bold 62px ‘Verdana’”;
ctx.beginPath();
ctx.fillText(“GAME OVER”, this.width / 2, this.height / 2);
3.8 本章小结
本章主要对系统的功能点设计进行分模块介绍,介绍了功能实现的具体方法,通过代码介绍将功能点实现的核心展现出来,通过项目抓图将预期实现的效果清晰的展示出来,让用户明确效果。
4 系统实现
4.1 功能实现效果
4.1.1 打开游戏界面
在玩家打开网页游戏界面的时候,游戏启动,显示当前初始设定数据,玩家根据初始的金钱来选择建筑物放在合适的位置进行游戏前的准备,当前状态设定为当玩家放置有攻击型建筑时才算真正开始游戏,初始界面如图4.1所示:
图4.1 初始界面图
4.1.2 放置建筑物
当玩家选择并放置好第一个具备攻击力的建筑时,第一波怪兽来临,建筑会自动识别怪兽来的方向,确定在攻击范围内就会自动发射炮弹。怪物也会根据之前系统中设置好的算法计算的特定轨迹进行移动,实现效果如图4.2所示:
图4.2 放置建筑物
在系统的设置中,玩家可以选择通过摆放不同的路障来将怪物的轨
迹固定,由于设计中将路障设定为怪物不能通过,所以如果玩家大面积布置路障,就会产生类似保卫萝卜的效果,固定怪物的行动轨迹,也变相的相当于是一种减小难度的手段,也可以算是一种趣味玩法,具体实现效果如图4.3、4.4所示:
图4.3 趣味玩法
图4.4 游戏中
4.1.3 升级建筑物
当玩家拥有足够的金钱时玩家可以选择某一建筑升级,点击想要升级的建筑,会出现该建筑目前的属性,将鼠标放置在升级按钮上就会显示升级当前建筑所需要的金钱,选择升级则该建筑升一级,随之属性增强,攻击范围增大,具体实现效果如图4.5、4.6所示:
图4.5 游戏中升级建筑
图4.6 升级成功
4.1.4 游戏失败
当玩家布置的建筑并未将所有怪物清除时,怪物将会移动到终点,终点将会减少一定量的生命值,当玩家生命值为零时,游戏结束,页面提示GAME OVER,这时玩家可以选择关闭游戏或者是重新开始,具体
实现效果如图4.7所示:
图4.7 游戏中怪兽属性图
4.2 本章小结
本章主要通过运行项目,获取界面截图的方式向用户展示各功能实现的效果,为用户详细介绍每一个功能点所能触发的事件效果,通过图文结合的方式更加直观的让用户了解整体的运行效果。
5 系统测试
5.1 模块功能测试
在整体设计完成之后,采用整体运行的方式对本次项目进行模块功能测试,通过完整运行并尝试各个模块功能是否正常,是否能够达到预期效果,并将数据记录下来,记录结果如表5.1:
表5.1 项目功能测试用例表
测试编号 测试功能 实现函数 操作步骤 期望结果 实际结果
1 购买布置建筑 building_type函数计算放置位置 1:选择某一建筑
2:将其放置于地图某位置 建筑放置在固定位子 建筑被放置在选定位置
2 怪物属性 定义monster_attributes来改变怪物不同属性 怪物出现时将鼠标放置在其身上 显示设定属性 显示当前选中怪物属性
3 建筑属性 定义building_attributes设计建筑物属性 将光标放置在建筑物身上 显示设定属性 显示当前建筑属性
4 建筑发射子弹攻击怪物 定义bullet_type函数不同建筑子弹发出 当怪物出现时建筑自动识别并发射子弹 怪物生命下降 怪物生命值明显下降
5 建筑升级 getUpgradeCost函数定义升级建筑花费 当选中建筑,将鼠标放置在升级上 显示当前建筑升级所需金钱 显示当前建筑升级所需金钱
6 建筑出售 getSellMoney函数定义出售建筑收入 当选中建筑,将鼠标放置在升级上 会显示当前建筑出售所收入金钱 显示当前建筑出售所收入金钱
7 暂停、继续、重新开始游戏 Stage变量定义当前游戏状态 点击不同的选项改变当前游戏进程 实现游戏暂停、继续或者重新开始 流畅运行暂停、继续或者重新开始
5.2 性能测试
本次测试为HTML5塔防游戏的性能测试,目的在于测试游戏整体是否符合最初设计以及时候能否完整运行。本次测试采用单人多次测试以期望达到最佳测试结果测试结果如表5.2所示:
表5.2 项目测试统计表
任务 开始时间 结束时间 总时长(天)
计划 2020年5月13日 2020年5月13日 1
实际 2020年5月13日 2020年5月13日 1
数据性能测试:根据各个模块所能实现的功能,按游戏流程进行不限时连续游戏,期间并未出现数据崩溃的情况,流程未被打乱,未出现不明BUG,此次测试操作流程简单,所以并未对服务器造成高度负载,系统承受能力良好,整体运行正常。
本次测试总结出来,项目整体运行良好,效果能够达到预期结果,系统运行流畅不卡顿,玩家使用感良好,并未出现非法指令。同时也总结出了以下可以在后续进行改进的两点:
(1)目前游戏进程较慢,可调整游戏节奏;
(2)难度无法调整,可增加闯关功能。
5.3 本章小结
本章主要是运行项目后的整体测试,包括对设计的所有功能模块的测试,例如对建筑物放置及升级等功能的测试,以及对项目整体性能的测试,经过测试,系统能够达到设计之初想要达到的效果。
结 论
通过以上分析,本项目是一次对基于HTML5网页游戏开发的尝试和实践。从项目最终的研究结果获悉,只要能够有效地利用HTML5、CSS、JavaScript这三门语言,就可以开发出很多可玩性强且逻辑简单的游戏,而HTML5在移动终端的开发与应用也会在后续的不断发展中迅速成长直至成熟[15]。本次设计中使用使用到的像Canvas标签也是HTML5在以后的发展中所常用的标签之一,同时本此设计中也使用到了例如head标签等等常用的基础标签,由于是简单的程序设计,所以在css样式表中并未设计过多的样式,以最基础样式的来实现效果,本次着重设计的是js脚本,在js脚本中也是使用到了很多例如定义函数变量从而使调用函数方式简化,也是笔者首次在设计中尝试自主设计变量控制建筑和怪物的属性,根据找寻到的资料进行一个学习和模仿,根据已学的知识和以往的实际经验进行整合,争取到在本次项目中完成实现。
通过本次项目的实践研究得出,在一些对环境要求并不是很高的游戏开发情况下,使用HTML5来进行设计编写的难度会降低不少,同时安全性也将大大提升,也方便了开发者在撰写过程中进行随时的测试修改,总体项目运行下来也会比使用插件的游戏消耗的资源小,这样就起到了对一些配置并不完备的机器的损失,并且在书写函数调用的过程中,笔者也得出结论如果想要不混淆繁多的变量最好在设定之初就根据想要实现的功能进行命名,避免后续出现重复或者不知道用途的废变量。
通过本次的游戏设计,笔者除了在技术上有些理解之外,在逻辑思维方面也有了很大的提升,也明白了严谨的逻辑思维能力是开发人员必须要拥有的一项能力,例如很多小小的变量,如果不加以注意那么有可能最后它就是导致系统崩盘的关键因素,这也提醒在以后的的开发生活中一定要以严谨的思维来面对每一次设计。
致 谢
大学四年学习时光已经接近尾声,在此我想对我的母校,我的父母、亲人们,我的老师和同学们表达我由衷的谢意。感谢我的家人对我大学三年学习的默默支持;感谢我的母校沈阳城市学院给了我在大学四年深造的机会,让我能继续学习和提高;感谢计算机系的老师和同学们四年来的关心和鼓励。老师们课堂上的激情洋溢,课堂下的谆谆教诲;同学们在学习中的认真热情,生活上的热心主动,感谢我的校外指导老师聂菲老师在我实习期间对我的帮助和引导,所有这些都让我的四年充满了感动。
这次毕业论文设计我得到了很多老师和同学的帮助,其中我的论文指导老师高丽老师对我的关心和支持尤为重要。每次遇到难题,我最先做得就是向高丽老师寻求帮助,而老师每次不管忙或闲,总会抽空来和我一起商量解决的办法。我做毕业设计的每个阶段,从选题到查阅资料,论文提纲的确定,中期论文的修改,后期论文格式调整等各个环节中都给予了我悉心的指导。这几个月以来,老师不仅在学业上给我以精心指导,同时还在思想给我以无微不至的关怀,在此谨向老师致以诚挚的谢意和崇高的敬意。
同时,本篇毕业论文的写作也得到了学长学姐们的热情帮助。感谢在整个毕业设计期间和我密切合作的同学,和曾经在各个方面给予过我帮助的伙伴们,在此,我再一次真诚地向帮助过我的老师和同学便是感谢!
参考文献
[1] 董春侠, 司占军. 基于HTML5技术的五子棋游戏的设计与开发[J]. 微型机与应用, 2007, 36(11) : 12-14
[2] 阳晓霞, 谭卫. 基于HTML5技术的游戏开发与实现[J]. 信息与电脑(理论版), 2019, (09) : 69-71
[3] 冯科融, 王和兴. HTML5网页游戏分析[J]. 电脑编程技巧与维护, 2012, (24) : 71-72
[4] 卫少林, 卫文学. 基于JavaS cript的人机五子棋游戏的设计与实现[J]. 现代计算机(专业版), 2016, (25): 58-62
[5] 朱桂林, 周凌翱. 基于HTML5的汉字拼装游戏[J]. 电子世界, 2019, (15): 48-49+52
[6] Rob Hawkes. HTML5 Canvas基础教程[M]. 北京:人民邮电出版社, 2012.5 :277-278
[7] Jeanine Meyer . HTML5游戏开发[M]. 北京:人民邮电出版社, 2011.3 :329-330
[8] PR Newswire. High 5 Games Doubles Down on HTML5 Library[J]. PR Newswire US, 2018, (12): 66-69
[9] Kevin J, Theisen. Programming languages in chemistry: a review of HTML5/JavaScript[J]. Journal of Cheminformatics, 2019, (19): 1-19
[10] Berkan Uslu, Ecir Uğur Küçüksille. Artificial intelligence library for HTML5 based games: DignityAI[J]. SAÜ Fen Bilimleri Enstitüsü Dergisi, 2017, 21(1): 23-25
[11] 陈青云. HTML5与CSS3技术在网页制作中的应用及发展前景[J]. 信息与电脑 (理论版), 2018, (16): 1-2
[12] 葛蓝. 基于HTML5+CSS3的网页布局[J]. 数字技术与应用, 2017, (10): 91-92
[13] 施瑶. 基于HTML5的贪吃蛇游戏设计与实现[J]. 福建电脑, 2018, 34(07): 118-119
[14] 黎志雄, 黄彦湘, 陈学中. 基于HTML5游戏开发的研究与实现[J].
东莞理工学院学报, 2014, 21(05): 48-53
[15] 张琳. 浅析HTML5+CSS3在网页设计中的新特性及优势[J]. 西安文理学院学报 (自然科学版), 2017, 20(06): 82-84
[16] 明日科技. HTML5从入门到精通[M]. HTML5从入门到精通[M], 2017: 68-72
[17] 王艳. 探析HTML5与CSS3在网页设计中的新特性和优势[J]. 电脑编程技巧与维护, 2016, (21): 70-71+88
[18] 王莉. 关于HTML5应用现状与前景的思考[J]. 电脑迷, 2018, (07): 100
[19] McBroom, Kathleen. Get Coding!Learn HTML,CSS,and JavaScript and Build a Website,App,and Game[J]. Booklist, 2019, 113(18): 106
[20] Adam Freeman. HTML,JSX,and CSS Primer[J]. Pro React 16, 2019, (07): 37-59
[21] 辛子俊, 林雪莹. HTML5游戏开发技术 Egret Engine[M]. 北京:中国水利水电出版社, 2018: 1-129
[22] 邵山欢. HTML与CSS网页制作实战教程[M]. 北京:高等教育出版社, 2019.04: 224-271
[23] 钟声, 黎苗苗. 基于HTML5的教育游戏设计与开发[J]. 报刊荟萃, 2018, (05): 75-79
[24] 曹同雷. 一款基于HTML5 技术的游戏引擎的设计[J]. 科技展望, 2017, (15): 37-39
[25] 沙茉. CSS样式表在网页制作的优点与特效实现[J]. 企业科技与发展, 2019, (04):211-212
附录 源程序清单
主界面td.html源代码:
HTML5 塔防游戏
<div id="td-loading">加载中...</div>
<div id="td-board">
<canvas id="td-canvas">抱歉,您的浏览器不支持 HTML 5 Canvas 标签,请使用 IE9 / Chrome 等浏览器浏览本页以获得最佳效果。</canvas>
</div>
</div>
<div id="about">
<a href="http://oldj.net/article/HTML5-tower-defense/" target="_blank">关于</a> |
<a href="https://github.com/oldj/HTML5-tower-defense" target="_blank">源码</a> |
<a href="http://oldj.net/">Rainy</a> ©
</div>
css样式表c.css源代码: html, body { margin: 0; padding: 0; font-size: 12px; line-height: 20px; font-family: Verdana, "Times New Roman", serif; background: #1A74BA; } h1 { padding: 0; margin: 0; line-height: 48px; font-size: 18px; font-weight: bold; font-family: Verdana, "Times New Roman", serif; letter-spacing: 0.12em; }
#wrapper {
margin: 0 auto;
}
#td-wrapper {
padding: 8px 24px 60px 24px;
background: #E0F4FC;
}
#td-board {
display: none;
font-size: 16px;
}
#td-board canvas#td-canvas {
position: relative;
background: #fff;
border: solid 1px #cdf;
}
#td-loading {
font-size: 18px;
line-height: 48px;
padding: 60px 0 120px 0;
font-style: italic;
}
#about {
color: #fff;
padding: 8px 24px;
}
#about a {
color: #fff;
}
建筑属性td-cfg-buildings.js源代码:
TD.getDefaultBuildingAttributes = function (building_type) {
var building_attributes = {
// 路障
"wall": {
damage: 0,
range: 0,
speed: 0,
bullet_speed: 0,
life: 100,
shield: 500,
cost: 5
},
// 炮台
"cannon": {
damage: 10,
range: 4,
max_range: 8,
speed: 2,
bullet_speed: 6,
life: 100,
shield: 100,
cost: 300,
_upgrade_rule_damage: function (old_level, old_value) {
return old_value * (old_level <= 10 ? 1.2 : 1.3);
}
},
// 轻机枪
"LMG": {
damage: 3,
range: 5,
max_range: 10,
speed: 3,
bullet_speed: 6,
life: 100,
shield: 50,
cost: 100
},
// 重机枪
"HMG": {
damage: 30,
range: 3,
max_range: 5,
speed: 3,
bullet_speed: 5,
life: 100,
shield: 200,
cost: 800,
_upgrade_rule_damage: function (old_level, old_value) {
return old_value * 1.3;
}
},
// 激光枪
"laser_gun": {
damage: 25,
range: 6,
max_range: 10,
speed: 20,
// bullet_speed: 10,
life: 100,
shield: 100,
cost: 2000
}
};
return building_attributes[building_type] || {};
};
怪兽属性td-cfg-monsters.js源代码:
function defaultMonsterRender() {
if (!this.is_valid || !this.grid) return;
var ctx = TD.ctx;
// 画一个圆代表怪物
ctx.strokeStyle = "#000";
ctx.lineWidth = 1;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.cx, this.cy, this.r, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
// 画怪物的生命值
if (TD.show_monster_life) {
var s = Math.floor(TD.grid_size / 4),
l = s * 2 - 2;
ctx.fillStyle = "#000";
ctx.beginPath();
ctx.fillRect(this.cx - s, this.cy - this.r - 6, s * 2, 4);
ctx.closePath();
ctx.fillStyle = "#f00";
ctx.beginPath();
ctx.fillRect(this.cx - s + 1, this.cy - this.r - 5, this.life * l / this.life0, 2);
ctx.closePath();
}
}
TD.getDefaultMonsterAttributes = function (monster_idx) {
var monster_attributes = [
{
// idx: 0
name: "monster 1",
desc: "最弱小的怪物",
speed: 3,
max_speed: 10,
life: 50,
damage: 1,
shield: 0,
money: 5
},
{
// idx: 1
name: "monster 2",
desc: "稍强一些的小怪",
speed: 6,
max_speed: 20,
life: 50,
damage: 5,
shield: 1
},
{
// idx: 2
name: "monster speed",
desc: "速度较快的小怪",
speed: 12,
max_speed: 30,
life: 50,
damage: 6,
shield: 1
},
{
// idx: 3
name: "monster life",
desc: "生命值很强的小怪",
speed: 5,
max_speed: 10,
life: 500,
damage: 7,
shield: 1
},
{
// idx: 4
name: "monster shield",
desc: "防御很强的小怪",
speed: 5,
max_speed: 10,
life: 50,
damage: 7,
shield: 50
},
{
// idx: 5
name: "monster damage",
desc: "伤害值很大的小怪",
speed: 7,
max_speed: 14,
life: 50,
damage: 15,
shield: 2
},
{
// idx: 6
name: "monster speed-life",
desc: "速度、生命都较高的怪物",
speed: 15,
max_speed: 30,
life: 100,
damage: 5,
shield: 3
},
{
// idx: 7
name: "monster speed-2",
desc: "速度很快的怪物",
speed: 30,
max_speed: 40,
life: 30,
damage: 5,
shield: 1
}
];
if (typeof monster_idx == "undefined") {
// 如果只传了一个参数,则只返回共定义了多少种怪物
return monster_attributes.length;
}
var attr = monster_attributes[monster_idx] || monster_attributes[0],
attr2 = {};
TD.lang.mix(attr2, attr);
if (!attr2.render) {
// 如果没有指定当前怪物的渲染方法
attr2.render = defaultMonsterRender
}
return attr2;
};
TD.makeMonsters = function (n, range) {
var a = [], count = 0, i, c, d, r, l;
if (!range) {
range = [];
for (i = 0; i < TD.monster_type_count; i ++) {
range.push(i);
}
}
l = range.length;
while (count < n) {
d = n - count;
c = Math.floor(Math.random() * d) + 1;
r = Math.floor(Math.random() * l);
a.push([c, range[r]]);
count += c;
}
return a;
}
初始数据td-data-stage-1.js源代码:
var _stage_main_init = function () {
var act = new TD.Act(this, “act-1”),
scene = new TD.Scene(act, “scene-1”),
cfg = TD.getDefaultStageData(“scene_endless”);
this.config = cfg.config;
TD.life = this.config.life;
TD.money = this.config.money;
TD.score = this.config.score;
TD.difficulty = this.config.difficulty;
TD.wave_damage = this.config.wave_damage;
// make map
var map = new TD.Map("main-map", TD.lang.mix({
scene: scene,
is_main_map: true,
step_level: 1,
render_level: 2
}, cfg.map));
map.addToScene(scene, 1, 2, map.grids);
scene.map = map;
// make panel
scene.panel = new TD.Panel("panel", TD.lang.mix({
scene: scene,
main_map: map,
step_level: 1,
render_level: 7
}, cfg.panel));
this.newWave = cfg.newWave;
this.map = map;
this.wait_new_wave = this.config.wait_new_wave;
},
_stage_main_step2 = function () {
//TD.log(this.current_act.current_scene.wave);
var scene = this.current_act.current_scene,
wave = scene.wave;
if ((wave == 0 && !this.map.has_weapon) || scene.state != 1) {
return;
}
if (this.map.monsters.length == 0) {
if (wave > 0 && this.wait_new_wave == this.config.wait_new_wave - 1) {
// 一波怪物刚刚走完
// 奖励生命值
var wave_reward = 0;
if (wave % 10 == 0) {
wave_reward = 10;
} else if (wave % 5 == 0) {
wave_reward = 5;
}
if (TD.life + wave_reward > 100) {
wave_reward = 100 - TD.life;
}
if (wave_reward > 0) {
TD.recover(wave_reward);
}
}
if (this.wait_new_wave > 0) {
this.wait_new_wave --;
return;
}
this.wait_new_wave = this.config.wait_new_wave;
wave ++;
scene.wave = wave;
this.newWave({
map: this.map,
wave: wave
});
}
};
TD.getDefaultStageData = function (k) {
var data = {
stage_main: {
width: 640, // px
height: 560,
init: _stage_main_init,
step2: _stage_main_step2
},
scene_endless: {
// scene 1
map: {
grid_x: 16,
grid_y: 16,
x: TD.padding,
y: TD.padding,
entrance: [0, 0],
exit: [15, 15],
grids_cfg: [
{
pos: [3, 3],
//building: "cannon",
passable_flag: 0
},
{
pos: [7, 15],
build_flag: 0
},
{
pos: [4, 12],
building: "wall"
},
{
pos: [4, 13],
building: "wall"
//}, {
//pos: [11, 9],
//building: "cannon"
//}, {
//pos: [5, 2],
//building: "HMG"
//}, {
//pos: [14, 9],
//building: "LMG"
//}, {
//pos: [3, 14],
//building: "LMG"
}
]
},
panel: {
x: TD.padding * 2 + TD.grid_size * 16,
y: TD.padding,
map: {
grid_x: 3,
grid_y: 3,
x: 0,
y: 110,
grids_cfg: [
{
pos: [0, 0],
building: "cannon"
},
{
pos: [1, 0],
building: "LMG"
},
{
pos: [2, 0],
building: "HMG"
},
{
pos: [0, 1],
building: "laser_gun"
},
{
pos: [2, 2],
building: "wall"
}
]
}
},
config: {
endless: true,
wait_new_wave: TD.exp_fps * 3, // 经过多少 step 后再开始新的一波
difficulty: 1.0, // 难度系数
wave: 0,
max_wave: -1,
wave_damage: 0, // 当前一波怪物造成了多少点生命值的伤害
max_monsters_per_wave: 100, // 每一波最多多少怪物
money: 500,
score: 0, // 开局时的积分
life: 100,
waves: [ // 这儿只定义了前 10 波怪物,从第 11 波开始自动生成
[],
// 第一个参数是没有用的(第 0 波)
// 第一波
[
[1, 0] // 1 个 0 类怪物
],
// 第二波
[1, 0], // 1 个 0 类怪物
[1, 1] // 1 个 1 类怪物
],
// wave 3
[
[2, 0], // 2 个 0 类怪物
[1, 1] // 1 个 1 类怪物
],
// wave 4
[
[2, 0],
[1, 1]
],
// wave 5
[
[3, 0],
[2, 1]
],
// wave 6
[
[4, 0],
[2, 1]
],
// wave 7
[
[5, 0],
[3, 1],
[1, 2]
],
// wave 8
[
[6, 0],
[4, 1],
[1, 2]
],
// wave 9
[
[7, 0],
[3, 1],
[2, 2]
],
// wave 10
[
[8, 0],
[4, 1],
[3, 2]
]
]
},
newWave: function (cfg) {
cfg = cfg || {};
var map = cfg.map,
wave = cfg.wave || 1,
//difficulty = TD.difficulty || 1.0,
wave_damage = TD.wave_damage || 0;
// 自动调整难度系数
if (wave == 1) {
//pass
} else if (wave_damage == 0) {
// 没有造成伤害
if (wave < 5) {
TD.difficulty *= 1.05;
} else if (TD.difficulty > 20) {
TD.difficulty *= 1.05;
} else if (TD.difficulty > 10) {
TD.difficulty *= 1.1;
} else {
TD.difficulty *= 1.2;
}
} else if (TD.wave_damage >= 50) {
TD.difficulty *= 0.7;
} else if (TD.wave_damage >= 20) {
TD.difficulty *= 0.8;
} else if (TD.wave_damage >= 10) {
TD.difficulty *= 0.9;
} else {
// 造成了 10 点以内的伤害
if (wave >= 10)
TD.difficulty *= 1.05;
}
TD.log("wave " + wave + ", last wave damage = " + wave_damage + ", difficulty = " + TD.difficulty
//map.addMonsters(100, 7);
//map.addMonsters2([[10, 7], [5, 0], [5, 5]]);
//
var wave_data = this.config.waves[wave] ||
// 自动生成怪物
TD.makeMonsters(Math.min(
Math.floor(Math.pow(wave, 1.1)),
this.config.max_monsters_per_wave
));
map.addMonsters2(wave_data);
TD.wave_damage = 0;
}
} // end of scene_endless
};
return data[k] || {};
};
游戏状态属性td-stage.js源代码:
TD.Stage = function (id, cfg) {
this.id = id || (“stage-” + TD.lang.rndStr());
this.cfg = cfg || {};
this.width = this.cfg.width || 600;
this.height = this.cfg.height || 540;
this.mode = "normal";
this.state = 0;
this.acts = [];
this.current_act = null;
this._step2 = TD.lang.nullFunc;
this._init();
};
TD.Stage.prototype = {
_init: function () {
if (typeof this.cfg.init == "function") {
this.cfg.init.call(this);
}
if (typeof this.cfg.step2 == "function") {
this._step2 = this.cfg.step2;
}
},
start: function () {
this.state = 1;
TD.lang.each(this.acts, function (obj) {
obj.start();
});
},
pause: function () {
this.state = 2;
},
gameover: function () {
//this.pause();
this.current_act.gameover();
},
clear: function () {
this.state = 3;
TD.lang.each(this.acts, function (obj) {
obj.clear();
});
// delete this;
},
step: function () {
if (this.state != 1 || !this.current_act) return;
TD.eventManager.step();
this.current_act.step();
this._step2();
},
render: function () {
if (this.state == 0 || this.state == 3 || !this.current_act) return;
this.current_act.render();
},
addAct: function (act) {
this.acts.push(act);
},
addElement: function (el, step_level, render_level) {
if (this.current_act)
this.current_act.addElement(el, step_level, render_level);
}
};
}); // _TD.a.push end
// _TD.a.push begin
_TD.a.push(function (TD) {
TD.Act = function (stage, id) {
this.stage = stage;
this.id = id || ("act-" + TD.lang.rndStr());
this.state = 0;
this.scenes = [];
this.end_queue = [];
this.current_scene = null;
this._init();
};
TD.Act.prototype = {
_init: function () {
this.stage.addAct(this);
},
start: function () {
if (this.stage.current_act && this.stage.current_act.state != 3) {
// queue...
this.state = 0;
this.stage.current_act.queue(this.start);
return;
}
// start
this.state = 1;
this.stage.current_act = this;
TD.lang.each(this.scenes, function (obj) {
obj.start();
});
},
pause: function () {
this.state = 2;
},
end: function () {
this.state = 3;
var f;
while (f = this.end_queue.shift()) {
f();
}
this.stage.current_act = null;
},
queue: function (f) {
this.end_queue.push(f);
},
clear: function () {
this.state = 3;
TD.lang.each(this.scenes, function (obj) {
obj.clear();
});
// delete this;
},
step: function () {
if (this.state != 1 || !this.current_scene) return;
this.current_scene.step();
},
render: function () {
if (this.state == 0 || this.state == 3 || !this.current_scene) return;
this.current_scene.render();
},
addScene: function (scene) {
this.scenes.push(scene);
},
addElement: function (el, step_level, render_level) {
if (this.current_scene)
this.current_scene.addElement(el, step_level, render_level);
},
gameover: function () {
//this.is_paused = true;
//this.is_gameover = true;
this.current_scene.gameover();
}
};
}); // _TD.a.push end.