20251107

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

今天晚上又稍微写了点,增加了碰撞伤害,现在玩家碰到怪物会回扣怪物.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>

 

作者:世z原文地址:https://www.cnblogs.com/Leesz/p/19200923

%s 个评论

要回复文章请先登录注册