20251107

news/2025/11/7 21:25:31/文章来源:https://www.cnblogs.com/Leesz/p/19200923

每次写类的时候都会觉得发明面向对象的人真是个天才。 今天晚上又稍微写了点,增加了碰撞伤害,现在玩家碰到怪物会回扣怪物.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>

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/959191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始实现简易版Netty(十) MyNetty 通用编解码器解决TCP黏包/拆包问题

从零开始实现简易版Netty(十) MyNetty 通用编解码器解决TCP黏包/拆包问题从零开始实现简易版Netty(十) MyNetty 通用编解码器解决TCP黏包/拆包问题 1. TCP黏包拆包问题介绍 在上一篇博客中,lab9版本的MyNetty已经实现…

[Python刷题记录]-除自身以外数组的乘积-普通数组-中等

[Python刷题记录]-除自身以外数组的乘积-普通数组-中等链接:238. 除自身以外数组的乘积 - 力扣(LeetCode) 题目限制不能使用除法,所以开了两个数组listl和listr,listl用来存储i之前的所有数字之乘积,listr用来存…

Transformer Decoder 中序列掩码(Sequence Mask / Look-ahead Mask) - 详解

Transformer Decoder 中序列掩码(Sequence Mask / Look-ahead Mask) - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fam…

P9785 [ROIR 2020] 对常规的斗争 (Day1) 题解

题目传送门 思路 我们不难发现,当区间中没有重复的点很好求,但如果中间部分产生重复的点,他们所产生的贡献会减少。 正着推不好推,那就反着来。 我们可以考虑计算当区间长度确定时,每个区间内每个元素是否出现过。…

实用指南:超越CNN和Transformer!Mamba结合多模态统领图像任务!

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Docker镜像建立【MSSQL2022】

sudo docker stop cont_mssql2022 sudo docker rm cont_mssql2022sudo mkdir -p ~/zapas/dbdata/mssql sudo chmod 777 ~/zapas/dbdata/mssqlcd ~/zapas/dbdata/mssql mkdir testdb sudo chmod 777 ~/zapas/dbdata/mss…

灰度发布

目录背景和价值参考资料 背景和价值 在 Spring Cloud 架构中,灰度发布(又称金丝雀发布)的核心目标是将新版本服务仅对部分用户 / 流量开放,验证稳定性后再全量上线,以降低发布风险。实现需结合服务注册发现、负载…

【刷题笔记】AT 经典 90 题

T2 爆搜 注意,string 只能与 string 运算,无法和 char 运算;访问 string 某一位时则是 char T5 数位 DP。 转化题意:若 \(x\) 是 \(B\) 的倍数,则 \(x\mod B = 0\)。 先设计 DP 状态,设 \(f_{i,j}\) 表示看到第 …

CF1758E Tick, Tock

首先考虑初始状态合法的充要条件,每两行对应位置差相等。 发现每个连通块有一些代表元,用带权并查集做即可。

深入解析:SciPy傅里叶变换与信号处理教程:数学原理与Python实现

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

CentOS Stream 9编译安装Nginx 1.28 - Leone

安装Nginx基本依赖:yum -y install gcc pcre2-devel zlib-devel openssl-devel cd /mnt/wgetwget https://nginx.org/download/nginx-1.28.0.tar.gztar -xvf nginx-1.28.0.tar.gz && cd nginx-1.28.0./config…

SQL核心语言详解:DQL、DML、DDL、DCL从入门到实践! - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Ubuntu安装JDK与Maven和IntelliJ IDEA - 详解

Ubuntu安装JDK与Maven和IntelliJ IDEA - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &qu…

JavaWeb03-Vue

JavaWeb03-VueVue 什么是VueVue是一款用于构建用户界面的渐进式的JavaScript框架。(http://cn.vuejs.org/)构建用户界面渐进式框架:就是一套完整的项目解决方案,用于快速构建项目优点:大大提升前端项目的开发效率…

【完结】Weblogic中间件应用服务器

【完结】Weblogic中间件应用服务器 学习地址:……/s/1FP9WOZ9FXcNRJTkaEApaVA 提取码:o2rh 在当今瞬息万变的数字商业世界中,企业应用的稳定性、安全性与高可用性已成为支撑业务运转的生命线。在这条无形的战线上,…

调整包含特定文本的单元格所在的行高

Sub 调整包含特定文本的单元格所在的行高()For i = 1 To Range("a" & 1048576).End(xlUp).RowIf Range("a" & i).Value Like "健康管理意见建议*" ThenRange("a" &am…

javabean和pojo的区别

JavaBean 是一种JAVA语言写成的可重用组件。 它的方法命名,构造及行为必须符合特定的约定:这个类必须有一个公共的缺省构造函数。 这个类的属性使用getter和setter来访问,其他方法遵从标准命名规范。 这个类应是可序…

一次十分折腾的系统迁移:BCD损坏(0xc000000f), 0xc0000255, 0xc000000e以及解决办法

起因:原装盘内存不够(matebook13原装256G,我用了五六年了,80G传奇c盘使我的所有朋友难绷),想着自己换一块512G的 两块都是WD的盘,兼容没问题 盲目信任自己的技术力和Qwen3的能力,于是开始快乐的装盘过程~ 期间…

2025微信小店代运营/电商优质服务商推荐榜:健安道领衔,三大实力机构助力商家全域增长

2025 年微信电商生态持续扩容,微信小店凭借公私域联动优势实现 GMV 同比激增 192%,成为商家线上布局的核心阵地。本次推荐聚焦真实靠谱的微信小店服务商,精选 3 家实力企业,为品牌提供清晰的合作参考。 健安道企业…