原文地址:http://www.script-tutorials.com/html5-game-development-lesson-10/

最后我们将继续使用canvas来进行HTML5游戏开发系列的文章。今天我准备了一个新游戏--SkyWalker。这游戏基本上算是一个射击模拟类型的游戏(有飞机和敌人)。我们的目标是到达终点线。该游戏有几个关键的特征:使用了精灵来处理飞机和爆炸,可以按下多个按键(比如你可以移动飞机的同时发射子弹),a certain level length,增强的碰撞检测(敌人可以撞击飞机),飞机的生命值和获得的分数。

之前的翻译文章可以点击这里:http://www.cnblogs.com/pigzhu/p/3281537.html

第一步:HTML

和往常一样,我们有个基本的html文件:

  1. <!DOCTYPE html>
  2. <html lang="en" >
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>HTML5 Game Development - Lesson 10 (SkyWalker) | Script Tutorials</title>
  6. <link href="css/main.css" rel="stylesheet" type="text/css" />
  7.  
  8. <script src="js/jquery-2.0.0.min.js"></script>
  9. <script src="js/script.js"></script>
  10. </head>
  11. <body>
  12. <header tabindex="0">
  13. <h2>HTML5 Game Development - Lesson 10</h2>
  14. <a href="http://www.script-tutorials.com/html5-game-development-lesson-10/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
  15. </header>
  16.  
  17. <div class="container">
  18. <canvas id="scene" width="700" height="700" tabindex="1"></canvas>
  19. </div>
  20. </body>
  21. </html>

第二步:JS

现在,请在文件夹js中创建一个空文件:“script.js”,并把下面的这些代码粘贴到里面去。我将解释主要的功能。

  1. //内部变量
  2. var canvas, ctx;
  3.  
  4. //各种图片
  5. var backgroundImage;
  6. var oRocketImage;
  7. var oExplosionImage;
  8. var introImage;
  9. var oEnemyImage;
  10.  
  11. var iBgShiftY = 9300;
  12. var bPause = true; //游戏是否暂停
  13. var plane = null; //飞机
  14. var rockets = []; //子弹对象的数组
  15. var enemies = []; //敌人对象的数组
  16. var explosions = []; //爆炸对象的数组
  17. var planeW = 200; //飞机的宽度
  18. var planeH = 110; //飞机的高度
  19. var iSprPos = 2; //飞机初始帧的位置
  20. var iMoveDir = 0; //移动的方向
  21. var iEnemyW = 128; //敌人的宽度
  22. var iEnemyH = 128; //敌人的高度
  23. var iRocketSpeed =10; //子弹的速度
  24. var iEnemySpeed = 5; //敌人的速度
  25. var pressedKeys = []; //按键的序列
  26. var iScore = 0; //总得分
  27. var iLife = 100; //生命值
  28. var iDamage = 10; //每次敌人碰到飞机的损耗值
  29. var enTimer = null; //产生敌人的定时器
  30.  
  31. function Plane(x, y, w, h, image) {
  32. this.x = x;
  33. this.y = y;
  34. this.w = w;
  35. this.h = h;
  36. this.image = image;
  37. this.bDrag = false;
  38. }
  39.  
  40. function Rocket(x, y, w, h, speed, image) {
  41. this.x = x;
  42. this.y = y;
  43. this.w = w;
  44. this.h = h;
  45. this.speed = speed;
  46. this.image = image;
  47. }
  48.  
  49. function Enemy(x, y, w, h, speed, image) {
  50. this.x = x;
  51. this.y = y;
  52. this.w = w;
  53. this.h = h;
  54. this.speed = speed;
  55. this.image = image;
  56. }
  57.  
  58. function Explosion(x, y, w, h, sprite, image) {
  59. this.x = x;
  60. this.y = y;
  61. this.w = w;
  62. this.h = h;
  63. this.sprite = sprite;
  64. this.image = image;
  65. }
  66.  
  67. //得到位于x和y之间的随机数
  68. function getRand(x, y) {
  69. return Math.floor(Math.random() * y) + x;
  70. }
  71.  
  72. //显示游戏介绍界面
  73. function displayIntro() {
  74. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  75. ctx.drawImage(introImage, 0, 0, 700, 700);
  76. }
  77.  
  78. //绘制主要的场景
  79. function drawScene() {
  80. if (! bPause) {
  81. iBgShiftY -= 2; //主背景向下移动
  82. if (iBgShiftY < 5) {
  83. bPause = true;
  84.  
  85. //绘制分数
  86. ctx.font = '40px Verdana';
  87. ctx.fillStyle = '#fff';
  88. ctx.fillText('Finish, your score: ' + iSore * 10 + ' points', 50, 200);
  89. return;
  90. }
  91.  
  92. //处理按下的建
  93. processPressedKeys();
  94.  
  95. //清除整个画布
  96. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  97.  
  98. //通过剪切图片绘制背景
  99. ctx.drawImage(backgroundImage, 0, 0 + iBgShiftY, 700, 700, 0, 0, 700, 700);
  100.  
  101. //绘制飞机
  102. ctx.drawImage(plane.image, iSprPos * plane.w, 0, plane.w, plane.h,
  103. plane.x - plane.w / 2, plane.y - plane.h / 2, plane.w, plane.h);
  104.  
  105. //绘制子弹
  106. if (rockets.length > 0) {
  107. for (var key in rockets) {
  108. if (rockets[key] != undefined) {
  109. ctx.drawImage(rockets[key].image, rockets[key].x, rockets[key].y);
  110. rockets[key].y -= rockets[key].speed;
  111.  
  112. if (rockets[key].y < 0) { //如果子弹飞出界面,删除掉
  113. delete rockets[key];
  114. }
  115. }
  116. }
  117. }
  118.  
  119. //绘制爆炸
  120. if (explosions.length > 0) {
  121. for (var key in explosions) {
  122. ctx.drawImage(explosions[key].image, explosions[key].sprite * explosions[key].w, 0, explosions[key].w, explosions[key].h,
  123. explosions[key].x - explosions[key].w / 2, explosions[key].y - explosions[key].h / 2,
  124. explosions[key].w, explosions[key].h);
  125. explosions[key].sprite++;
  126.  
  127. if (explosions[key].sprite > 10) { //如果爆炸播放完,则删除掉
  128. delete explosions[key];
  129. }
  130. }
  131. }
  132.  
  133. //绘制敌人
  134. if (enemies.length > 0) {
  135. for (var ekey in enemies) {
  136. if (enemies[ekey] != undefined) {
  137. ctx.drawImage(enemies[ekey].image, enemies[ekey].x, enemies[ekey].y);
  138. enemies[ekey].y -= enemies[ekey].speed;
  139. }
  140.  
  141. if (enemies[ekey].y > canvas.height) { //如果敌人飞出界面,则删除掉
  142. delete enemies;
  143. }
  144. }
  145. }
  146.  
  147. //处理各种碰撞
  148. if (enemies.length > 0) {
  149. for (var ekey in enemies) {
  150. if (enemies[ekey] != undefined) {
  151.  
  152. //子弹打中敌人
  153. if (rockets.length > 0) {
  154. for (var key in rockets) {
  155. if (rockets[key] != undefined && enemies[ekey] != undefined) {
  156. if (rockets[key].y < enemies[ekey].y + enemies[ekey].h / 2
  157. && rockets[key].x > enemies[ekey].x
  158. && rockets[key].x + rockets[key].w < enemies[ekey].x + enemies[ekey].w) {
  159. explosions.push(new Explosion(enemies[ekey].x + enemies[ekey].w / 2, enemies[ekey].y + enemies[ekey].h / 2,
  160. 120, 120, 0, oExplosionImage));
  161.  
  162. delete enemies[ekey];
  163. delete rockets[key];
  164. iScore++;
  165. }
  166. }
  167. }
  168. }
  169.  
  170. //敌人打中飞机
  171. if (enemies[ekey] != undefined) {
  172. if (plane.y - plane.h / 2 < enemies[ekey].y + enemies[ekey].h / 2
  173. && plane.x - plane.w / 2 < enemies[ekey].x + enemies[ekey].w
  174. && plane.x + plane.w / 2 > enemies[ekey].x) {
  175. explosions.push(new Explosion(enemies[ekey].x + enemies[ekey].w / 2, enemies[ekey].y + enemies[ekey].h / 2,
  176. 120, 120, 0, oExplosionImage));
  177.  
  178. delete enemies[ekey];
  179. iLife -= iDamage;
  180.  
  181. if (iLife <= 0) { //游戏结束
  182. bPause = true;
  183.  
  184. ctx.font = '38px Verdana';
  185. ctx.fillStyle = '#fff';
  186. ctx.fillText('Game voer, your socre: ' + iScore * 10 + ' points', 25, 200);
  187. return;
  188. }
  189. }
  190. }
  191.  
  192. }
  193. }
  194. }
  195.  
  196. //即时显示分数和生命值
  197. ctx.font = '14px Verdana';
  198. ctx.fillStyle = '#fff';
  199. ctx.fillText('life: ' + iLife + ' / 100', 50, 660);
  200. ctx.fillText('Score: ' + iScore * 10, 50, 680);
  201. }
  202. }
  203.  
  204. //处理各种按键事件
  205. function processPressedKeys() {
  206. if (pressedKeys[37] != undefined) {
  207. if (iSprPos > 0) {
  208. iSprPos--;
  209. iMoveDir = -7;
  210. }
  211. if (plane.x - plane.w / 2 > 10) {
  212. plane.x += iMoveDir;
  213. }
  214. } else if (pressedKeys[39] != undefined) {
  215. if (iSprPos < 4) {
  216. iSprPos++;
  217. iMoveDir = 7;
  218. }
  219. if (plane.x + plane.w / 2 < canvas.width - 10) {
  220. plane.x += iMoveDir;
  221. }
  222. }
  223. }
  224.  
  225. //不定时间的增加敌人
  226. function addEnemy() {
  227. clearInterval(enTimer);
  228. var randX = getRand(0, canvas.height - iEnemyH);
  229. enemies.push(new Enemy(randX, 0, iEnemyW, iEnemyH, -iEnemySpeed, oEnemyImage));
  230.  
  231. var interval = getRand(1000, 4000);
  232. enTimer = setInterval(addEnemy, interval);
  233. }
  234.  
  235. // 主初始化
  236. $(function() {
  237. canvas = document.getElementById('scene');
  238. ctx = canvas.getContext('2d');
  239.  
  240. //加载背景图片
  241. backgroundImage = new Image();
  242. backgroundImage.src = 'images/levelmap.jpg';
  243. backgroundImage.onload = function() {}
  244. backgroundImage.onerror = function() {
  245. console.log('Error loading the background image.');
  246. }
  247.  
  248. //初始化介绍图片
  249. introImage = new Image();
  250. introImage.src = 'images/intro.jpg';
  251.  
  252. oRocketImage = new Image();
  253. oRocketImage.src = 'images/rocket.png';
  254. oRocketImage.onload = function(){}
  255.  
  256. oExplosionImage = new Image();
  257. oExplosionImage.src = 'images/explosion.png';
  258. oExplosionImage.onload = function() {}
  259.  
  260. oEnemyImage = new Image();
  261. oEnemyImage.src = 'images/enemy.png';
  262. oEnemyImage.onload = function() {}
  263.  
  264. var oPlaneImage = new Image();
  265. oPlaneImage.src = 'images/plane.png';
  266. oPlaneImage.onload = function() {
  267. plane = new Plane(canvas.width / 2, canvas.height - 100, planeW, planeH, oPlaneImage);
  268. }
  269.  
  270. $(window).keydown(function(evt) { //按下事件处理
  271. var pk = pressedKeys[evt.keyCode];
  272. if (!pk) {
  273. pressedKeys[evt.keyCode] = 1;
  274. }
  275.  
  276. if (bPause && evt.keyCode == 13) { //enter键 开始游戏
  277. bPause = false;
  278. setInterval(drawScene , 30);
  279. addEnemy();
  280. }
  281. });
  282.  
  283. $(window).keyup(function(evt) { //键释放事件处理
  284. var pk = pressedKeys[evt.keyCode];
  285. if (pk) {
  286. delete pressedKeys[evt.keyCode];
  287. }
  288. if (evt.keyCode == 65) { //'A'键发射子弹
  289. rockets.push(new Rocket(plane.x - 16, plane.y - plane.h, 32, 32, iRocketSpeed, oRocketImage));
  290. }
  291. if (evt.keyCode ==37 || evt.keyCode == 39) {
  292. //左右移动 37 左 39 右 但移动完之后,飞机恢复正常状态
  293. if (iSprPos > 2) {
  294. for (var i = iSprPos; i >= 2; i--) {
  295. iSprPos = i;
  296. iMoveDir = 0;
  297. }
  298. } else {
  299. for (var i = iSprPos; i <= 2; i++) {
  300. iSprPos = i;
  301. iMoveDir = 0;
  302. }
  303. }
  304. }
  305. });
  306.  
  307. introImage.onload = function() {
  308. displayIntro();
  309. }
  310. });

在主初始化代码中,加载了所有必需的图片资源(level map, 介绍图片,子弹,爆炸,敌人和飞机)。然后,我们使用了数组来处理多个按键事件(在我们渲染主场景的时候,将会使用这个数组来操作我们的飞机),最后,介绍页被加载--显示界面图片。下面是一个重要的代码片段--怎样处理多个按键,请看下面代码:

  1. var pressedKeys = []; //按键的序列
  2.  
  3. $(window).keydown(function (evt){ //鼠标按下事件处理
  4. var pk = pressedKeys[evt.keyCode];
  5. if (! pk) {
  6. pressedKeys[evt.keyCode] = 1; // 把按下的键添加的按键序列中
  7. }
  8. });
  9.  
  10. $(window).keyup(function (evt) { // 鼠标释放事件处理
  11. var pk = pressedKeys[evt.keyCode];
  12. if (pk) {
  13. delete pressedKeys[evt.keyCode]; // 从按键序列中删除
  14. }
  15. });

这个技术允许我们操作多个按键,在渲染我们主场景时,我们绘制的对象有:背景,飞机,子弹,敌人和爆炸。一旦我们击中敌人,将会在敌人的位置处绘制出爆炸。最后我们的对手是有攻击力的,当他们碰到飞机时,他们会爆炸,同时对飞机造成损伤。如果我们飞机的生命值小于0,则游戏结束。为了实现碰撞和爆炸,我使用了下面的代码:

  1. if (plane.y - plane.h/2 < enemies[ekey].y + enemies[ekey].h/2 && plane.x - plane.w/2 < enemies[ekey].x + enemies[ekey].w && plane.x + plane.w/2 > enemies[ekey].x) {
  2. explosions.push(new Explosion(enemies[ekey].x + enemies[ekey].w / 2, enemies[ekey].y + enemies[ekey].h / 2, 120, 120, 0, oExplosionImage));
  3.  
  4. // 删除敌人,并计算损伤
  5. delete enemies[ekey];
  6. iLife -= iDamage;
  7.  
  8. if (iLife <= 0) { // 游戏结束
  9. bPause = true;
  10.  
  11. // 绘制分数
  12. ctx.font = '38px Verdana';
  13. ctx.fillStyle = '#fff';
  14. ctx.fillText('Game over, your score: ' + iScore * 10 + ' points', 25, 200);
  15. return;
  16. }
  17. }

第三步:Custom graphics

enemy.png, explosion.png, intro.jpg, levelmap.jpg, plane.png, rocket.png

上面所有的图片都在源码包里。

结论

你喜欢我们的新SkyWalker游戏吗? 我非常乐意看见你的谢意和评论。好运!

HTML5游戏开发系列教程10(译)的更多相关文章

  1. HTML5游戏开发系列教程7(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-7/ 今天我们将完成我们第一个完整的游戏--打砖块.这次教程中,将 ...

  2. HTML5游戏开发系列教程6(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-6/ 这是我们最新一篇HTML5游戏开发系列文章.我们将继续使用c ...

  3. HTML5游戏开发系列教程5(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-5/ 最终我决定准备下一篇游戏开发系列的文章,我们将继续使用can ...

  4. HTML5游戏开发系列教程4(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-4/ 这篇文章是我们继续使用canvas来进行HTML5游戏开发系 ...

  5. HTML5游戏开发系列教程8(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-8/ 这是我们最新一篇HTML5游戏开发系列文章.我们将继续使用c ...

  6. HTML5游戏开发系列教程9(译)

    原文地址:http://www.script-tutorials.com/html5-game-development-lesson-9/ 今天我们将继续使用canvas来进行HTML5游戏开发系列的 ...

  7. cocos2d-x游戏开发系列教程-前言

    cocos2d-x游戏开发前景: 最近企业对于Cocos2D-X开发人才的用人需求很大,而且所提供的薪资相当可观. 为满足广大向往游戏开发行业同学的需求,特推出适合新手的Cocos2D-X手游开发教程 ...

  8. unity3D实际的原始视频游戏开发系列讲座10它《战斗:外来入侵》在第一季度游戏开发

    解说文件夹 <保卫战:异形入侵>游戏开发 第一讲   游戏演示和资源的介绍 第二讲  "异形"怪物的实现 第三讲  "异形"怪物生命值的体现 第四讲 ...

  9. cocos2d-x游戏开发系列教程-超级玛丽07-CMGameMap

    背景 在上一篇博客中,我们提到CMGameScene,但是CMGameScene只是个框架,实际担任游戏逻辑的是CMGameMap类,这个博文就来了解下CMGameMap 头文件 class CMGa ...

随机推荐

  1. MySQL<多表操作>

    多表操作 外键 什么是外键 外键是指引用另一个表中的一列或多列,被引用的列应该具有主键约束或唯一性约束. 外键用于建立和加强两个表数据之间的链接. 为表添加外键约束 想要真正连接两个表的数据,就需要为 ...

  2. 如何配置samba 要求共享文件夹public

    第一步:在根下执行:mkdir /public 修改下权限:chmod 777 /public第二步:vi /etc/samba/smb.conf修改如下配置:修改security = user为se ...

  3. beginUpdates和endUpdates-实现UITableView的动画块

    我们在做UITableView的修改,删除,选择时,需要对UITableView进行一系列的动作操作. 这样,我们就会用到 [tableView beginUpdates]; if (newCount ...

  4. UVa 673 Parentheses Balance (stack)

    题目描述 : 判断字符串是不是符合正确的表达式形式. 要点 : 考虑字符串为空的时候,用getline输入,每一次判断后如果为No则要清空栈.对称思想. 注意输入格式. 代码: #include &l ...

  5. [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法

    简介 一本为完善<设计篇>的书,教你CORDIC算法以及定点数等,内容请看目录. 贴士 这本教程难度略高,请先用<时序篇>垫底. 目录 Experiment 01:认识CORD ...

  6. Object.prototype.toString()

    Object.prototype.toString()方法返回一个代表该对象的字符串. var o = new Object(); o.toString(); //"[object Obje ...

  7. Lucene构建索引时的一些概念和索引构建的过程

    在搜索文档内容之前要做的事情就是对从各种不同来源(网页,数据库,电子邮件等)的文档进行索引,索引的过程就是对内容进行提取,规范化(通过对内容进行建模来实现),然后存储. 在索引的过程中有几个基本的概念 ...

  8. [IIS] IIS Framework "aspnet_regiis.exe" 注册

    Error:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“

  9. ios UITableView默认选中第一行

    NSIndexPath *ip = [NSIndexPath indexPathForRow:0inSection:0]; [titleTableViewselectRowAtIndexPath:ip ...

  10. windows MySQL5.7.9免安装版配置方法

    1. 解压MySQL压缩包    将下载的MySQL压缩包解压到自定义目录下,我的解压目录是:    "D:\Program Files\mysql-5.7.9-win32"    ...