我的第一个项目(九) :飞机大战Vue版本塞到主页

发布时间 2023-04-05 20:25:13作者: 养肥胖虎

好家伙,

 

这是未进行分包的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