20251106
今天晚上完成了血量显示和攻击判定,另外也优化了代码,把animate()里的各种逻辑封装成方法放在Player类里了,等有时间了再写写monster的ai移动逻辑,优化后的代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <style> body { margin: 0; overflow: hidden; } </style> <body> <canvas id="Canvas" style="border:1px solid #000000;"></canvas> </body> <script> //初始化画布 const canvas = document.getElementById('Canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.style.backgroundColor = '#000000'; const ctx = canvas.getContext('2d'); //定义游戏对象数组 const grounds = []; const monsters = []; //定义玩家类 class Player { //基础属性 hp = 10; x = Math.round(canvas.width / 2); y = Math.round(canvas.height / 2); width = 30; height = 30; color = '#FF0000'; invincibleColor = 'white'; speedX = 0; speedY = 0; a = 0.05; g = 0.1; maxSpeedX = 3; maxSpeedY = 3; lastAttackedTime = Date.now(); status = { up: false, down: false, left: false, right: false, landing: false, toward: 'right', attacking: false, invincible: false, } damage = { at: 1, width: 80, height: 40, } //方法 //跳跃方法 jump() { this.speedY = -5; this.status.landing = false; } //碰撞检测方法 crush(ground) { if (ground.y - (this.y + this.height) <= 0 && ground.y - (this.y + this.height) >= -this.speedY) return true; else return false; } //玩家运动 move() { this.x += this.speedX; this.y += this.speedY; } //碰撞监测 checkCrash() { grounds.forEach(Ground => { if (this.crush(Ground)) { this.y = Ground.y - this.height; this.status.landing = true; } }); } //重力作用 applyGravity() { if (this.status.landing == false) { this.speedY += this.g; if (this.speedY > this.maxSpeedY) this.speedY = this.maxSpeedY; } else { this.speedY = 0; } } //水平无操作时水平减速 velocityDecay() { if ((this.status.left == false && this.status.right == false) || (this.status.left == true && this.status.right == true)) { if (this.speedX > 0) { this.speedX -= this.a; if (this.speedX < 0) this.speedX = 0; } else if (this.speedX < 0) { this.speedX += this.a; if (this.speedX > 0) this.speedX = 0; } } } //水平加速度操作速度 controlSpeed() { if (this.status.left) { this.speedX -= this.a; if (this.speedX < -this.maxSpeedX) this.speedX = -this.maxSpeedX; } if (this.status.right) { this.speedX += this.a; if (this.speedX > this.maxSpeedX) this.speedX = this.maxSpeedX; } } //绘制玩家 draw() { if (this.status.invincible) ctx.fillStyle = this.invincibleColor; else ctx.fillStyle = this.color; ctx.fillRect(this.x, this.y, this.width, this.height); } //展示血量数字 showHp() { ctx.fillStyle = 'white'; ctx.font = '12px Arial'; ctx.fillText(this.hp, this.x + this.width / 2 - 6, this.y - 2); } //攻击方法 attack(m) { m.hp -= this.damage.at; console.log("攻击命中!怪物剩余血量:" + m.hp); if (m.hp <= 0) { const index = monsters.indexOf(m); if (index > -1) { monsters.splice(index, 1); console.log("怪物已被击败!"); } } } //绘制攻击范围与攻击判定 drawAttackRange() { //绘制范围 if (this.status.attacking) { ctx.fillStyle = '#FFFF00'; if (this.status.toward == 'right') { ctx.fillRect(this.x + this.width, this.y + (this.height - this.damage.height) / 2, this.damage.width, this.damage.height); } else if (this.status.toward == 'left') { ctx.fillRect(this.x - this.damage.width, this.y + (this.height - this.damage.height) / 2, this.damage.width, this.damage.height); } //攻击判定 monsters.forEach(m => { if (this.status.toward == 'right' && m.x < this.x + this.width + this.damage.width && m.x + m.width > this.x + this.width && m.y < this.y + (this.height + this.damage.height) / 2 && m.y + m.height > this.y + (this.height - this.damage.height) / 2 ) { this.attack(m); } else if ( this.status.toward == 'left' && m.x + m.width > this.x - this.damage.width && m.x < this.x && m.y < this.y + (this.height + this.damage.height) / 2 && m.y + m.height > this.y + (this.height - this.damage.height) / 2 ) { this.attack(m); } }) this.status.attacking = false; } } } //定义地面类 class Ground { x = 0; y = 0; width = 0; height = 0; color = '#654321'; constructor(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; } } //定义怪物类 class Monster { hp = 5; at = 1; x = 0; y = 0; width = 30; height = 30; color = '#00FF00'; constructor(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; } //绘制怪物 draw() { ctx.fillStyle = this.color; ctx.fillRect(this.x, this.y, this.width, this.height); } //展示血量数字 showHp() { ctx.fillStyle = 'white'; ctx.font = '12px Arial'; ctx.fillText(this.hp, this.x + this.width / 2 - 6, this.y - 2); } } //创建初始 玩家对象 地面对象 怪物对象 const p = new Player(); const ground1 = new Ground(0, Math.round(canvas.height - 100), Math.round(canvas.width), 100); grounds.push(ground1); const monster1 = new Monster(200, ground1.y - 30, 30, 30); monsters.push(monster1); //键盘事件监听 //1.按下按键 document.addEventListener('keydown', function (event) { switch (event.key) { case 'ArrowUp': if (p.status.landing == true) p.jump(); break; case 'ArrowDown': p.status.down = true; break; case 'ArrowLeft': p.status.left = true; p.status.toward = 'left'; break; case 'ArrowRight': p.status.right = true; p.status.toward = 'right'; break; case 'z' || 'Z': p.status.attacking = true; break; } }); //2.松开按键 document.addEventListener('keyup', function (event) { switch (event.key) { case 'ArrowUp': break; case 'ArrowDown': p.status.down = false; break; case 'ArrowLeft': p.status.left = false; break; case 'ArrowRight': p.status.right = false; break; } }); //3.c键查看玩家状态 document.addEventListener('keydown', function (event) { if (event.key === 'c' || event.key === 'C') { console.log("玩家状态:", p); } }); //动画循环 function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); //绘制陆地 grounds.forEach(Ground => { ctx.fillStyle = Ground.color; ctx.fillRect(Ground.x, Ground.y, Ground.width, Ground.height); }); //玩家 { //玩家运动 p.move(); //碰撞监测 p.checkCrash(); //重力作用 p.applyGravity(); //水平无操作时水平减速 p.velocityDecay(); //水平加速度操作速度 p.controlSpeed(); //绘制玩家 p.draw(); //展示血量 p.showHp(); //绘制攻击范围 p.drawAttackRange(); } //怪物 { monsters.forEach(m => { //绘制怪物 m.draw(); //展示血量 m.showHp(); }); } requestAnimationFrame(animate); } //启动!! animate(); </script> </html>

浙公网安备 33010602011771号