敌人管理

Hey folks! 这里展示了敌人如何被调度、移动以及如何离场,下面附带了一些真实的游戏代码片段。

调度波

敌人出现被存储为敌人类型和时间戳的对。例如,游戏设置会排起一个小丝带波:

queueEnemyWave(entityType.ENEMY_RIBBON, 100, 110, 120, 130, 140);

助手将每个调度的出现添加到生成表中:

private void queueEnemyWave(entityType type, int... spawnTimes) {
    for (int spawnTime : spawnTimes) {
        game.addEnemySpawn(spawnTime, type);
    }
}

游戏时钟使用帧为单位。当出现时间到达时,如果还有活跃的敌人,喷发器会等待,防止一波立即叠加在另一波之上:

if (!enemyList.isEmpty()) {
    return;
}

在选定的出现时间,玩家等级决定生成的敌人数量:

int spawnMultiplier = playerLevel + 1 < 1 ? 1 : playerLevel + 1;

障碍块有自己的形成逻辑,包括随机的组大小和颜色。Boss 是特殊情况,它由单独的阶段计时器触发。

不同移动,共享更新循环

敌人共享位置、速度、血量和动画等核心实体数据,但类型决定其行为。例如,丝带敌人会从一个小的位置助手获取位置:

public static Position calculateEnemyPosition(float x, float speed, float worldHeight) {
    float nextX = x - .3f * speed;
    float nextY = 30 * MathUtils.sin(.1f * nextX) + worldHeight / 2f;
    return new Position(nextX, nextY);
}

更新循环会在 ribbon 敌人时调用该助手,而追踪器、射击者和障碍物则走不同的路径。追踪器会追随玩家,并且多种敌人类型会定时射击。例如,追踪器会将射线对准玩家:

case entityType.ENEMY_TRACKER:
    game.getEnemyBulletList().add(game.bulletFactory(
        entityType.ENEMY_BULLET,
        e.x,
        e.y,
        angleToPlayer(e, player)
    ));
    break;

伤害、失败与清理

当玩家子弹与敌人重叠时,游戏会检查敌人是否完全在屏幕上并且已经过了短暂的击中冷却时间后才应用伤害:

if (e.collisionRectangle.overlaps(f.collisionRectangle)
    && e.scale >= 1f
    && e.invincibilityTimer >= 5f) {
    e.invincibilityTimer = 0;
    e.health -= f.damage;
    releaseFromList(playerBulletList, j);
    entityEnemyDeathCheck(
        game.getPowerUPRedList(),
        game.getPowerUPBlueList(),
        game.getPowerUPGreenList(),
        game.getEnemyList(),
        game.getExplosionsList(),
        e,
        false
    );
}

死亡处理器会触发反馈:爆炸、分数和击杀计数更新,以及根据敌人类型掉落硬币或力量提升。被击败的实体会被排入删除队列,并在活动更新工作完成后清理,从而保持实体列表安全可处理。