前情提要
在上一节中,我们添加了敌人角色,并实现了踩敌人的功能。但是敌人只是站在那里不动,这样的游戏没有什么挑战性。
这节课我们来给敌人添加 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;
}
}
小结
这节课我们学习了:
- 如何实现简单的敌人 AI
- 如何让敌人自动左右移动
- 如何实现边界检测和转向
- 如何让敌人越来越快
- 如何避免敌人重生在角色附近
- 更复杂的 AI 模式(追踪、随机)
在下一节课中,我们会实现动画状态机,让角色和敌人的动作更加生动。