用JavaScript来实现一个鼠标指针控制的飞机大战小程序,效果图如下。
1.进入页面阶段
2.第二载入阶段效果图
3.第三核心阶段
4.第四暂停阶段
5.第五结束阶段
实现这个项目的HTML代码只需要一行,创建一个画布并且设置宽高到合适,并且要将画布设置为块级元素。
<canvas id="canvas" width="480" height="650"></canvas>
由上图可知,这个游戏分为4个阶段,在写项目之前先分析一下每个项目都需要做什么工作。
1.第一阶段:加载背景图片,然后不停的运动,并且在图片上加一个logo。
2.第二阶段是游戏的过度阶段,加载过度界面,出现一个小飞机飘过。
3.第三阶段是游戏的核心阶段。
3.1设置自己的英雄飞机的的动态效果和移动方法,并且考虑到飞机发生碰撞的情况进行判断。
3.2 子弹的绘制以及子弹的运动,还有子弹碰到敌方飞机的时候的改变。
3.3 设置地方飞机的绘制和移动,以及敌方飞机的各个属性和与子弹发生碰撞的情况,和与英雄飞机发生碰撞的情况。
4.第四阶段判断鼠标一处画布的时候游戏暂停
5.当生命值为0的时候进入游戏的第五阶段,GAMEOVER。
下面是这个项目的核心JavaScript代码
1 var canvas = document.getElementById("canvas"); 2 var context = canvas.getContext("2d"); 3 4 // 0 游戏初始化的一些数据 5 // 0.1 把上面游戏的五个阶段整理成数字 6 const START = 0; 7 const STARTTING = 1; 8 const RUNNING = 2; 9 const PAUSE = 3; 10 const GAMEOVER = 4; 11 // 0.2 定义一个自己的状态,时刻跟上面的五个状态进行比较,然后判断游戏目前处于哪个阶段 12 var state = START; 13 // 0.3 画布的信息得获取过来 14 const WIDTH = 480; 15 const HEIGHT = 650; 16 // 0.4 游戏的分数 17 var score = 0; 18 // 0.5 我方飞机的生命 19 var life = 3; 20 // 1 第一阶段 游戏欢迎阶段 21 // 1.1 加载背景图片 22 // 1.1.1 创建背景图片的dom对象 23 var bg = new Image(); 24 bg.src = "images/background.png"; 25 // 1.1.2 背景图片的详细信息(用对象表示) 26 var BG = { 27 imgs : bg, 28 width : 480, 29 height : 852 30 } 31 // 1.1.3 自定义构造函数,构造背景图片的 32 function Bg(config){ 33 this.imgs = config.imgs; 34 this.width = config.width; 35 this.height = config.height; 36 // 定义绘制背景图片的坐标 37 this.x1 = 0; 38 this.y1 = 0; 39 this.x2 = 0; 40 this.y2 = -this.height; 41 // 定义绘制方法 42 this.paint = function(){ 43 context.drawImage(this.imgs,this.x1,this.y1); 44 context.drawImage(this.imgs,this.x2,this.y2); 45 } 46 //图片要运动 47 this.step = function(){ 48 this.y1++; 49 this.y2++; 50 // 判断图片的临界值 51 if(this.y1 == this.height){ 52 this.y1 = -this.height; 53 } 54 if(this.y2 == this.height){ 55 this.y2 = -this.height; 56 } 57 } 58 } 59 // 1.1.4 创建背景图片的对象 60 var abc = new Bg(BG) 61 // 1.2 加载LOGO 62 var logo = new Image(); 63 logo.src = "images/start.png"; 64 // 2 第二阶段 游戏过渡阶段 65 // 2.1 创建图片的构造 66 var loadings = []; 67 loadings[0] = new Image(); 68 loadings[0].src = "images/game_loading1.png"; 69 loadings[1] = new Image(); 70 loadings[1].src = "images/game_loading2.png"; 71 loadings[2] = new Image(); 72 loadings[2].src = "images/game_loading3.png"; 73 loadings[3] = new Image(); 74 loadings[3].src = "images/game_loading4.png" 75 // 2.2 图片的详细信息 76 var LOADINGS = { 77 imgs : loadings, 78 length : loadings.length, 79 width : 186, 80 height : 38 81 } 82 // 2.3 动画效果的构造 83 function Loading(config){ 84 this.imgs = config.imgs; 85 this.length = config.length; 86 this.width = config.width; 87 this.height = config.height; 88 // 在数组中去寻找图片。得定义一个索引。 89 this.startIndex = 0; 90 // 开始绘制 91 this.paint = function(){ 92 context.drawImage(this.imgs[this.startIndex],0,HEIGHT - this.height); 93 } 94 // 定义一个速度 95 this.time = 0; 96 // 运动方法 97 this.step = function(){ 98 this.time ++; 99 if(this.time % 5 == 0){ 100 this.startIndex ++; 101 } 102 // 临界点,图片加载完成以后,到第三阶段去 103 if(this.startIndex == this.length){ 104 state = RUNNING; 105 } 106 } 107 } 108 // 2.4 动画效果的对象 109 var loading = new Loading(LOADINGS); 110 // 2.5 onclick 111 canvas.onclick = function(){ 112 if(state == START){ 113 state = STARTTING; 114 } 115 } 116 // 3 第三阶段 游戏运行中 117 // 3.1 绘制我方飞机 118 // 3.1.1 加载我方飞机的图片(1.飞机正常运行的状态,2.飞机碰撞以后的状态) 119 var heros = []; 120 heros[0] = new Image(); 121 heros[0].src = "images/hero1.png"; 122 heros[1] = new Image(); 123 heros[1].src = "images/hero2.png"; 124 125 heros[2] = new Image(); 126 heros[2].src = "images/hero_blowup_n1.png"; 127 heros[3] = new Image(); 128 heros[3].src = "images/hero_blowup_n2.png"; 129 heros[4] = new Image(); 130 heros[4].src = "images/hero_blowup_n3.png"; 131 heros[5] = new Image(); 132 heros[5].src = "images/hero_blowup_n4.png"; 133 // 3.1.2 初始化我方飞机的数据 134 var HEROS = { 135 imgs : heros, 136 length : heros.length, 137 width : 99, 138 height : 124, 139 frame : 2 //添加一个状态 140 } 141 // 3.1.3 我方飞机的构造函数 142 function Hero(config){ 143 this.imgs = config.imgs; 144 this.length = config.length; 145 this.width = config.width; 146 this.height = config.height; 147 this.frame = config.frame; 148 // 定义索引值 149 this.startIndex = 0; 150 // 定义飞机的坐标 151 this.x = WIDTH/2 - this.width/2; 152 this.y = HEIGHT - 150; 153 // 增加一个标识,表示飞机是否发生了碰撞,给个false,表示一直没有碰撞 154 this.down = false; 155 // 增加一个标识,表示飞机碰撞以后,碰撞的动画,碰撞的动画是否执行完成 156 this.candel = false; 157 158 // 绘制方法 159 this.paint = function(){ 160 context.drawImage(this.imgs[this.startIndex],this.x,this.y); 161 } 162 // 运动方法 163 this.step = function(){ 164 // 监测飞机是否碰撞的属性,如果没有碰撞,索引在0和1之间切换 165 if(!this.down){ 166 // 没有碰撞,切换索引 167 if(this.startIndex == 0){ 168 this.startIndex = 1; 169 }else{ 170 this.startIndex = 0; 171 } 172 }else{ 173 // 飞机发生了碰撞 174 this.startIndex++; 175 if(this.startIndex == this.length){ 176 life -- ; 177 if(life == 0){ 178 state = GAMEOVER; 179 this.startIndex = this.length - 1; 180 }else{ 181 hero = new Hero(HEROS); 182 } 183 } 184 } 185 } 186 this.time = 0; 187 //射击方法 188 this.shoot = function(){ 189 this.time ++; 190 if(this.time % 4 == 0){ 191 bullets.push(new Bullet(BULLET)); 192 } 193 } 194 this.bang = function(){ 195 this.down = true; 196 } 197 } 198 // 3.1.4 创建对象 199 var hero = new Hero(HEROS); 200 // 3.1.5 飞机跟随鼠标移动 201 canvas.onmousemove = function(event){ 202 if(state == RUNNING){ 203 var x = event.offsetX; 204 var y = event.offsetY; 205 // 直接赋值给飞机的x和y坐标 206 hero.x = x - hero.width/2; 207 hero.y = y - hero.height/2; 208 } 209 } 210 // 3.2 绘制子弹 211 // 3.2.1 加载子弹的图片 212 var bullet = new Image(); 213 bullet.src = "images/bullet1.png"; 214 // 3.2.2 初始化子弹的数据 215 var BULLET = { 216 imgs : bullet, 217 width : 9, 218 height : 21 219 } 220 // 3.2.3 子弹的构造函数 221 function Bullet(config){ 222 this.imgs = config.imgs; 223 this.width = config.width; 224 this.height = config.height; 225 // 坐标 226 this.x = hero.x + hero.width/2 - this.width/2; 227 this.y = hero.y - this.height; 228 // 绘制 229 this.paint = function(){ 230 context.drawImage(this.imgs,this.x,this.y); 231 } 232 // 运动 233 this.step = function(){ 234 this.y -= 10; 235 } 236 // 加上一个标识,标识子弹是否发生碰撞 237 this.candel = false; 238 // 撞击的方法,用于修改子弹是否碰撞的属性 239 this.bang = function(){ 240 this.candel = true; 241 } 242 } 243 // 3.2.4 增加一个数组,用来存储子弹 244 var bullets = []; 245 // 3.2.5 绘制数组里面的所有的子弹 246 function bulletsPaint(){ 247 for(var i = 0;i < bullets.length;i++){ 248 bullets[i].paint() 249 } 250 } 251 // 3.2.6 绘制数组里面的所有的子弹的运动 252 function bulletsStep(){ 253 for(var i = 0;i < bullets.length;i++){ 254 bullets[i].step() 255 } 256 } 257 // 3.2.7 当子弹移出画布的时候和发生碰撞以后,要把子弹从数组中删除 258 function bulletsDel(){ 259 for(var i = 0;i < bullets.length;i++){ 260 if(bullets[i].y < -bullets[i].height || bullets[i].candel){ 261 bullets.splice(i,1); 262 } 263 } 264 } 265 // 3.3 绘制地方飞机 266 // 3.3.1 加载敌方飞机的图片(3种) 267 // 小飞机 268 var enemy1 = []; 269 enemy1[0] = new Image(); 270 enemy1[0].src = "images/enemy1.png"; 271 enemy1[1] = new Image(); 272 enemy1[1].src = "images/enemy1_down1.png"; 273 enemy1[2] = new Image(); 274 enemy1[2].src = "images/enemy1_down2.png"; 275 enemy1[3] = new Image(); 276 enemy1[3].src = "images/enemy1_down3.png"; 277 enemy1[4] = new Image(); 278 enemy1[4].src = "images/enemy1_down4.png"; 279 // 中飞机 280 var enemy2 = []; 281 enemy2[0] = new Image(); 282 enemy2[0].src = "images/enemy2.png"; 283 enemy2[1] = new Image(); 284 enemy2[1].src = "images/enemy2_down1.png"; 285 enemy2[2] = new Image(); 286 enemy2[2].src = "images/enemy2_down2.png"; 287 enemy2[3] = new Image(); 288 enemy2[3].src = "images/enemy2_down3.png"; 289 enemy2[4] = new Image(); 290 enemy2[4].src = "images/enemy2_down4.png"; 291 // 大飞机 292 var enemy3 = []; 293 enemy3[0] = new Image(); 294 enemy3[0].src = "images/enemy3_n1.png"; 295 enemy3[1] = new Image(); 296 enemy3[1].src = "images/enemy3_n2.png"; 297 enemy3[2] = new Image(); 298 enemy3[2].src = "images/enemy3_down1.png"; 299 enemy3[3] = new Image(); 300 enemy3[3].src = "images/enemy3_down2.png"; 301 enemy3[4] = new Image(); 302 enemy3[4].src = "images/enemy3_down3.png"; 303 enemy3[5] = new Image(); 304 enemy3[5].src = "images/enemy3_down4.png"; 305 enemy3[6] = new Image(); 306 enemy3[6].src = "images/enemy3_down5.png"; 307 enemy3[7] = new Image(); 308 enemy3[7].src = "images/enemy3_down6.png"; 309 // 3.3.2 初始化敌方飞机的数据 310 var ENEMY1 = { 311 imgs : enemy1, 312 length : enemy1.length, 313 width : 57, 314 height : 51, 315 type : 1, //增加一个类型,判断是哪一种飞机 316 frame : 1, 317 life : 1, 318 score : 1 319 } 320 var ENEMY2 = { 321 imgs : enemy2, 322 length : enemy2.length, 323 width : 69, 324 height : 95, 325 type : 2, //增加一个类型,判断是哪一种飞机 326 frame : 1, 327 life : 5, 328 score : 5 329 } 330 var ENEMY3 = { 331 imgs : enemy3, 332 length : enemy3.length, 333 width : 169, 334 height : 258, 335 type : 3, //增加一个类型,判断是哪一种飞机 336 frame : 2, 337 life : 10, 338 score : 10 339 } 340 // 3.3.3 敌方飞机的构造函数 341 function Enemy(config){ 342 this.imgs = config.imgs; 343 this.length = config.length; 344 this.width = config.width; 345 this.height = config.height; 346 this.type = config.type; 347 this.frame = config.frame; 348 this.life = config.life; 349 this.score = config.score; 350 // 坐标 351 this.x = Math.random() * (WIDTH - this.width); 352 this.y = -this.height; 353 // 索引 354 this.startIndex = 0; 355 // 增加一个标识,表示飞机是否发生了碰撞,给个false,表示一直没有碰撞 356 this.down = false; 357 // 增加一个标识,表示飞机碰撞以后,碰撞的动画,碰撞的动画是否执行完成 358 this.candel = false; 359 // 绘制 360 this.paint = function(){ 361 context.drawImage(this.imgs[this.startIndex],this.x,this.y); 362 } 363 // 运动 364 this.step = function(){ 365 if(!this.down){ 366 // 根据飞机的状态来判定飞机是否由动画,就是要大飞机有动画效果 367 this.startIndex ++; 368 // 小飞机和中飞机就是0,大飞机是在0和1之间切换 369 this.startIndex = this.startIndex % this.frame; 370 this.y ++; 371 }else{ 372 this.startIndex++; 373 if(this.startIndex == this.length){ 374 this.candel = true; 375 this.startIndex = this.length - 1; 376 } 377 } 378 } 379 this.checkHit = function(zd){ //这个参数可能是子弹,可能是我方飞机 380 return zd.y + zd.height > this.y 381 && zd.x + zd.width > this.x 382 && zd.y < this.y + this.height 383 && zd.x < this.x + this.width 384 } 385 // 撞击的方法,用于修改飞机是否碰撞的属性 386 this.bang = function(){ 387 this.life -- ; 388 if(this.life == 0){ 389 this.down = true; 390 score += this.score; 391 } 392 } 393 } 394 // 3.3.4 创建数组,用于存储敌方飞机 395 var enemies = []; 396 // 3.3.5 数组中去添加飞机 397 function pushEnemies(){ 398 var numRand = Math.floor(Math.random() * 100); 399 if(numRand < 10){ 400 enemies.push(new Enemy(ENEMY1)) 401 }else if(numRand > 98){ 402 enemies.push(new Enemy(ENEMY2)) 403 }else if(numRand == 50){ 404 enemies.push(new Enemy(ENEMY3)) 405 } 406 } 407 // 3.3.6 敌方飞机的绘制函数 408 function paintEnemies(){ 409 for(var i = 0;i < enemies.length;i++){ 410 enemies[i].paint() 411 } 412 } 413 // 3.3.7 敌方飞机的运动函数 414 function stepEnemies(){ 415 for(var i = 0;i < enemies.length;i++){ 416 enemies[i].step() 417 } 418 } 419 // 3.3.8 敌方飞机的删除函数 420 function delEnemies(){ 421 for(var i = 0;i < enemies.length;i++){ 422 // 两种情况 423 if(enemies[i].y > HEIGHT || enemies[i].candel){ 424 enemies.splice(i,1); 425 } 426 } 427 } 428 // 3.4 检测是否撞击 429 function hitEnemies(){ 430 for(var i = 0;i < enemies.length;i++){ 431 // 自己飞机撞 432 if(enemies[i].checkHit(hero)){ 433 enemies[i].bang(); 434 hero.bang(); 435 } 436 // 子弹撞 437 for(var j = 0;j < bullets.length;j++){ 438 if(enemies[i].checkHit(bullets[j])){ 439 enemies[i].bang(); 440 bullets[j].bang(); 441 } 442 } 443 } 444 } 445 // 3.5 文本函数 446 function paintText(){ 447 context.font = "bold 30px 微软雅黑"; 448 context.fillText("SCORE:" + score,20,20); 449 context.fillText("LIFE:" + life,300,20); 450 } 451 452 // 4 第四阶段 游戏暂停 453 canvas.onmouseout = function(){ 454 if(state == RUNNING){ 455 state = PAUSE; 456 } 457 } 458 canvas.onmouseover = function(){ 459 if(state == PAUSE){ 460 state = RUNNING; 461 } 462 } 463 var pause = new Image(); 464 pause.src = "images/game_pause_nor.png"; 465 // 5 第五阶段 游戏GG 466 function paintOver(){ 467 context.font = "bold 50px 微软雅黑"; 468 context.fillText("GAME OVER!!!",50,250); 469 } 470 471 472 473 474 475 setInterval(function(){ 476 abc.paint(); 477 abc.step(); 478 switch (state) { 479 case START: 480 context.drawImage(logo,40,0) 481 break; 482 483 case STARTTING: 484 loading.paint(); 485 loading.step(); 486 break; 487 488 case RUNNING: 489 hero.paint(); 490 hero.step(); 491 hero.shoot(); 492 493 bulletsPaint(); 494 bulletsStep(); 495 bulletsDel(); 496 497 pushEnemies(); 498 paintEnemies(); 499 stepEnemies(); 500 delEnemies(); 501 502 hitEnemies(); 503 504 paintText(); 505 break; 506 507 case PAUSE: 508 hero.paint(); 509 510 bulletsPaint(); 511 512 paintEnemies(); 513 514 paintText(); 515 516 context.drawImage(pause,150,350); 517 break; 518 519 case GAMEOVER: 520 hero.paint(); 521 522 bulletsPaint(); 523 524 paintEnemies(); 525 526 paintText(); 527 528 paintOver() 529 break; 530 531 } 532 },10) 533 534 </script>