每次写类的时候都会觉得发明面向对象的人真是个天才。

今天晚上又稍微写了点,增加了碰撞伤害,现在玩家碰到怪物会回扣怪物.at血量并进入一秒的无敌状态,并且自身颜色变为白色,也是第一次体会到异步的方便之处吧。

更新后代码如下:

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 
  4 <head>
  5   <meta charset="UTF-8">
  6   <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7   <title>Document</title>
  8 </head>
  9 <style>
 10   body {
 11     margin: 0;
 12     overflow: hidden;
 13   }
 14 </style>
 15 
 16 <body>
 17   <canvas id="Canvas" style="border:1px solid #000000;"></canvas>
 18 </body>
 19 <script>
 20   //初始化画布
 21   const canvas = document.getElementById('Canvas');
 22   canvas.width = window.innerWidth;
 23   canvas.height = window.innerHeight;
 24   canvas.style.backgroundColor = '#000000';
 25   const ctx = canvas.getContext('2d');
 26   //定义游戏对象数组
 27   const grounds = [];
 28   const monsters = [];
 29   //定义玩家类
 30   class Player {
 31     //基础属性
 32     hp = 10;
 33     x = Math.round(canvas.width / 2);
 34     y = Math.round(canvas.height / 2);
 35     width = 30;
 36     height = 30;
 37     color = '#FF0000';
 38     invincibleColor = 'white';
 39     speedX = 0;
 40     speedY = 0;
 41     a = 0.05;
 42     g = 0.1;
 43     maxSpeedX = 3;
 44     maxSpeedY = 3;
 45 
 46     lastAttackedTime = Date.now();
 47 
 48     status = {
 49       up: false,
 50       down: false,
 51       left: false,
 52       right: false,
 53 
 54       landing: false,
 55       toward: 'right',
 56       attacking: false,
 57       invincible: false,
 58     }
 59 
 60     damage = {
 61       at: 1,
 62       width: 80,
 63       height: 40,
 64     }
 65 
 66     //方法
 67 
 68     //跳跃方法
 69     jump() {
 70       this.speedY = -5;
 71       this.status.landing = false;
 72     }
 73     //碰撞检测方法
 74     crush(ground) {
 75       if (ground.y - (this.y + this.height) <= 0 && ground.y - (this.y + this.height) >= -this.speedY)
 76         return true;
 77       else
 78         return false;
 79     }
 80     //玩家运动
 81     move() {
 82       this.x += this.speedX;
 83       this.y += this.speedY;
 84     }
 85     //碰撞监测
 86     checkCrash() {
 87       grounds.forEach(Ground => {
 88         if (this.crush(Ground)) {
 89           this.y = Ground.y - this.height;
 90           this.status.landing = true;
 91         }
 92       });
 93     }
 94     //重力作用
 95     applyGravity() {
 96       if (this.status.landing == false) {
 97         this.speedY += this.g;
 98         if (this.speedY > this.maxSpeedY)
 99           this.speedY = this.maxSpeedY;
100       } else {
101         this.speedY = 0;
102       }
103     }
104     //水平无操作时水平减速
105     velocityDecay() {
106       if ((this.status.left == false && this.status.right == false) || (this.status.left == true && this.status.right == true)) {
107         if (this.speedX > 0) {
108           this.speedX -= this.a;
109           if (this.speedX < 0) this.speedX = 0;
110         } else if (this.speedX < 0) {
111           this.speedX += this.a;
112           if (this.speedX > 0) this.speedX = 0;
113         }
114       }
115     }
116     //水平加速度操作速度
117     controlSpeed() {
118       if (this.status.left) {
119         this.speedX -= this.a;
120         if (this.speedX < -this.maxSpeedX) this.speedX = -this.maxSpeedX;
121       }
122       if (this.status.right) {
123         this.speedX += this.a;
124         if (this.speedX > this.maxSpeedX) this.speedX = this.maxSpeedX;
125       }
126     }
127     //绘制玩家
128     draw() {
129       if (this.status.invincible)
130         ctx.fillStyle = this.invincibleColor;
131       else
132         ctx.fillStyle = this.color;
133       ctx.fillRect(this.x, this.y, this.width, this.height);
134     }
135     //展示血量数字
136     showHp() {
137       ctx.fillStyle = 'white';
138       ctx.font = '12px Arial';
139       ctx.fillText(this.hp, this.x + this.width / 2 - 6, this.y - 2);
140     }
141     //攻击方法
142     attack(m) {
143       m.hp -= this.damage.at;
144       console.log("攻击命中!怪物剩余血量:" + m.hp);
145       if (m.hp <= 0) {
146         const index = monsters.indexOf(m);
147         if (index > -1) {
148           monsters.splice(index, 1);
149           console.log("怪物已被击败!");
150         }
151       }
152     }
153     //绘制攻击范围与攻击判定
154     drawAttackRange() {
155       //绘制范围
156       if (this.status.attacking) {
157         ctx.fillStyle = '#FFFF00';
158         if (this.status.toward == 'right') {
159           ctx.fillRect(this.x + this.width, this.y + (this.height - this.damage.height) / 2, this.damage.width, this.damage.height);
160         } else if (this.status.toward == 'left') {
161           ctx.fillRect(this.x - this.damage.width, this.y + (this.height - this.damage.height) / 2, this.damage.width, this.damage.height);
162         }
163         //攻击判定
164         monsters.forEach(m => {
165           if (this.status.toward == 'right' &&
166             m.x < this.x + this.width + this.damage.width &&
167             m.x + m.width > this.x + this.width &&
168             m.y < this.y + (this.height + this.damage.height) / 2 &&
169             m.y + m.height > this.y + (this.height - this.damage.height) / 2
170           ) {
171             this.attack(m);
172           }
173           else if (
174             this.status.toward == 'left' &&
175             m.x + m.width > this.x - this.damage.width &&
176             m.x < this.x &&
177             m.y < this.y + (this.height + this.damage.height) / 2 &&
178             m.y + m.height > this.y + (this.height - this.damage.height) / 2
179           ) {
180             this.attack(m);
181           }
182         })
183         this.status.attacking = false;
184       }
185     }
186     //受击方法
187     attacked() {
188       monsters.forEach(m => {
189         if (
190           m.x < this.x + this.width &&
191           m.x + m.width > this.x &&
192           m.y < this.y + this.height &&
193           m.y + m.height > this.y
194         ) {
195           this.reduceHp(m.at);
196         }
197       });
198     }
199     //常规血量减少
200     reduceHp(at) {
201       const currentTime = Date.now();
202       if (currentTime - this.lastAttackedTime > 1000) {
203         this.hp -= at;
204         this.status.invincible = true;
205         this.lastAttackedTime = currentTime;
206         //异步延迟
207         setTimeout(() => {
208           this.status.invincible = false;
209         }, 1000);
210       }
211     }
212   }
213   //定义地面类
214   class Ground {
215     x = 0;
216     y = 0;
217     width = 0;
218     height = 0;
219     color = '#654321';
220 
221     constructor(x, y, width, height) {
222       this.x = x;
223       this.y = y;
224       this.width = width;
225       this.height = height;
226     }
227   }
228   //定义怪物类
229   class Monster {
230     hp = 5;
231     at = 1;
232     x = 0;
233     y = 0;
234     width = 30;
235     height = 30;
236     color = '#00FF00';
237 
238     constructor(x, y, width, height) {
239       this.x = x;
240       this.y = y;
241       this.width = width;
242       this.height = height;
243     }
244     //绘制怪物
245     draw() {
246       ctx.fillStyle = this.color;
247       ctx.fillRect(this.x, this.y, this.width, this.height);
248     }
249     //展示血量数字
250     showHp() {
251       ctx.fillStyle = 'white';
252       ctx.font = '12px Arial';
253       ctx.fillText(this.hp, this.x + this.width / 2 - 6, this.y - 2);
254     }
255   }
256 
257   //创建初始测试 玩家对象 地面对象 怪物对象
258   const p = new Player();
259   const ground1 = new Ground(0, Math.round(canvas.height - 100), Math.round(canvas.width), 100);
260   grounds.push(ground1);
261   const monster1 = new Monster(200, ground1.y - 30, 30, 30);
262   monsters.push(monster1);
263 
264   //键盘事件监听
265 
266   //1.按下按键
267   document.addEventListener('keydown', function (event) {
268     switch (event.key) {
269       case 'ArrowUp':
270         if (p.status.landing == true)
271           p.jump();
272         break;
273       case 'ArrowDown':
274         p.status.down = true;
275         break;
276       case 'ArrowLeft':
277         p.status.left = true;
278         p.status.toward = 'left';
279         break;
280       case 'ArrowRight':
281         p.status.right = true;
282         p.status.toward = 'right';
283         break;
284       case 'z':
285         p.status.attacking = true;
286         break;
287       case 'Z':
288         p.status.attacking = true;
289         break;
290     }
291   });
292   //2.松开按键
293   document.addEventListener('keyup', function (event) {
294     switch (event.key) {
295       case 'ArrowUp':
296         break;
297       case 'ArrowDown':
298         p.status.down = false;
299         break;
300       case 'ArrowLeft':
301         p.status.left = false;
302         break;
303       case 'ArrowRight':
304         p.status.right = false;
305         break;
306     }
307   });
308   //3.c键查看玩家状态
309   document.addEventListener('keydown', function (event) {
310     if (event.key === 'c' || event.key === 'C') {
311       console.log("玩家状态:", p);
312     }
313 
314   });
315   //动画循环
316   function animate() {
317     ctx.clearRect(0, 0, canvas.width, canvas.height);
318 
319     //绘制陆地
320     grounds.forEach(Ground => {
321       ctx.fillStyle = Ground.color;
322       ctx.fillRect(Ground.x, Ground.y, Ground.width, Ground.height);
323     });
324 
325     //玩家
326     {
327       //玩家运动
328       p.move();
329       //碰撞监测
330       p.checkCrash();
331       //重力作用
332       p.applyGravity();
333       //水平无操作时水平减速
334       p.velocityDecay();
335       //水平加速度操作速度
336       p.controlSpeed();
337       //受到伤害方法
338       p.attacked()
339       //绘制玩家
340       p.draw();
341       //展示血量
342       p.showHp();
343       //绘制攻击范围
344       p.drawAttackRange();
345     }
346 
347     //怪物
348     {
349       monsters.forEach(m => {
350         //绘制怪物
351         m.draw();
352         //展示血量
353         m.showHp();
354       });
355 
356     }
357     requestAnimationFrame(animate);
358   }
359   //启动!!
360   animate();
361 </script>
362 
363 </html>

 

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]