You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在面向对象的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:39:59