如何在面向对象的JavaScript游戏中修改Enemy类实现敌人追踪玩家功能
游戏开发问题:实现敌人追踪玩家的俯视视角游戏
我最近在用HTML、CSS和JavaScript开发一款游戏,一开始参考了FreeCodeCamp的一个横版卷轴游戏教程——那个教程做的是没有角色重力的横版游戏,但我想做个俯视视角、敌人能主动追踪玩家的游戏。目前我已经搞定了玩家的WASD移动控制,但卡在了敌人的追踪逻辑上,不知道怎么修改Enemy类的行为让它们跟着玩家走。我是面向对象编程(OOP)和JavaScript游戏开发的新手,下面是我的相关代码:
现有代码
Enemy类与子类
class Enemy { constructor(game){ this.game = game; this.x = this.game.width; this.speed = Math.random() * -1.5 - 0.5; this.markedForDeletion = false; this.lives = 5; this.score = this.lives; // 修正:初始化基础尺寸属性,确保子类继承后能正常使用 this.width = 0; this.height = 0; this.y = 0; } update(){ this.x += this.speed; // 修正拼写错误:wodth → width if (this.x + this.width > 0) this.markedForDeletion = true; } draw(context){ context.fillStyle = 'red'; context.fillRect(this.x, this.y, this.width, this.height); context.fillStyle = 'black'; context.font = '20px Helvetica' context.fillText(this.lives, this.x, this.y); } } class Angler1 extends Enemy { constructor(game){ super(game); this.width = 228 * 0.2; this.height = 169 * 0.2; this.y = Math.random() * (this.game.height * 0.9 - this.height); } }
Player类
class Player { constructor(game){ this.game = game; this.width = 80; this.height = 120; this.x = 20; this.y = 100; this.speedX = 3; this.speedY = 0; this.maxSpeed = 3; this.projectiles = []; } update(){ if(this.game.keys.includes('w')) this.speedY = -this.maxSpeed; else if (this.game.keys.includes('s')) this.speedY = this.maxSpeed; else if (this.game.keys.includes('a')) this.speedX = -this.maxSpeed; else if (this.game.keys.includes('d')) this.speedX = this.maxSpeed; else { this.speedY = 0; this.speedX = 0; } this.y += this.speedY; this.x += this.speedX; //handle projectiles this.projectiles.forEach(projectile => { projectile.update(); }); this.projectiles = this.projectiles.filter(projectile => !projectile.markedForDeletion); } draw(context){ context.fillStyle = 'black'; context.fillRect(this.x, this.y, this.width, this.height); this.projectiles.forEach(projectile => { projectile.draw(context); }); } shootTop(){ if (this.game.ammo > 0){ this.projectiles.push(new Projectile(this.game, this.x + 80, this.y + 30)); this.game.ammo--; } } }
Game类与游戏循环
class Game { constructor(width, height){ this.width = width; this.height = height; this.player = new Player(this); this.input = new InputHandler(this); this.ui = new UI(this); this.keys = []; this.enemies = []; this.enemyTimer = 0; this.enemyInterval = 1000; this.ammo = 20; this.maxAmmo = 50; this.ammoTimer = 0; this.ammoInterval = 500; this.gameOver = false; this.score = 0; this.winningScore = 10; this.gameTime = 0; this.timeLimit = 20000; } update(deltaTime){ if(!this.gameOver) this.gameTime += deltaTime; if(this.gameTime > this.timeLimit) this.gameOver = true; this.player.update(); if (this.ammoTimer > this.ammoInterval){ if (this.ammo < this.maxAmmo) this.ammo++; this.ammoTimer = 0; } else { this.ammoTimer += deltaTime; } this.enemies.forEach(enemy => { enemy.update(); if (this.checkCollision(this.player, enemy)){ enemy.markedForDeletion = true; } this.player.projectiles.forEach(projectile => { if (this.checkCollision(projectile, enemy)){ enemy.lives--; projectile.markedForDeletion = true; if(enemy.lives <= 0) { enemy.markedForDeletion = true; if (!this.gameOver) this.score += enemy.score; if (this.score > this.winningScore) this.gameOver = true; } } }) }); this.enemies = this.enemies.filter(enemy => !enemy.markedForDeletion); if(this.enemyTimer > this.enemyInterval && this.gameOver === false){ this.addEnemy(); this.enemyTimer = 0; } else { this.enemyTimer += deltaTime; } } draw(context){ this.player.draw(context); this.ui.draw(context); this.enemies.forEach(enemy => { enemy.draw(context); }); } addEnemy(){ this.enemies.push(new Angler1(this)); } checkCollision(rect1, rect2){ return ( rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.height + rect1.y > rect2.y) } } const game = new Game(canvas.width, canvas.height); let lastTime = 0; //animation loop function animate(timeStamp){ const deltaTime = timeStamp - lastTime; lastTime = timeStamp; ctx.clearRect(0, 0, canvas.width, canvas.height); game.update(deltaTime); game.draw(ctx); requestAnimationFrame(animate); }
解决方案:实现敌人追踪逻辑
要让敌人追踪玩家,核心是计算敌人到玩家的方向向量,然后让敌人朝着这个方向移动。我会一步步帮你修改代码:
1. 重构Enemy类的基础属性
首先把Enemy类里的随机负速度改成固定基础移动速度(方向要动态计算),同时调整敌人的初始生成位置,更适合俯视视角:
class Enemy { constructor(game){ this.game = game; // 让敌人从画布内随机位置生成,更符合俯视游戏的体验 this.x = Math.random() * this.game.width; this.y = Math.random() * this.game.height; // 基础移动速度,子类可以重写这个值来区分敌人速度 this.speed = 2; this.markedForDeletion = false; this.lives = 5; this.score = this.lives; this.width = 0; this.height = 0; } // ... 其他方法 }
2. 修改Enemy的update方法,加入追踪逻辑
替换原来的update方法,加入方向计算和追踪移动逻辑:
update(){ // 计算敌人与玩家的中心点位置差,让追踪更自然 const dx = this.game.player.x + this.game.player.width/2 - (this.x + this.width/2); const dy = this.game.player.y + this.game.player.height/2 - (this.y + this.height/2); // 计算两者之间的直线距离 const distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { // 归一化方向向量,确保敌人移动速度恒定(不会因为距离远就突然加速) const moveX = dx / distance * this.speed; const moveY = dy / distance * this.speed; // 更新敌人位置 this.x += moveX; this.y += moveY; } // 修正删除逻辑:敌人超出画布范围就标记删除 if (this.x < -this.width || this.x > this.game.width || this.y < -this.height || this.y > this.game.height) { this.markedForDeletion = true; } }
3. (可选)给不同敌人设置差异化速度
比如让Angler1移动慢一点,可以在它的构造函数里重写速度值:
class Angler1 extends Enemy { constructor(game){ super(game); this.width = 228 * 0.2; this.height = 169 * 0.2; // 重写移动速度,让这个敌人更慢 this.speed = 1.5; } }
4. (可选)添加追踪范围限制
如果不想让敌人一出现就追踪玩家,可以加个距离判断,比如只有当玩家在300像素范围内才开始追踪,否则敌人随机移动:
update(){ const dx = this.game.player.x + this.game.player.width/2 - (this.x + this.width/2); const dy = this.game.player.y + this.game.player.height/2 - (this.y + this.height/2); const distance = Math.sqrt(dx * dx + dy * dy); // 只有距离小于300像素时才追踪玩家 if (distance < 300 && distance > 0) { const moveX = dx / distance * this.speed; const moveY = dy / distance * this.speed; this.x += moveX; this.y += moveY; } else { // 不在追踪范围内时,让敌人随机游荡 this.x += (Math.random() - 0.5) * 1; this.y += (Math.random() - 0.5) * 1; } // 删除逻辑不变 if (this.x < -this.width || this.x > this.game.width || this.y < -this.height || this.y > this.game.height) { this.markedForDeletion = true; } }
内容的提问来源于stack exchange,提问作者human computer
相关产品推荐
相关产品推荐

