前情提要

在上一节中,我们添加了敌人角色,并实现了踩敌人的功能。但是敌人只是站在那里不动,这样的游戏没有什么挑战性。

这节课我们来给敌人添加 AI,让它能够自动左右移动。

什么是游戏 AI

游戏 AI(人工智能)听起来很高大上,但其实在简单的 2D 游戏中,AI 可以非常简单。

对于我们的敌人来说,AI 就是一套规则:

  • 向左移动,直到碰到边界
  • 碰到左边界后,转向右移动
  • 碰到右边界后,转向左移动
  • 如此循环往复

实现敌人的移动

首先,我们需要在 update 函数中更新敌人的位置:

function update(modifier) {
  // ... 其他更新逻辑 ...

  // 更新敌人的位置
  if (enemy.isAlive) {
    if (enemy.direction === 0) {
      // 向左移动
      enemy.x -= enemy.moveSpeed * modifier;
    } else {
      // 向右移动
      enemy.x += enemy.moveSpeed * modifier;
    }
  }

  // ... 其他更新逻辑 ...
}

实现边界检测和转向

当敌人碰到边界时,需要转向:

function enemyAI(enemy, modifier) {
  if (!enemy.isAlive) {
    return; // 如果敌人已经死了,就不执行 AI
  }

  // 检查是否碰到边界
  if (enemy.x <= 0) {
    enemy.direction = 1; // 碰到左边界,转向右
  } else if (enemy.x >= cvWidth) {
    enemy.direction = 0; // 碰到右边界,转向左
  }

  // 根据方向移动
  if (enemy.direction === 0) {
    enemy.x -= enemy.moveSpeed * modifier;
  } else {
    enemy.x += enemy.moveSpeed * modifier;
  }
}

然后在 update 函数中调用这个 AI 函数:

function update(modifier) {
  gameData.currentTimeStamp = new Date().getTime();
  gameData.updateDelta = modifier;
  gameData.fps = Math.floor(1 / modifier);

  if ("Space" in keysDown) {
    if (player.airCount < player.maxAirCount) {
      player.ySpeed += 20;
      player.airCount++;
    }
  }

  if ("KeyA" in keysDown) {
    player.x += -player.speed * modifier;
  }
  if ("KeyD" in keysDown) {
    player.x += player.speed * modifier;
  }

  if (player.x < 0) {
    player.x = 0;
  }
  if (player.x > cvWidth) {
    player.x = cvWidth;
  }

  // 调用敌人 AI
  enemyAI(enemy, modifier);

  gravityController(player, modifier);
  gravityController(enemy, modifier);

  // 碰撞检测
  if (enemy.isAlive && isCollision(player, enemy)) {
    if (isStampOn(player, enemy)) {
      enemy.isAlive = false;
      player.ySpeed = 200;
      gameData.score++;

      setTimeout(() => {
        enemy.isAlive = true;
        enemy.x = Math.random() * cvWidth;
        enemy.y = floorY;
      }, 1000);
    } else {
      console.log("被敌人撞到了!游戏结束!");
    }
  }
}

优化:让敌人越来越快

为了增加游戏难度,我们可以让敌人每次重生后移动速度变快一点:

if (isStampOn(player, enemy)) {
  enemy.isAlive = false;
  player.ySpeed = 200;
  gameData.score++;

  setTimeout(() => {
    enemy.isAlive = true;
    enemy.x = Math.random() * cvWidth;
    enemy.y = floorY;
    // 每次重生速度增加 10
    enemy.moveSpeed += 10;
  }, 1000);
}

优化:避免敌人重生在角色附近

如果敌人重生的位置离角色太近,玩家可能来不及反应就被撞到了。我们可以确保敌人重生时离角色有一定距离:

function respawnEnemy() {
  enemy.isAlive = true;
  enemy.y = floorY;

  // 重复随机位置,直到找到一个离角色足够远的位置
  do {
    enemy.x = Math.random() * cvWidth;
  } while (Math.abs(enemy.x - player.x) < 100); // 至少距离 100 像素

  enemy.moveSpeed += 10; // 速度增加
}

然后在踩到敌人时调用这个函数:

if (isStampOn(player, enemy)) {
  enemy.isAlive = false;
  player.ySpeed = 200;
  gameData.score++;

  setTimeout(respawnEnemy, 1000);
}

添加速度上限

如果敌人的速度无限增加,游戏会变得不可能完成。我们可以设置一个速度上限:

function respawnEnemy() {
  enemy.isAlive = true;
  enemy.y = floorY;

  do {
    enemy.x = Math.random() * cvWidth;
  } while (Math.abs(enemy.x - player.x) < 100);

  // 速度增加,但不超过 200
  if (enemy.moveSpeed < 200) {
    enemy.moveSpeed += 10;
  }
}

更复杂的 AI 模式

如果你想让 AI 更有趣,可以添加更多的行为模式:

追踪玩家

function enemyAI(enemy, modifier) {
  if (!enemy.isAlive) {
    return;
  }

  // 简单的追踪:如果玩家在左边,就向左移动;如果在右边,就向右移动
  if (player.x < enemy.x) {
    enemy.direction = 0; // 向左
  } else {
    enemy.direction = 1; // 向右
  }

  // 根据方向移动
  if (enemy.direction === 0) {
    enemy.x -= enemy.moveSpeed * modifier;
  } else {
    enemy.x += enemy.moveSpeed * modifier;
  }

  // 边界限制
  if (enemy.x < 0) {
    enemy.x = 0;
  }
  if (enemy.x > cvWidth) {
    enemy.x = cvWidth;
  }
}

随机改变方向

function enemyAI(enemy, modifier) {
  if (!enemy.isAlive) {
    return;
  }

  // 每隔一段时间随机改变方向
  if (!enemy.lastDirectionChange) {
    enemy.lastDirectionChange = Date.now();
  }

  const now = Date.now();
  if (now - enemy.lastDirectionChange > 2000) { // 每 2 秒
    enemy.direction = Math.random() > 0.5 ? 0 : 1; // 随机选择方向
    enemy.lastDirectionChange = now;
  }

  // 碰到边界时转向
  if (enemy.x <= 0) {
    enemy.direction = 1;
  } else if (enemy.x >= cvWidth) {
    enemy.direction = 0;
  }

  // 根据方向移动
  if (enemy.direction === 0) {
    enemy.x -= enemy.moveSpeed * modifier;
  } else {
    enemy.x += enemy.moveSpeed * modifier;
  }
}

小结

这节课我们学习了:

  1. 如何实现简单的敌人 AI
  2. 如何让敌人自动左右移动
  3. 如何实现边界检测和转向
  4. 如何让敌人越来越快
  5. 如何避免敌人重生在角色附近
  6. 更复杂的 AI 模式(追踪、随机)

在下一节课中,我们会实现动画状态机,让角色和敌人的动作更加生动。

下载完整代码

Leason8.zip