每日算法 — 使用java实现小蜜蜂:敌人波浪阵型与最优射击策略

引言

小蜜蜂(Space Invaders)是街机游戏黄金时代的经典之作,玩家控制一艘飞船在屏幕底部移动,击落从上方成群结队压下来的外星敌人。游戏的核心算法挑战在于敌人阵型的波浪移动模式设计,以及在密集弹幕中寻找最优射击时机的决策策略。本文将用Java实现一个完整的小蜜蜂游戏,讲解基于正弦波的阵型运动算法、碰撞检测网格优化,以及基于威胁评估的贪心射击策略。

核心算法原理

1. 波浪阵型运动算法

敌人编队采用矩阵排列,整体运动可分解为水平移动和垂直下降两个阶段。为增加视觉趣味性,可引入正弦波叠加:

x(t) = baseX + amplitude * sin(frequency * t + phase)
y(t) = baseY + descentSpeed * t

其中 baseX 为编队基准位置,amplitude 控制左右摆动幅度,frequency 控制波动速度。

2. 空间哈希碰撞检测

当子弹和敌人数量增多时,逐对检测的O(N²)复杂度会成为性能瓶颈。空间哈希将屏幕划分为固定大小的网格单元,每个对象只与同一单元及相邻单元内的对象做碰撞检测:

cellX = object.x / CELL_SIZE
cellY = object.y / CELL_SIZE

理想情况下,每个单元内对象数趋近于常数,碰撞检测复杂度降为O(N)。

3. 威胁评估与贪心射击策略

AI需要选择射击哪个敌人以获得最高收益。贪心策略考虑以下因素:

  1. 距离优先:越近的敌人威胁越大
  2. 高度优先:越低的敌人越接近玩家
  3. 暴露度:左右无遮挡的敌人更容易击中
  4. 编队价值:击落后可能导致整行下坠的敌人价值更高

综合评分公式:

score = w1 * (screenHeight - enemy.y) + w2 * exposedBonus + w3 * formationValue

Java完整实现

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

/**
 * 小蜜蜂模拟 - 敌人波浪阵型与最优射击策略
 * 
 * 核心算法:
 * 1. 正弦波叠加的波浪阵型运动
 * 2. 空间哈希网格碰撞检测优化
 * 3. 威胁评估贪心射击策略
 * 4. 粒子爆炸与屏幕震动效果
 */
public class SpaceInvadersGame extends JPanel implements Runnable, KeyListener {

    // 游戏常量
    private static final int SCREEN_WIDTH = 800;
    private static final int SCREEN_HEIGHT = 600;
    private static final int CELL_SIZE = 40;        // 空间哈希单元大小
    private static final int ENEMY_ROWS = 5;
    private static final int ENEMY_COLS = 11;
    private static final double ENEMY_SPACING_X = 50;
    private static final double ENEMY_SPACING_Y = 40;
    private static final double PLAYER_SPEED = 5;
    private static final double BULLET_SPEED = 8;
    private static final int PLAYER_COOLDOWN = 15;  // 射击冷却帧数

    // 游戏对象
    private final List<Enemy> enemies = new ArrayList<>();
    private final List<Bullet> playerBullets = new ArrayList<>();
    private final List<Bullet> enemyBullets = new ArrayList<>();
    private final List<Particle> particles = new ArrayList<>();
    private Player player;

    // 游戏状态
    private volatile boolean running = true;
    private volatile boolean gameOver = false;
    private volatile int score = 0;
    private volatile int lives = 3;
    private volatile int wave = 1;
    private int frameCount = 0;
    private boolean aiMode = true;
    private int shakeFrames = 0;  // 屏幕震动剩余帧数

    // 敌人编队运动参数
    private double formationX = 100;
    private double formationY = 50;
    private double formationSpeed = 0.8;
    private double formationDirection = 1;  // 1为右,-1为左
    private double waveAmplitude = 15;
    private double waveFrequency = 0.03;
    private double descentAmount = 10;

    /**
     * 玩家飞船
     */
    static class Player {
        double x, y;
        double width = 40, height = 30;
        int cooldown = 0;
        boolean leftPressed, rightPressed, shootPressed;

        Player(double x, double y) {
            this.x = x;
            this.y = y;
        }

        Rectangle getBounds() {
            return new Rectangle((int) (x - width / 2), (int) (y - height / 2), 
                                (int) width, (int) height);
        }
    }

    /**
     * 敌人
     */
    static class Enemy {
        double baseX, baseY;    // 在编队中的相对位置
        double offsetX;         // 正弦波偏移
        int row, col;
        int type;               // 敌人类型(0-2,影响分值和外观)
        boolean alive;
        int animFrame;          // 动画帧
        double phase;           // 正弦波相位

        Enemy(double bx, double by, int r, int c, int type) {
            this.baseX = bx;
            this.baseY = by;
            this.row = r;
            this.col = c;
            this.type = type;
            this.alive = true;
            this.animFrame = 0;
            this.phase = c * 0.3;  // 每列不同相位,形成波浪效果
        }

        double getX() {
            return formationX + baseX + offsetX;
        }

        double getY() {
            return formationY + baseY;
        }

        Rectangle getBounds() {
            return new Rectangle((int) (getX() - 18), (int) (getY() - 14), 36, 28);
        }
    }

    /**
     * 子弹
     */
    static class Bullet {
        double x, y;
        double vy;
        boolean fromPlayer;
        Color color;

        Bullet(double x, double y, double vy, boolean player) {
            this.x = x;
            this.y = y;
            this.vy = vy;
            this.fromPlayer = player;
            this.color = player ? Color.GREEN : Color.RED;
        }

        Rectangle getBounds() {
            return new Rectangle((int) x - 2, (int) y - 6, 4, 12);
        }
    }

    /**
     * 爆炸粒子
     */
    static class Particle {
        double x, y, vx, vy;
        int life, maxLife;
        Color color;
        double size;

        Particle(double x, double y, double vx, double vy, 
                 Color c, int life, double size) {
            this.x = x;
            this.y = y;
            this.vx = vx;
            this.vy = vy;
            this.color = c;
            this.life = life;
            this.maxLife = life;
            this.size = size;
        }
    }

    public SpaceInvadersGame() {
        setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        setBackground(Color.BLACK);
        setFocusable(true);
        addKeyListener(this);

        initWave();
    }

    /**
     * 初始化一波敌人
     */
    private void initWave() {
        enemies.clear();
        playerBullets.clear();
        enemyBullets.clear();

        formationX = 100;
        formationY = 50 + wave * 10;
        formationSpeed = 0.5 + wave * 0.15;

        for (int row = 0; row < ENEMY_ROWS; row++) {
            for (int col = 0; col < ENEMY_COLS; col++) {
                double bx = col * ENEMY_SPACING_X;
                double by = row * ENEMY_SPACING_Y;
                int type = row / 2;  // 上方敌人分值更高
                enemies.add(new Enemy(bx, by, row, col, type));
            }
        }

        if (player == null) {
            player = new Player(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 50);
        }
    }

    /**
     * 更新游戏逻辑
     */
    private void update() {
        if (gameOver) return;

        frameCount++;
        if (shakeFrames > 0) shakeFrames--;

        // 更新玩家位置
        updatePlayer();

        // 更新敌人编队运动
        updateFormation();

        // 更新子弹
        updateBullets();

        // 碰撞检测(空间哈希优化)
        checkCollisions();

        // 敌人随机射击
        enemyShoot();

        // AI射击决策
        if (aiMode && player.cooldown <= 0) {
            aiShootDecision();
        }

        // 更新粒子
        updateParticles();

        // 检查波次完成
        if (enemies.stream().noneMatch(e -> e.alive)) {
            wave++;
            initWave();
        }

        // 检查敌人是否到达底部
        for (Enemy e : enemies) {
            if (e.alive && e.getY() > player.y - 20) {
                gameOver = true;
                return;
            }
        }
    }

    /**
     * 更新玩家位置
     */
    private void updatePlayer() {
        if (player.leftPressed) player.x -= PLAYER_SPEED;
        if (player.rightPressed) player.x += PLAYER_SPEED;

        // 边界限制
        player.x = Math.max(20, Math.min(SCREEN_WIDTH - 20, player.x));

        if (player.cooldown > 0) player.cooldown--;

        // 玩家射击
        if (player.shootPressed && player.cooldown <= 0 && !aiMode) {
            playerBullets.add(new Bullet(player.x, player.y - 15, -BULLET_SPEED, true));
            player.cooldown = PLAYER_COOLDOWN;
        }
    }

    /**
     * 更新敌人编队运动
     * 
     * 核心算法:
     * 1. 整体左右移动
     * 2. 到达边界后下移并反向
     * 3. 每列叠加独立的正弦波,形成波浪效果
     */
    private void updateFormation() {
        formationX += formationSpeed * formationDirection;

        // 检查是否到达边界
        double maxX = 0, minX = Double.MAX_VALUE;
        for (Enemy e : enemies) {
            if (!e.alive) continue;
            double x = e.getX();
            maxX = Math.max(maxX, x);
            minX = Math.min(minX, x);
        }

        if (maxX > SCREEN_WIDTH - 40 || minX < 40) {
            formationDirection *= -1;
            formationY += descentAmount;
        }

        // 正弦波偏移更新
        for (Enemy e : enemies) {
            if (!e.alive) continue;
            e.offsetX = waveAmplitude * Math.sin(frameCount * waveFrequency + e.phase);
            e.animFrame = (frameCount / 30) % 2;
        }
    }

    /**
     * 更新子弹位置
     */
    private void updateBullets() {
        Iterator<Bullet> it = playerBullets.iterator();
        while (it.hasNext()) {
            Bullet b = it.next();
            b.y += b.vy;
            if (b.y < 0) it.remove();
        }

        it = enemyBullets.iterator();
        while (it.hasNext()) {
            Bullet b = it.next();
            b.y += b.vy;
            if (b.y > SCREEN_HEIGHT) it.remove();
        }
    }

    /**
     * 空间哈希碰撞检测
     * 
     * 算法步骤:
     * 1. 将屏幕划分为 CELL_SIZE × CELL_SIZE 的网格
     * 2. 将每个子弹放入对应的网格单元
     * 3. 遍历每个敌人,只检测同单元及相邻单元内的子弹
     * 4. 若AABB相交,判定碰撞
     */
    private void checkCollisions() {
        // 构建空间哈希:格子坐标 -> 子弹列表
        Map<String, List<Bullet>> spatialHash = new HashMap<>();

        for (Bullet b : playerBullets) {
            int cx = (int) (b.x / CELL_SIZE);
            int cy = (int) (b.y / CELL_SIZE);
            String key = cx + "," + cy;
            spatialHash.computeIfAbsent(key, k -> new ArrayList<>()).add(b);
        }

        // 检测玩家子弹与敌人的碰撞
        Iterator<Enemy> eit = enemies.iterator();
        while (eit.hasNext()) {
            Enemy e = eit.next();
            if (!e.alive) continue;

            Rectangle enemyRect = e.getBounds();
            int minCX = (int) (enemyRect.x / CELL_SIZE) - 1;
            int maxCX = (int) ((enemyRect.x + enemyRect.width) / CELL_SIZE) + 1;
            int minCY = (int) (enemyRect.y / CELL_SIZE) - 1;
            int maxCY = (int) ((enemyRect.y + enemyRect.height) / CELL_SIZE) + 1;

            for (int cx = minCX; cx <= maxCX; cx++) {
                for (int cy = minCY; cy <= maxCY; cy++) {
                    String key = cx + "," + cy;
                    List<Bullet> bullets = spatialHash.get(key);
                    if (bullets == null) continue;

                    Iterator<Bullet> bit = bullets.iterator();
                    while (bit.hasNext()) {
                        Bullet b = bit.next();
                        if (enemyRect.intersects(b.getBounds())) {
                            e.alive = false;
                            bit.remove();
                            playerBullets.remove(b);

                            int points = (3 - e.type) * 10 * wave;
                            score += points;

                            createExplosion(e.getX(), e.getY(), getEnemyColor(e.type));
                            shakeFrames = 5;
                            break;
                        }
                    }
                }
            }
        }

        // 检测敌人子弹与玩家
        for (Bullet b : enemyBullets) {
            if (b.getBounds().intersects(player.getBounds())) {
                lives--;
                createExplosion(player.x, player.y, Color.CYAN);
                shakeFrames = 10;
                enemyBullets.clear();

                if (lives <= 0) {
                    gameOver = true;
                }
                break;
            }
        }
    }

    /**
     * 敌人随机射击
     */
    private void enemyShoot() {
        if (frameCount % (45 - Math.min(30, wave * 2)) != 0) return;

        // 收集最下方的存活敌人(每列)
        Map<Integer, Enemy> bottomEnemies = new HashMap<>();
        for (Enemy e : enemies) {
            if (!e.alive) continue;
            Enemy current = bottomEnemies.get(e.col);
            if (current == null || e.row > current.row) {
                bottomEnemies.put(e.col, e);
            }
        }

        // 随机选择一列射击
        List<Enemy> shooters = new ArrayList<>(bottomEnemies.values());
        if (!shooters.isEmpty()) {
            Enemy shooter = shooters.get((int) (Math.random() * shooters.size()));
            enemyBullets.add(new Bullet(shooter.getX(), shooter.getY() + 15, 
                                       BULLET_SPEED * 0.6, false));
        }
    }

    /**
     * AI贪心射击决策
     * 
     * 核心算法:
     * 1. 收集所有存活的敌人
     * 2. 对每个敌人计算威胁评分
     * 3. 评分因素:高度(越低越危险)、暴露度(左右无遮挡加分)、类型价值
     * 4. 选择评分最高的敌人,将玩家移动到其正下方射击
     */
    private void aiShootDecision() {
        List<Enemy> aliveEnemies = new ArrayList<>();
        for (Enemy e : enemies) {
            if (e.alive) aliveEnemies.add(e);
        }

        if (aliveEnemies.isEmpty()) return;

        Enemy bestTarget = null;
        double bestScore = -1;

        for (Enemy e : aliveEnemies) {
            double ex = e.getX();
            double ey = e.getY();

            // 因素1:高度评分(越低越危险,分数越高)
            double heightScore = (SCREEN_HEIGHT - ey) / SCREEN_HEIGHT * 100;

            // 因素2:暴露度评分(左右是否有遮挡)
            double exposedScore = 0;
            boolean leftBlocked = false, rightBlocked = false;
            for (Enemy other : aliveEnemies) {
                if (other == e) continue;
                if (Math.abs(other.getY() - ey) < 30) {
                    if (other.getX() < ex - 20) leftBlocked = true;
                    if (other.getX() > ex + 20) rightBlocked = true;
                }
            }
            if (!leftBlocked || !rightBlocked) exposedScore = 30;
            if (!leftBlocked && !rightBlocked) exposedScore = 50;

            // 因素3:类型价值
            double typeScore = (3 - e.type) * 10;

            // 因素4:距离玩家水平距离(越近越容易命中)
            double distScore = 50 - Math.abs(ex - player.x) / SCREEN_WIDTH * 50;

            double totalScore = heightScore * 0.4 + exposedScore * 0.3 + 
                               typeScore * 0.2 + distScore * 0.1;

            if (totalScore > bestScore) {
                bestScore = totalScore;
                bestTarget = e;
            }
        }

        if (bestTarget != null) {
            double targetX = bestTarget.getX();
            double diff = targetX - player.x;

            // 移动到目标正下方
            if (Math.abs(diff) > 5) {
                player.leftPressed = diff < 0;
                player.rightPressed = diff > 0;
            } else {
                player.leftPressed = false;
                player.rightPressed = false;
                // 到位后射击
                playerBullets.add(new Bullet(player.x, player.y - 15, 
                                            -BULLET_SPEED, true));
                player.cooldown = PLAYER_COOLDOWN;
            }
        }
    }

    /**
     * 创建爆炸粒子效果
     */
    private void createExplosion(double x, double y, Color color) {
        for (int i = 0; i < 12; i++) {
            double angle = Math.random() * Math.PI * 2;
            double speed = 1 + Math.random() * 4;
            particles.add(new Particle(x, y,
                Math.cos(angle) * speed, Math.sin(angle) * speed,
                color, 20 + (int) (Math.random() * 15), 2 + Math.random() * 3));
        }
    }

    /**
     * 更新粒子
     */
    private void updateParticles() {
        Iterator<Particle> it = particles.iterator();
        while (it.hasNext()) {
            Particle p = it.next();
            p.x += p.vx;
            p.y += p.vy;
            p.vy += 0.1;  // 重力
            p.life--;
            if (p.life <= 0) it.remove();
        }
    }

    /**
     * 获取敌人类型对应颜色
     */
    private Color getEnemyColor(int type) {
        switch (type) {
            case 0: return Color.MAGENTA;
            case 1: return Color.GREEN;
            case 2: return Color.CYAN;
            default: return Color.WHITE;
        }
    }

    @Override
    public void run() {
        while (running) {
            update();
            repaint();

            try {
                Thread.sleep(16);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                             RenderingHints.VALUE_ANTIALIAS_ON);

        // 屏幕震动效果
        int shakeX = 0, shakeY = 0;
        if (shakeFrames > 0) {
            shakeX = (int) ((Math.random() - 0.5) * 6);
            shakeY = (int) ((Math.random() - 0.5) * 6);
        }
        g2d.translate(shakeX, shakeY);

        // 绘制星星背景
        g2d.setColor(Color.WHITE);
        for (int i = 0; i < 50; i++) {
            int sx = (i * 137 + frameCount / 10) % SCREEN_WIDTH;
            int sy = (i * 89) % (SCREEN_HEIGHT - 100);
            g2d.fillRect(sx, sy, 1 + (i % 2), 1 + (i % 2));
        }

        // 绘制敌人
        for (Enemy e : enemies) {
            if (!e.alive) continue;

            double x = e.getX();
            double y = e.getY();
            Color c = getEnemyColor(e.type);

            g2d.setColor(c);

            // 绘制外星人形状
            int[] xPoints, yPoints;
            if (e.animFrame == 0) {
                xPoints = new int[]{(int) x - 15, (int) x - 10, (int) x, 
                                    (int) x + 10, (int) x + 15,
                                    (int) x + 8, (int) x - 8};
                yPoints = new int[]{(int) y + 10, (int) y - 5, (int) y - 12,
                                    (int) y - 5, (int) y + 10,
                                    (int) y + 5, (int) y + 5};
            } else {
                xPoints = new int[]{(int) x - 12, (int) x - 8, (int) x,
                                    (int) x + 8, (int) x + 12,
                                    (int) x + 6, (int) x - 6};
                yPoints = new int[]{(int) y + 8, (int) y - 8, (int) y - 10,
                                    (int) y - 8, (int) y + 8,
                                    (int) y + 3, (int) y + 3};
            }
            g2d.fillPolygon(xPoints, yPoints, 7);

            // 眼睛
            g2d.setColor(Color.WHITE);
            g2d.fillRect((int) x - 6, (int) y - 4, 4, 4);
            g2d.fillRect((int) x + 2, (int) y - 4, 4, 4);
        }

        // 绘制玩家
        if (lives > 0) {
            g2d.setColor(Color.GREEN);
            int px = (int) player.x;
            int py = (int) player.y;
            int[] pxPoints = {px, px - 20, px - 15, px + 15, px + 20};
            int[] pyPoints = {py - 15, py + 15, py + 10, py + 10, py + 15};
            g2d.fillPolygon(pxPoints, pyPoints, 5);

            // 引擎火焰
            g2d.setColor(Color.ORANGE);
            int flameH = 5 + (int) (Math.random() * 8);
            g2d.fillRect(px - 5, py + 15, 3, flameH);
            g2d.fillRect(px + 2, py + 15, 3, flameH);
        }

        // 绘制子弹
        for (Bullet b : playerBullets) {
            g2d.setColor(b.color);
            g2d.fillRect((int) b.x - 2, (int) b.y - 6, 4, 12);
        }
        for (Bullet b : enemyBullets) {
            g2d.setColor(b.color);
            g2d.fillOval((int) b.x - 3, (int) b.y - 3, 6, 6);
        }

        // 绘制粒子
        for (Particle p : particles) {
            float alpha = p.life / (float) p.maxLife;
            g2d.setColor(new Color(
                p.color.getRed() / 255.0f,
                p.color.getGreen() / 255.0f,
                p.color.getBlue() / 255.0f,
                alpha));
            int s = (int) (p.size * alpha);
            g2d.fillOval((int) (p.x - s / 2), (int) (p.y - s / 2), s, s);
        }

        // 绘制底部地面
        g2d.setColor(new Color(50, 50, 50));
        g2d.fillRect(0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, 20);

        // UI
        g2d.setColor(Color.WHITE);
        g2d.setFont(new Font("Arial", Font.BOLD, 18));
        g2d.drawString("分数: " + score, 20, 30);
        g2d.drawString("波次: " + wave, 20, 55);
        g2d.drawString("生命: " + lives, 20, 80);

        if (aiMode) {
            g2d.setColor(Color.CYAN);
            g2d.drawString("AI 模式", 20, 105);
        }

        if (gameOver) {
            g2d.setColor(new Color(0, 0, 0, 200));
            g2d.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
            g2d.setColor(Color.RED);
            g2d.setFont(new Font("Arial", Font.BOLD, 48));
            String msg = "GAME OVER";
            FontMetrics fm = g2d.getFontMetrics();
            g2d.drawString(msg, (SCREEN_WIDTH - fm.stringWidth(msg)) / 2, 
                          SCREEN_HEIGHT / 2 - 20);
            g2d.setFont(new Font("Arial", Font.BOLD, 24));
            g2d.setColor(Color.WHITE);
            String scoreMsg = "最终分数: " + score;
            g2d.drawString(scoreMsg, (SCREEN_WIDTH - fm.stringWidth(scoreMsg)) / 2 + 20, 
                          SCREEN_HEIGHT / 2 + 30);
        }

        g2d.translate(-shakeX, -shakeY);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (aiMode) return;
        switch (e.getKeyCode()) {
            case KeyEvent.VK_LEFT: player.leftPressed = true; break;
            case KeyEvent.VK_RIGHT: player.rightPressed = true; break;
            case KeyEvent.VK_SPACE: player.shootPressed = true; break;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        if (aiMode) return;
        switch (e.getKeyCode()) {
            case KeyEvent.VK_LEFT: player.leftPressed = false; break;
            case KeyEvent.VK_RIGHT: player.rightPressed = false; break;
            case KeyEvent.VK_SPACE: player.shootPressed = false; break;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("小蜜蜂 - 敌人波浪阵型与最优射击策略");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            SpaceInvadersGame game = new SpaceInvadersGame();
            frame.add(game);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            new Thread(game).start();
        });
    }
}

算法复杂度分析

操作 时间复杂度 空间复杂度 说明
编队运动更新 O(N) O(1) N为敌人数量
空间哈希构建 O(B) O(B) B为子弹数
碰撞检测 O(N × K) O(B) K为每格平均子弹数,K≈常数
AI射击决策 O(N²) O(1) 遍历所有敌人对
粒子更新 O(P) O(1) P为粒子数

核心算法优化点

  1. 正弦波叠加:每列敌人使用不同相位的正弦波偏移,形成视觉上的波浪蠕动效果,增强游戏表现力。
  2. 空间哈希碰撞检测:将O(N×B)的逐对检测优化为O(N×K),当K为常数时近似O(N),显著提升大规模战斗场景的性能。
  3. 多因素威胁评分:AI综合考虑高度、暴露度、类型价值和水平距离四个维度,使用加权评分选择最优射击目标。
  4. 底部敌人优先射击:只有每列最下方的敌人才有射击权限,符合原作设计且降低子弹密度。

扩展思考

  1. 阵型变换:引入多种预设阵型(V字形、菱形、螺旋),通过状态机切换,增加游戏多样性。
  2. A*路径规划:让AI控制的玩家飞船不仅考虑射击,还需同时规划躲避 enemy bullets 的移动路径。
  3. 群体智能:使用Boids算法让敌人编队具有分离、对齐、聚合行为,形成更自然的群体移动。

总结

本文通过Java实现了一个完整的小蜜蜂游戏模拟,涵盖波浪阵型运动、空间哈希碰撞检测和贪心威胁评估射击策略三大核心算法。该实现不仅还原了经典街机游戏的核心机制,也展示了如何用空间分割优化碰撞检测,以及如何用多因素评分指导AI决策,为理解实时游戏AI和性能优化提供了实用范例。