尚斌 发表于 2023-4-19 02:09:45

我的第一个项目(十一) :飞机大战分包完成(简单阐述分包思路以及过程)

好家伙,

 
代码已开源
Git:
https://gitee.com/tang-and-han-dynasties/panghu-planebattle-esm.git
NPM:
panghu-planebattle-esm - npm (npmjs.com)
 
现在,比如说,我用Vue写好了个人博客主页的前端
我想在这个主页里面加点东西,让我的博客更缤纷多彩一点
我想在他的主页里面塞个小游戏,他会怎么做
 
1.思考步骤

如下:
第一步:去网上找个小游戏的资源,将这个包下载到本地,
诶,正好发现有个飞机大战 panghu-planebattle-modular 小游戏开发好了
我可以直接下载,或者通过npm安装
<strong>npm install panghu-planebattle-modular</strong> 
第二步:导入到某个.vue文件或html文件
通过import导入
 
第三步:划一个区域给这个包去渲染游戏
 剩下的他就不用管了
大概是这么个过程,然后我们按着这个思路,反向去分我们这个包
 
先来看看原先的完整代码:
完整代码
<strong><template>
   
    <h1>欢迎来到主页面</h1>
      
   
</template>





</strong>Helloworld.vue
 
一看,738行,这,没人想维护的,复制粘贴都嫌累
(再看一眼就要爆炸)
 
我们要实现一个这样的效果(事实上也实现了)
 
而事实上,就是三行代码:
<strong>//从包中导入canvas,main_1
import { canvas, main_1 } from "panghu-planebattle-modular"

//dom操作添加canvas
this.$refs.stage.appendChild(canvas);

//调用main_1的maingame方法
main_1.maingame();</strong> 
让使用者操作的部分由738行代码变成3行代码
 
2.开始分包

将程序主要分成下面几个需要处理的部分
1、静态的:图片,图片地址,配置项
2、六个小类:Enemy、Hero、Loading、Sky、Award、Bullet
3、主启动类:Main
4、全局方法,全局变量
5、入口(对外导出的对象)
 
项目目录结构如下:
 

 
 
1.图片

 
2.配置项
首先是配置项config.js
我们将所有的配置项文件都放在这里,全局变量也放在这里
 如果将来我们要调参数,比如说,改改图片,改改子弹的速度之类,就直接在这个文件里改就行了 
3.其中六个小类,我把他们"独立"分开
比如Bullet(子弹类)
<strong>//初始化一个子弹类
class Bullet {
constructor(config, x, y) {
    this.img = config.img;
    this.width = config.width;
    this.height = config.height;
    this.x = x;
    this.y = y;
    this.destory = false;
}
//子弹绘制方法
paint(context) {
    context.drawImage(this.img, this.x, this.y);
}
//移动子弹 this.y--
move() {
    this.y -= 8;
}
outOfBounds() {
    //如果返回的是真的话 那么我们应该销毁掉这个子弹
    return this.y < -this.height;
}
collide() {
    //让这颗子弹变成可销毁状态
    this.destory = true;
}
}

export default Bullet </strong> 
这里需要提一嘴的是,类的导出必须带 default,否则会报错
<strong>export default Bullet </strong> 
 
4.主启动类main
我们将所有曾经的全局方法,还有定时器都封装到这个类中
最后新建一个实例,并导出
<strong>1 import Enemy from "./enemy"
2 import Hero from "./hero"
3 import Loading from "./loading"
4 import Sky from "./sky"
5 import Award from "./award"
6
7 import { START, STARTING, RUNNING, PAUSE, END } from "./config"
8 import { SKY, LOADING, HERO, E1, E2, E3, C1 } from "./config"
9 import { bg, copyright, pause } from "./config"
10 import { canvas, context } from "./config"
11
12 class Main {
13   //以下全为全局变量或方法 (全局的!!)
14   //初始化一个天空实例
15   //主启动方法
16   maingame() {
17         const sky = new Sky(SKY);
18         //初始化一个飞机界面加载实例
19         const loading = new Loading(LOADING);
20         //初始化一个英雄实例 英雄是会变的
21         let hero = new Hero(HERO);
22         //该变量中有所有的敌机实例
23         let enemies = [];
24         //该变量中存放所有的奖励实例
25         let awards = [];
26         //敌机产生的速率
27         let ENEMY_CREATE_INTERVAL = 800;
28         let ENEMY_LASTTIME = new Date().getTime();
29
30         function stateControl() {
31             //为canvas绑定一个点击事件 且他如果是START状态的时候需要修改成STARTING状态
32             canvas.addEventListener("click", () => {
33               if (state === START) {
34                     state = STARTING;
35               }
36             });
37             // 为canvas绑定一个鼠标移动事件 鼠标正好在飞机图片的正中心
38             canvas.addEventListener("mousemove", (e) => {
39               let x = e.offsetX;
40               let y = e.offsetY;
41               hero.x = x - hero.width / 2;
42               hero.y = y - hero.height / 2;
43             });
44             // 为canvas绑定一个鼠标离开事件 鼠标离开时 RUNNING -> PAUSE
45             canvas.addEventListener("mouseleave", () => {
46               if (state === RUNNING) {
47                     state = PAUSE;
48               }
49             });
50             // 为canvas绑定一个鼠标进入事件 鼠标进入时 PAUSE => RUNNING
51             canvas.addEventListener("mouseenter", () => {
52               if (state === PAUSE) {
53                     state = RUNNING;
54               }
55             });
56             //为canvas绑定一个屏幕移动触摸点事件 触碰点正好在飞机图片的正中心
57             canvas.addEventListener("touchmove", (e) => {
58               // let x = e.pageX;
59               // let y = e.pageY;
60               console.log(e);
61               // let x = e.touches.clientX;
62               // let y = e.touches.clinetY;
63               let x = e.touches.pageX;
64               let y = e.touches.pageY;
65               // let x = e.touches.screenX;
66               // let y = e.touches.screenY;
67               let write1 = (document.body.clientWidth - 480) / 2;
68               let write2 = (document.body.clientHeight - 650) / 2;
69               hero.x = x - write1 - hero.width / 2;
70               hero.y = y - write2 - hero.height / 2;
71
72               // hero.x = x - hero.width / 2;
73               // hero.y = y - hero.height / 2;
74               console.log(x, y);
75               console.log(document.body.clientWidth, document.body.clientHeight);
76               e.preventDefault(); // 阻止屏幕滚动的默认行为
77
78             })
79         }
80         stateControl();
81         // 碰撞检测函数
82         //此处的碰撞检测包括
83         //1.子弹与敌机的碰撞
84         //2.英雄与敌机的碰撞
85         //3.英雄与随机奖励的碰撞
86         function checkHit() {
87             // 遍历所有的敌机
88             for (let i = 0; i < awards.length; i++) {
89               //检测英雄是否碰到奖励类
90               if (awards.hit(hero)) {
91                     //当然了,这个随机奖励的样式也要删了
92                     awards.splice(i, 1);
93                     //清除所有的敌机
94                     // for (let i = 0; i < enemies.length; i++) {
95                     //   enemies.splice(i, 1);
96                     // }
97                     enemies.length = 0;
98
99               }
100             }
101             for (let i = 0; i < enemies.length; i++) {
102               //检测英雄是否撞到敌机
103               if (enemies.hit(hero)) {
104                     //将敌机和英雄的destory属性改为true
105                     enemies.collide();
106                     hero.collide();
107               }
108               for (let j = 0; j < hero.bulletList.length; j++) {
109                     enemies.hit(hero.bulletList);
110                     //检测子弹是否撞到敌机
111                     if (enemies.hit(hero.bulletList)) {
112                         //将敌机和子弹的destory属性改为true
113                         enemies.collide();
114                         hero.bulletList.collide();
115                     }
116               }
117             }
118         }
119         // 全局函数 隔一段时间就来初始化一架敌机/奖励
120         function createComponent() {
121             const currentTime = new Date().getTime();
122             if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {
123               let ran = Math.floor(Math.random() * 100);
124               if (ran < 55) {
125                     enemies.push(new Enemy(E1));
126               } else if (ran < 85 && ran > 55) {
127                     enemies.push(new Enemy(E2));
128               } else if (ran < 95 && ran > 85) {
129                     enemies.push(new Enemy(E3));
130               } else if (ran > 95) {
131                     awards.push(new Award(C1));
132
133               }
134
135               ENEMY_LASTTIME = currentTime;
136             }
137         }
138         // 全局函数 来判断所有的子弹/敌人组件 "负责移动"
139         function judgeComponent() {
140             for (let i = 0; i < hero.bulletList.length; i++) {
141               hero.bulletList.move();
142             }
143             for (let i = 0; i < enemies.length; i++) {
144               enemies.move();
145             }
146             for (let i = 0; i < awards.length; i++) {
147               awards.move();
148             }
149         }
150         // 全局函数 来绘制所有的子弹/敌人组件 绘制score&life面板
151         function paintComponent() {
152             for (let i = 0; i < hero.bulletList.length; i++) {
153               hero.bulletList.paint(context);
154             }
155             for (let i = 0; i < enemies.length; i++) {
156               enemies.paint(context);
157             }
158             for (let i = 0; i < awards.length; i++) {
159               awards.paint(context);
160             }
161             context.font = "20px 微软雅黑";
162             context.fillStyle = "green";
163             context.textAlign = "left";
164             context.fillText("score: " + score, 10, 20);
165             context.textAlign = "right";
166             context.fillText("life: " + life, 480 - 10, 20);
167             //重置样式
168             context.fillStyle = "black";
169             context.textAlign = "left";
170         }
171         // 全局函数 来销毁所有的子弹/敌人组件 销毁掉英雄
172         function deleteComponent() {
173             if (hero.destory) {
174               life--;
175               hero.destory = false;
176               if (life === 0) {
177                     state = END;
178               } else {
179                     hero = new Hero(HERO);
180               }
181             }
182             for (let i = 0; i < hero.bulletList.length; i++) {
183               if (hero.bulletList.outOfBounds() || hero.bulletList.destory) {
184                     hero.bulletList.splice(i, 1);
185               }
186             }
187             for (let i = 0; i < enemies.length; i++) {
188               if (enemies.outOfBounds() || enemies.destory) {
189                     enemies.splice(i, 1);
190               }
191             }
192         }
193
194         //当图片加载完毕时,需要做某些事情
195         bg.addEventListener("load", () => {
196             setInterval(() => {
197               switch (state) {
198                     case START:
199                         sky.judge();
200                         sky.paint(context);
201                         let logo_x = (480 - copyright.naturalWidth) / 2;
202                         let logo_y = (650 - copyright.naturalHeight) / 2;
203                         context.drawImage(copyright, logo_x, logo_y);
204                         break;
205                     case STARTING:
206                         sky.judge();
207                         sky.paint(context);
208                         loading.judge();
209                         loading.paint(context);
210                         break;
211                     case RUNNING:
212                         sky.judge();
213                         sky.paint(context);
214                         hero.judge();
215                         hero.paint(context);
216                         hero.shoot(context);
217                         createComponent();
218                         judgeComponent();
219                         deleteComponent();
220                         paintComponent();
221                         checkHit();
222                         break;
223                     case PAUSE:
224                         let pause_x = (480 - pause.naturalWidth) / 2;
225                         let pause_y = (650 - pause.naturalHeight) / 2;
226                         context.drawImage(pause, pause_x, pause_y);
227                         break;
228                     case END:
229                         //给我的画笔设置一个字的样式
230                         //后面写出来的字都是这个样式的
231                         context.font = "bold 24px 微软雅黑";
232                         context.textAlign = "center";
233                         context.textBaseline = "middle";
234                         context.fillText("GAME_OVER", 480 / 2, 650 / 2);
235                         break;
236               }
237             }, 10);
238         });
239
240
241         //背景切换方法
242         // function changebg() {
243         //   console.log("changebg方法被触发")
244         //   bg.src = "img/background.png"
245         // }
246   }
247 }
248 export let main_1 = new Main()
249 // export default Main </strong>main.js 
 
5.包的入口
首先看一眼package.json

 看main,(这个可以自己调的)
由上图可知,这个包的入口就是index.js了
 
<strong>//index.js
export { canvas } from "./config"
export { main_1 } from "./main"</strong>config.js中的canvas
<strong>export let canvas = document.createElement('canvas');
canvas.width = 480;
canvas.height = 650;
canvas.ref = canvas;
canvas.style = "border: 1px solid red;"</strong>main.js中的main
<strong>export let main_1 = new Main()</strong> 
在这里,用上我们前几天学的语法(嘿嘿)

 
分包完成
 
 

来源:https://www.cnblogs.com/FatTiger4399/p/17322997.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 我的第一个项目(十一) :飞机大战分包完成(简单阐述分包思路以及过程)