原文地址: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文件:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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游戏吗? 我非常乐意看见你的谢意和评论。好运!