用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>