图2.1 游戏流程图
2.3 功能分析
本次系统主要设计的是一款塔防策略型游戏,主要实现的大功能点有以下几个:玩家放置建筑、释放怪物使其按照轨迹行动、建筑物进行攻击和建筑物升级或出售。
开发者与玩家可使用的功能不同。目前项目暂时开放的玩家功能有限,为了简化玩家进行游戏时的操作步骤,设置了如下几个功能,玩家用户可以选择开始游戏、选择建筑物并放置于地图中某一位置、升级建筑物、出售建筑物、暂停、继续或者重新开始游戏,玩家的用例图如图2.2所示:
图2.2 玩家用例图
开发者是可以通过更改后台数据来更改游戏数据,比如游戏初始金钱数和生命值、怪物的属性包括攻击力和移动速度以及移动的轨迹、建筑物的属性包括攻击力、攻击速度和攻击范围、建筑物升级所需的金钱数、出售建筑物的收入以及游戏地图的设计,用例图如2.3所示:
图2.3 开发者用例图
同时本系统设计保有一定的可拓展性与可维护性。由于系统处理能力的需求不是一成不变的,随着功能的不断拓展,对系统处理能力的要求也会越来越高。而本项目的可拓展性就是在日后数据的不断更新中,为开发者留有一定升级数据的空间,可以根据将来的变化进行实时的更新与升级。
维护管理是指为了保证维护质量、提高维护效率、控制维护成本而进行的维护过程管理,要求对项目的每次“修改”均需经过申请、评估、批准、实施、验证等步骤。维护的核心是维护评估和维护验证。
而针对本游戏的可维护性就是指在日后一旦出现某一些玩家无法克服的问题那么这时候就需要后台人员对该系统进行一个定时定期的维护以保证用户良好的使用体验。
2.4 本章小结
本章主要介绍了关于系统的需求分析以及用例分析,结合图示进行分别介绍,对系统中的不同角色也就是玩家与开发者可以使用的不同功能点进行分析介绍,同时介绍了系统保有的可拓展性和可维护性。
3.1 技术支持
初步设计游戏考虑到技术支持方面的问题,有些浏览器可能会出现不兼容的问题,为了减少开发者前期设计框架的麻烦,故而游戏目前只支持在IE9和Chrome浏览器中进行[13],使用Canvas标签的属性提示用户当前浏览器不支持,其代码设计如下:
加载中… 抱歉,您的浏览器不支持 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 积分 0
3.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.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.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源代码:
加载中...抱歉,您的浏览器不支持 HTML 5 Canvas 标签,请使用 IE9 / Chrome 等浏览器浏览本页以获得最佳效果。关于 |源码 |Rainy ©
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
// 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: 0name: "monster 1",desc: "最弱小的怪物",speed: 3,max_speed: 10,life: 50,damage: 1,shield: 0,money: 5},{// idx: 1name: "monster 2",desc: "稍强一些的小怪",speed: 6,max_speed: 20,life: 50,damage: 5,shield: 1},{// idx: 2name: "monster speed",desc: "速度较快的小怪",speed: 12,max_speed: 30,life: 50,damage: 6,shield: 1},{// idx: 3name: "monster life",desc: "生命值很强的小怪",speed: 5,max_speed: 10,life: 500,damage: 7,shield: 1},{// idx: 4name: "monster shield",desc: "防御很强的小怪",speed: 5,max_speed: 10,life: 50,damage: 7,shield: 50},{// idx: 5name: "monster damage",desc: "伤害值很大的小怪",speed: 7,max_speed: 14,life: 50,damage: 15,shield: 2},{// idx: 6name: "monster speed-life",desc: "速度、生命都较高的怪物",speed: 15,max_speed: 30,life: 100,damage: 5,shield: 3},{// idx: 7name: "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 mapvar 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 panelscene.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, // pxheight: 560,init: _stage_main_init,step2: _stage_main_step2},scene_endless: {// scene 1map: {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 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;}// startthis.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.