引言
消灭病毒是一款休闲射击游戏,玩家控制飞机发射子弹消灭从上方不断袭来的病毒。本文用Java实现消灭病毒的核心AI系统,重点讲解弹幕轨迹的线性预测算法、扫描线碰撞检测以及最优射击策略,包含完整的代码实现。
游戏机制建模
战场坐标系
- 玩家飞机位于屏幕底部,可左右移动
- 病毒从屏幕顶部以不同速度向下移动
- 子弹从飞机向上直线飞行
- 碰撞检测在二维平面进行
病毒运动模型
病毒的运动轨迹可简化为线性或轻微正弦波动:
virus_x(t) = x0 + vx * t + A * sin(w * t)
virus_y(t) = y0 + vy * t
其中 A 为振幅,w 为角频率,控制左右摇摆幅度。
弹幕轨迹预测算法
为了提前规划射击位置,需要预测病毒未来的运动轨迹:
public class TrajectoryPredictor {
/**
* 预测病毒在t时刻后的位置(线性+正弦模型)
*/
public Point2D predictPosition(Virus virus, double t) {
double x = virus.getX() + virus.getVx() * t
+ virus.getAmplitude() * Math.sin(virus.getOmega() * t);
double y = virus.getY() + virus.getVy() * t;
return new Point2D.Double(x, y);
}
/**
* 计算子弹命中病毒所需的提前量
* @param bulletSpeed 子弹速度
* @return 预计命中时间,无解返回-1
*/
public double calculateLeadTime(Player player, Virus virus, double bulletSpeed) {
double dx = virus.getX() - player.getX();
double dy = virus.getY() - player.getY();
double vx = virus.getVx();
double vy = virus.getVy();
// 简化模型:忽略正弦扰动,用相对运动求解
// 子弹方向固定向上,相对速度 = (virus_vx, virus_vy - bulletSpeed)
double relVy = vy - bulletSpeed;
// 需要子弹到达病毒y坐标的时间
if (relVy >= 0) return -1; // 病毒下落速度比子弹快,无法命中
double t = dy / (-relVy); // 相对运动到达y坐标的时间
double hitX = virus.getX() + vx * t;
// 检查x坐标是否在射程内(玩家可左右移动补偿)
if (Math.abs(hitX - player.getX()) > player.getMaxMoveSpeed() * t + 50) {
return -1; // 即使最大速度移动也无法到达射击位置
}
return t;
}
}
扫描线碰撞检测
游戏中子弹和病毒数量众多,需要高效碰撞检测。扫描线算法(Sweep Line)可以将二维碰撞检测优化到接近线性:
public class SweepLineCollision {
static class Event implements Comparable<Event> {
double x;
boolean isStart;
Entity entity;
Event(double x, boolean isStart, Entity entity) {
this.x = x;
this.isStart = isStart;
this.entity = entity;
}
@Override
public int compareTo(Event other) {
return Double.compare(this.x, other.x);
}
}
/**
* 扫描线检测所有碰撞对
* 时间复杂度: O((N+M) log(N+M)),N=子弹数, M=病毒数
*/
public List<CollisionPair> detectCollisions(List<Bullet> bullets, List<Virus> viruses) {
List<Event> events = new ArrayList<>();
// 为每个实体生成x轴区间事件
for (Bullet b : bullets) {
double r = b.getRadius();
events.add(new Event(b.getX() - r, true, b));
events.add(new Event(b.getX() + r, false, b));
}
for (Virus v : viruses) {
double r = v.getRadius();
events.add(new Event(v.getX() - r, true, v));
events.add(new Event(v.getX() + r, false, v));
}
Collections.sort(events);
List<CollisionPair> collisions = new ArrayList<>();
Set<Entity> active = new HashSet<>();
for (Event e : events) {
if (e.isStart) {
// 新实体进入扫描范围,与当前活跃实体检测碰撞
for (Entity other : active) {
if (checkCollision(e.entity, other)) {
collisions.add(new CollisionPair(e.entity, other));
}
}
active.add(e.entity);
} else {
active.remove(e.entity);
}
}
return collisions;
}
private boolean checkCollision(Entity a, Entity b) {
double dx = a.getX() - b.getX();
double dy = a.getY() - b.getY();
double dist = Math.sqrt(dx * dx + dy * dy);
return dist < (a.getRadius() + b.getRadius());
}
}
最优射击策略
面对屏幕上大量病毒,AI需要决定射击目标和移动位置:
public class ShootingStrategy {
private TrajectoryPredictor predictor = new TrajectoryPredictor();
/**
* 动态规划/贪心混合策略:优先消灭威胁最大的病毒
* 威胁评估 = 病毒价值 / (距离 * 移动所需时间)
*/
public Decision makeDecision(Player player, List<Virus> viruses, double bulletSpeed) {
Virus bestTarget = null;
double bestPriority = Double.NEGATIVE_INFINITY;
double bestMoveX = player.getX();
for (Virus v : viruses) {
double leadTime = predictor.calculateLeadTime(player, v, bulletSpeed);
if (leadTime < 0) continue; // 无法命中
Point2D futurePos = predictor.predictPosition(v, leadTime);
double moveDist = Math.abs(futurePos.getX() - player.getX());
double moveTime = moveDist / player.getMaxMoveSpeed();
if (moveTime > leadTime) continue; // 来不及移动到射击位置
// 威胁优先级 = 价值 / (距离 * 总响应时间)
double distToPlayer = Math.sqrt(
Math.pow(v.getX() - player.getX(), 2) +
Math.pow(v.getY() - player.getY(), 2)
);
double priority = v.getValue() / (distToPlayer * (leadTime + moveTime));
if (priority > bestPriority) {
bestPriority = priority;
bestTarget = v;
bestMoveX = futurePos.getX();
}
}
return new Decision(bestTarget, bestMoveX, bestTarget != null);
}
}
完整项目结构
virus-shooter/
├── src/
│ ├── model/
│ │ ├── Player.java # 玩家飞机
│ │ ├── Virus.java # 病毒实体
│ │ ├── Bullet.java # 子弹实体
│ │ └── Decision.java # AI决策封装
│ ├── physics/
│ │ ├── TrajectoryPredictor.java # 轨迹预测
│ │ └── SweepLineCollision.java # 扫描线碰撞检测
│ ├── strategy/
│ │ └── ShootingStrategy.java # 射击策略
│ └── game/
│ └── VirusGame.java # 游戏主循环
└── Main.java
复杂度分析
| 算法 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 轨迹预测 | O(1) | O(1) | 线性运动方程求值 |
| 扫描线碰撞 | O((N+M) log(N+M)) | O(N+M) | N=子弹数, M=病毒数 |
| 射击策略 | O(M) | O(1) | 遍历所有病毒计算优先级 |
总结
本文通过Java实现了消灭病毒射击游戏的核心AI系统。轨迹预测利用线性运动模型计算提前量,扫描线算法将碰撞检测从O(N×M)优化到O((N+M)log(N+M)),射击策略基于威胁优先级进行贪心决策。三者协同构成了高效的弹幕射击AI。