5 " />
好家伙,
这是未进行分包的vue版本的飞机大战
效果如下:
这里说明一下,大概使用逻辑是提供一个<div>
然后在这<div>中渲染游戏
游戏主界面代码如下:
1 <template> 2 <div> 3 <h1>欢迎来到主页面</h1> 4 <div ref="stage"></div> 5 </div> 6 </template> 7 8 <script> 9 export default { 10 mounted() { 11 //canvas初始化 12 console.log("我被执行啦") 13 let canvas = document.createElement('canvas'); 14 this.$refs.stage.appendChild(canvas); 15 canvas.width = 480; 16 canvas.height = 650; 17 canvas.ref = canvas; 18 canvas.style = "border: 1px solid red;" 19 const context = canvas.getContext("2d"); 20 21 //图片初始化方法 22 function createImage(src) { 23 let img; 24 if (typeof src === "string") { 25 img = new Image(); 26 img.src = require('./img/' + src); 27 } else { 28 img = []; 29 for (let i = 0; i < src.length; i++) { 30 img[i] = new Image(); 31 img[i].src = require('./img/' + src[i]); 32 } 33 } 34 return img; 35 } 36 //createImage()方法测试样例 37 // let bg = createImage("4.jpg") 38 // bg.onload = function () { 39 // console.log("img加载完毕") 40 // context.drawImage(bg, 0, 0, 480, 650) 41 // } 42 const IMAGES = { 43 b: "bullet1.png", 44 bg: "4.png", 45 copyright: "shoot_copyright.png", 46 pause: "game_pause.png", 47 loading_frame: ["game_loading1.png", "game_loading2.png", "game_loading3.png", 48 "game_loading4.png" 49 ], 50 hero_frame_live: ["hero1.png", "hero2.png"], 51 hero_frame_death: ["hero_blowup_n1.png", "hero_blowup_n2.png", "hero_blowup_n3.png", 52 "hero_blowup_n4.png" 53 ], 54 e1_live: ["enemy1.png"], 55 e1_death: ["enemy1_down1.png", "enemy1_down2.png", "enemy1_down3.png", "enemy1_down4.png"], 56 e2_live: ["enemy2.png"], 57 e2_death: ["enemy2_down1.png", "enemy2_down2.png", "enemy2_down3.png", "enemy2_down4.png"], 58 e3_live: ["enemy3_n1.png", "enemy3_n2.png"], 59 e3_death: ["enemy3_down1.png", "enemy3_down2.png", "enemy3_down3.png", "enemy3_down4.png", 60 "enemy3_down5.png", "enemy3_down6.png" 61 ], 62 c1: "lanqiu.png" 63 }; 64 //初始化各个图片 65 const b = createImage(IMAGES.b); 66 const bg = createImage(IMAGES.bg); 67 const copyright = createImage(IMAGES.copyright); 68 const pause = createImage(IMAGES.pause); 69 const loading_frame = createImage(IMAGES.loading_frame); 70 const hero_frame = { 71 live: createImage(IMAGES.hero_frame_live), 72 death: createImage(IMAGES.hero_frame_death), 73 }; 74 const e1 = { 75 live: createImage(IMAGES.e1_live), 76 death: createImage(IMAGES.e1_death), 77 }; 78 const e2 = { 79 live: createImage(IMAGES.e2_live), 80 death: createImage(IMAGES.e2_death), 81 }; 82 const e3 = { 83 live: createImage(IMAGES.e3_live), 84 death: createImage(IMAGES.e3_death), 85 }; 86 const c1 = createImage(IMAGES.c1); 87 88 //配置项: 89 // 定义游戏的状态 90 // 开始 91 const START = 0; 92 // 开始时 93 const STARTING = 1; 94 // 运行时 95 const RUNNING = 2; 96 // 暂停时 97 const PAUSE = 3; 98 // 结束时 99 const END = 4; 100 // 加载中 101 const LOADINGING = 5; 102 103 //state表示游戏的状态 取值必须是以上的五种状态 104 let state = LOADINGING; 105 // hero_frame.addEventListener("load", () => { 106 // state = START; 107 // }) 108 109 pause.onload = function () { 110 state = START; 111 console.log(state) 112 } 113 114 //score 分数变量 life 变量 115 let score = 0; 116 let life = 3; 117 118 //天空类的配置项 119 const SKY = { 120 bg: bg, 121 width: 480, 122 height: 650, 123 speed: 10, 124 }; 125 126 // 飞机加载界面的配置项 127 const LOADING = { 128 frame: loading_frame, 129 width: 186, 130 height: 38, 131 x: 0, 132 y: 650 - 38, 133 speed: 400, 134 }; 135 136 // 英雄配置项 137 const HERO = { 138 frame: hero_frame, 139 width: 99, 140 height: 124, 141 speed: 100, 142 }; 143 144 // 子弹配置项 145 const BULLET = { 146 img: b, 147 width: 9, 148 height: 21, 149 }; 150 151 //小敌机配置项 152 const E1 = { 153 type: 1, 154 width: 57, 155 height: 51, 156 life: 10, 157 score: 1, 158 frame: e1, 159 minSpeed: 20, 160 maxSpeed: 10 161 }; 162 //中敌机配置项 163 const E2 = { 164 type: 2, 165 width: 69, 166 height: 95, 167 life: 50, 168 score: 5, 169 frame: e2, 170 minSpeed: 50, 171 maxSpeed: 20 172 }; 173 //打敌机配置项 174 const E3 = { 175 type: 3, 176 width: 169, 177 height: 258, 178 life: 100, 179 score: 20, 180 frame: e3, 181 minSpeed: 100, 182 maxSpeed: 100 183 }; 184 //奖励类配置项 185 const C1 = { 186 type: 4, 187 width: 75, 188 height: 75, 189 life: 1, 190 score: 1, 191 img: c1, 192 minSpeed: 5, 193 maxSpeed: 10 194 }; 195 //正式代码 196 197 //初始化奖励类 198 class Award { 199 constructor(config) { 200 this.type = config.type; 201 this.width = config.width; 202 this.height = config.height; 203 this.x = Math.floor(Math.random() * (480 - config.width)); 204 this.y = -config.height; 205 this.life = config.life; 206 this.score = config.score; 207 this.img = config.img; 208 this.live = true; 209 this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed; 210 this.lastTime = new Date().getTime(); 211 this.deathIndex = 0; 212 this.destory = false; 213 } 214 move() { 215 const currentTime = new Date().getTime(); 216 if (currentTime - this.lastTime >= this.speed) { 217 if (this.live) { 218 this.y = this.y + 6; 219 this.lastTime = currentTime; 220 } else { 221 this.destory = true; 222 223 } 224 } 225 } 226 paint(context) { 227 context.drawImage(this.img, this.x, this.y, this.width, this.height); 228 } 229 outOfBounds() { 230 if (this.y > 650) { 231 return true; 232 } 233 } 234 hit(o) { 235 let ol = o.x; 236 let or = o.x + o.width; 237 let ot = o.y; 238 let ob = o.y + o.height; 239 let el = this.x; 240 let er = this.x + this.width; 241 let et = this.y; 242 let eb = this.y + this.height; 243 if (ol > er || or < el || ot > eb || ob < et) { 244 return false; 245 } else { 246 return true; 247 } 248 } 249 // collide() { 250 // this.life--; 251 // if (this.life === 0) { 252 // this.live = false; 253 // score += this.score; 254 // } 255 // } 256 } 257 258 // 259 //初始化一个子弹类 260 class Bullet { 261 constructor(config, x, y) { 262 this.img = config.img; 263 this.width = config.width; 264 this.height = config.height; 265 this.x = x; 266 this.y = y; 267 this.destory = false; 268 } 269 //子弹绘制方法 270 paint(context) { 271 context.drawImage(this.img, this.x, this.y); 272 } 273 //移动子弹 this.y-- 274 move() { 275 this.y -= 8; 276 } 277 outOfBounds() { 278 //如果返回的是真的话 那么我们应该销毁掉这个子弹 279 return this.y < -this.height; 280 } 281 collide() { 282 //让这颗子弹变成可销毁状态 283 this.destory = true; 284 } 285 } 286 // 287 288 // 初始化一个敌机类 289 class Enemy { 290 291 292 293 294 295 296 constructor(config) { 297 this.type = config.type; 298 this.width = config.width; 299 this.height = config.height; 300 this.x = Math.floor(Math.random() * (480 - config.width)); 301 this.y = -config.height; 302 this.life = config.life; 303 this.score = config.score; 304 this.frame = config.frame; 305 this.img = this.frame.live[0]; 306 this.live = true; 307 this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed; 308 this.lastTime = new Date().getTime(); 309 this.deathIndex = 0; 310 this.destory = false; 311 } 312 move() { 313 const currentTime = new Date().getTime(); 314 if (currentTime - this.lastTime >= this.speed) { 315 if (this.live) { 316 this.img = this.frame.live[0]; 317 this.y++; 318 this.lastTime = currentTime; 319 } else { 320 this.img = this.frame.death[this.deathIndex++]; 321 if (this.deathIndex === this.frame.death.length) { 322 this.destory = true; 323 } 324 } 325 } 326 } 327 paint(context) { 328 context.drawImage(this.img, this.x, this.y); 329 } 330 outOfBounds() { 331 if (this.y > 650) { 332 return true; 333 } 334 } 335 hit(o) { 336 let ol = o.x; 337 let or = o.x + o.width; 338 let ot = o.y; 339 let ob = o.y + o.height; 340 let el = this.x; 341 let er = this.x + this.width; 342 let et = this.y; 343 let eb = this.y + this.height; 344 if (ol > er || or < el || ot > eb || ob < et) { 345 return false; 346 } else { 347 return true; 348 } 349 } 350 collide() { 351 this.life--; 352 if (this.life === 0) { 353 this.live = false; 354 score += this.score; 355 } 356 } 357 } 358 359 // 360 // 初始化一个英雄类 361 class Hero { 362 constructor(config) { 363 this.width = config.width; 364 this.height = config.height; 365 this.x = (480 - config.width) / 2; 366 this.y = 650 - config.height; 367 this.frame = config.frame; 368 this.frameLiveIndex = 0; 369 this.frameDeathIndex = 0; 370 this.lastTime = new Date().getTime(); 371 this.speed = config.speed; 372 //当前展示的图片 373 this.img = null; 374 this.live = true; 375 //子弹上次射击的时间 376 this.lastShootTime = new Date().getTime(); 377 //子弹射击的间隔 378 this.shootInterval = 50; 379 //子弹夹数组 380 this.bulletList = []; 381 this.destory = false; 382 } 383 judge() { 384 const currentTime = new Date().getTime(); 385 if (currentTime - this.lastTime > this.speed) { 386 if (this.live) { 387 this.img = this.frame.live[this.frameLiveIndex++ % this.frame.live.length]; 388 } else { 389 //0 1 2 3 4 390 this.img = this.frame.death[this.frameDeathIndex++]; 391 //到4的时候英雄死了 392 if (this.frameDeathIndex === this.frame.death.length) { 393 this.destory = true; 394 } 395 } 396 this.lastTime = currentTime; 397 } 398 } 399 paint(context) { 400 context.drawImage(this.img, this.x, this.y, this.width, this.height); 401 } 402 //英雄可以射击子弹 403 shoot() { 404 //获取当前时间 405 const currentTime = new Date().getTime(); 406 //飞机的位置 407 if (currentTime - this.lastShootTime > this.shootInterval) { 408 //在飞机的头部初始化一个子弹对象 409 let bullet = new Bullet(BULLET, this.x + this.width / 2 - BULLET.width / 2, this.y - BULLET.height); 410 //英雄飞机要认领这个子弹 411 this.bulletList.push(bullet); 412 //在网页上绘制一个子弹对象 413 bullet.paint(context); 414 //更新英雄射击时间 415 this.lastShootTime = currentTime; 416 } 417 } 418 collide() { 419 //将活着标识符切换为false 420 //活着 -> 爆炸中 -> 死亡(销毁) 421 this.live = false; 422 } 423 } 424 // 425 // 初始化一个飞机界面加载类 426 class Loading { 427 constructor(config) { 428 this.frame = config.frame; 429 this.frameIndex = 0; 430 this.width = config.width; 431 this.height = config.height; 432 this.x = config.x; 433 this.y = config.y; 434 this.speed = config.speed; 435 this.lastTime = new Date().getTime(); 436 } 437 judge() { 438 const currentTime = new Date().getTime(); 439 if (currentTime - this.lastTime > this.speed) { 440 this.frameIndex++; 441 if (this.frameIndex === 4) { 442 state = RUNNING; 443 } 444 this.lastTime = currentTime; 445 } 446 } 447 paint(context) { 448 context.drawImage(this.frame[this.frameIndex], this.x, this.y); 449 } 450 } 451 452 453 class Main { 454 //一下全为全局变量或方法 (全局的!!) 455 //初始化一个天空实例 456 //主启动方法 457 maingame() { 458 const sky = new Sky(SKY); 459 //初始化一个飞机界面加载实例 460 const loading = new Loading(LOADING); 461 //初始化一个英雄实例 英雄是会变的 462 let hero = new Hero(HERO); 463 //该变量中有所有的敌机实例 464 let enemies = []; 465 //该变量中存放所有的奖励实例 466 467 let awards = []; 468 //敌机产生的速率 469 let ENEMY_CREATE_INTERVAL = 800; 470 let ENEMY_LASTTIME = new Date().getTime(); 471 472 function stateControl() { 473 //为canvas绑定一个点击事件 且他如果是START状态的时候需要修改成STARTING状态 474 canvas.addEventListener("click", () => { 475 if (state === START) { 476 state = STARTING; 477 } 478 }); 479 // 为canvas绑定一个鼠标移动事件 鼠标正好在飞机图片的正中心 480 canvas.addEventListener("mousemove", (e) => { 481 let x = e.offsetX; 482 let y = e.offsetY; 483 hero.x = x - hero.width / 2; 484 hero.y = y - hero.height / 2; 485 }); 486 // 为canvas绑定一个鼠标离开事件 鼠标离开时 RUNNING -> PAUSE 487 canvas.addEventListener("mouseleave", () => { 488 if (state === RUNNING) { 489 state = PAUSE; 490 } 491 }); 492 // 为canvas绑定一个鼠标进入事件 鼠标进入时 PAUSE => RUNNING 493 canvas.addEventListener("mouseenter", () => { 494 if (state === PAUSE) { 495 state = RUNNING; 496 } 497 }); 498 //为canvas绑定一个屏幕移动触摸点事件 触碰点正好在飞机图片的正中心 499 canvas.addEventListener("touchmove", (e) => { 500 // let x = e.pageX; 501 // let y = e.pageY; 502 console.log(e); 503 // let x = e.touches[0].clientX; 504 // let y = e.touches[0].clinetY; 505 let x = e.touches[0].pageX; 506 let y = e.touches[0].pageY; 507 // let x = e.touches[0].screenX; 508 // let y = e.touches[0].screenY; 509 let write1 = (document.body.clientWidth - 480) / 2; 510 let write2 = (document.body.clientHeight - 650) / 2; 511 hero.x = x - write1 - hero.width / 2; 512 hero.y = y - write2 - hero.height / 2; 513 514 // hero.x = x - hero.width / 2; 515 // hero.y = y - hero.height / 2; 516 console.log(x, y); 517 console.log(document.body.clientWidth, document.body.clientHeight); 518 e.preventDefault(); // 阻止屏幕滚动的默认行为 519 520 }) 521 } 522 stateControl(); 523 // 碰撞检测函数 524 //此处的碰撞检测包括 525 //1.子弹与敌机的碰撞 526 //2.英雄与敌机的碰撞 527 //3.英雄与随机奖励的碰撞 528 function checkHit() { 529 // 遍历所有的敌机 530 for (let i = 0; i < awards.length; i++) { 531 //检测英雄是否碰到奖励类 532 if (awards[i].hit(hero)) { 533 //当然了,这个随机奖励的样式也要删了 534 awards.splice(i, 1); 535 //清除所有的敌机 536 // for (let i = 0; i < enemies.length; i++) { 537 // enemies.splice(i, 1); 538 // } 539 enemies.length = 0; 540 541 } 542 } 543 for (let i = 0; i < enemies.length; i++) { 544 //检测英雄是否撞到敌机 545 if (enemies[i].hit(hero)) { 546 //将敌机和英雄的destory属性改为true 547 enemies[i].collide(); 548 hero.collide(); 549 } 550 for (let j = 0; j < hero.bulletList.length; j++) { 551 enemies[i].hit(hero.bulletList[j]); 552 //检测子弹是否撞到敌机 553 if (enemies[i].hit(hero.bulletList[j])) { 554 //将敌机和子弹的destory属性改为true 555 enemies[i].collide(); 556 hero.bulletList[j].collide(); 557 } 558 } 559 } 560 } 561 // 全局函数 隔一段时间就来初始化一架敌机/奖励 562 function createComponent() { 563 const currentTime = new Date().getTime(); 564 if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) { 565 let ran = Math.floor(Math.random() * 100); 566 if (ran < 55) { 567 enemies.push(new Enemy(E1)); 568 } else if (ran < 85 && ran > 55) { 569 enemies.push(new Enemy(E2)); 570 } else if (ran < 95 && ran > 85) { 571 enemies.push(new Enemy(E3)); 572 } else if (ran > 95) { 573 awards.push(new award(C1)); 574 575 } 576 577 ENEMY_LASTTIME = currentTime; 578 } 579 } 580 // 全局函数 来判断所有的子弹/敌人组件 "负责移动" 581 function judgeComponent() { 582 for (let i = 0; i < hero.bulletList.length; i++) { 583 hero.bulletList[i].move(); 584 } 585 for (let i = 0; i < enemies.length; i++) { 586 enemies[i].move(); 587 } 588 for (let i = 0; i < awards.length; i++) { 589 awards[i].move(); 590 } 591 } 592 // 全局函数 来绘制所有的子弹/敌人组件 绘制score&life面板 593 function paintComponent() { 594 for (let i = 0; i < hero.bulletList.length; i++) { 595 hero.bulletList[i].paint(context); 596 } 597 for (let i = 0; i < enemies.length; i++) { 598 enemies[i].paint(context); 599 } 600 for (let i = 0; i < awards.length; i++) { 601 awards[i].paint(context); 602 } 603 context.font = "20px 微软雅黑"; 604 context.fillStyle = "green"; 605 context.textAlign = "left"; 606 context.fillText("score: " + score, 10, 20); 607 context.textAlign = "right"; 608 context.fillText("life: " + life, 480 - 10, 20); 609 //重置样式 610 context.fillStyle = "black"; 611 context.textAlign = "left"; 612 } 613 // 全局函数 来销毁所有的子弹/敌人组件 销毁掉英雄 614 function deleteComponent() { 615 if (hero.destory) { 616 life--; 617 hero.destory = false; 618 if (life === 0) { 619 state = END; 620 } else { 621 hero = new Hero(HERO); 622 } 623 } 624 for (let i = 0; i < hero.bulletList.length; i++) { 625 if (hero.bulletList[i].outOfBounds() || hero.bulletList[i].destory) { 626 hero.bulletList.splice(i, 1); 627 } 628 } 629 for (let i = 0; i < enemies.length; i++) { 630 if (enemies[i].outOfBounds() || enemies[i].destory) { 631 enemies.splice(i, 1); 632 } 633 } 634 } 635 636 //当图片加载完毕时,需要做某些事情 637 bg.addEventListener("load", () => { 638 setInterval(() => { 639 switch (state) { 640 case START: 641 sky.judge(); 642 sky.paint(context); 643 let logo_x = (480 - copyright.naturalWidth) / 2; 644 let logo_y = (650 - copyright.naturalHeight) / 2; 645 context.drawImage(copyright, logo_x, logo_y); 646 break; 647 case STARTING: 648 sky.judge(); 649 sky.paint(context); 650 loading.judge(); 651 loading.paint(context); 652 break; 653 case RUNNING: 654 sky.judge(); 655 sky.paint(context); 656 hero.judge(); 657 hero.paint(context); 658 hero.shoot(); 659 createComponent(); 660 judgeComponent(); 661 deleteComponent(); 662 paintComponent(); 663 checkHit(); 664 break; 665 case PAUSE: 666 let pause_x = (480 - pause.naturalWidth) / 2; 667 let pause_y = (650 - pause.naturalHeight) / 2; 668 context.drawImage(pause, pause_x, pause_y); 669 break; 670 case END: 671 //给我的画笔设置一个字的样式 672 //后面写出来的字都是这个样式的 673 context.font = "bold 24px 微软雅黑"; 674 context.textAlign = "center"; 675 context.textBaseline = "middle"; 676 context.fillText("GAME_OVER", 480 / 2, 650 / 2); 677 break; 678 } 679 }, 10); 680 }); 681 682 683 //背景切换方法 684 // function changebg() { 685 // console.log("changebg方法被触发") 686 // bg.src = "img/background.png" 687 // } 688 } 689 } 690 // 691 692 //初始化一个天空类 693 class Sky { 694 constructor(config) { 695 this.bg = config.bg; 696 this.width = config.width; 697 this.height = config.height; 698 this.x1 = 0; 699 this.y1 = 0; 700 this.x2 = 0; 701 this.y2 = -this.height; 702 this.speed = config.speed; 703 this.lastTime = new Date().getTime(); 704 } 705 //判断方法 706 judge() { 707 let currentTime = new Date().getTime(); 708 if (currentTime - this.lastTime > this.speed) { 709 this.y1++; 710 this.y2++; 711 this.lastTime = currentTime; 712 } 713 if (this.y2 === 0) { 714 this.y1 = 0; 715 this.y2 = -this.height; 716 } 717 } 718 //绘图方法 719 paint(context) { 720 context.drawImage(this.bg, this.x1, this.y1, this.width, this.height); 721 context.drawImage(this.bg, this.x2, this.y2, this.width, this.height); 722 } 723 } 724 let main_1 = new Main() 725 main_1.maingame(); 726 } 727 } 728 729 730 </script> 731 732 <style> 733 #stage { 734 width: 480px; 735 height: 650px; 736 margin: 0 auto; 737 } 738 </style> 739 740
好家伙, 这是未进行分包的vue版本的飞机大战 效果如下: 这里说明一下,大概使用逻辑是提供一个 然后在这中渲染游戏 游戏主界面代码如下: 1 2 3 欢迎来到主页面 4 5
好家伙, 现在,我想要把我的飞机大战塞到我的主页里去,想办法把文件导入 然后,直接死在第一步,图片渲染都成问题 先用vue写一个测试文件 来测试canvas的绘制