飞机大战 V2.0 版重磅升级!从会动到能打,核心玩法全面升级
引言
在V1版本中,我们实现了玩家飞机移动、敌方自动生成和简单的子弹框架,但游戏缺少核心玩法——射击和碰撞,敌方也不会攻击。V2版本针对这些痛点进行了全面升级,真正让游戏“活”了起来。
本文详细解析V2版本的核心改进,涵盖:
线程安全集合解决并发修改异常
双缓冲技术 + 滚动背景,消除闪烁、增强视觉效果
完整的碰撞检测系统(子弹与飞机、敌机与玩家)
生命值与积分系统,并可视化血条
敌方AI:自动射击 + 随机水平移动,增加游戏难度
玩家射击功能(空格键)
统一图片管理(
ImageManager)
让我们一步步拆解这些新特性,看看它们是如何实现的。
项目架构
V2版本在V1基础上新增了两个类,并对原有类进行了重构:
| 类名 | 职责 |
|---|---|
GameUI |
主窗口,改用CopyOnWriteArrayList存储游戏元素,启动自动射击线程 |
GameThread |
游戏主线程,增加了碰撞检测、积分血量绘制、双缓冲滚动背景 |
AutoFighterThread |
自动生成敌方,调整生成间隔和敌方速度 |
AutoShootThread |
新增:控制所有敌方飞机自动发射子弹,并随机改变其水平速度(走位) |
Fighter |
飞机类,新增shoot()方法,玩家射击用;敌方移动增加水平速度 |
Bullet |
子弹类,区分敌我图片,尺寸不同,移动边界判断增加上边界 |
GameListener |
键盘监听,增加空格键发射子弹,并传入子弹列表 |
ImageManager |
新增:统一加载和存放所有图片资源,避免各类型重复加载 |
Data |
常量接口,保持不变 |
项目新功能详解
1. 线程安全:从 ArrayList 到 CopyOnWriteArrayList
问题背景
V1版本中,AutoFighterThread 向 fighters 添加敌机,同时 GameThread 遍历 fighters 进行绘制和移动,容易抛出 ConcurrentModificationException。

解决方案
为了图方便,我直接使用了 java.util.concurrent.CopyOnWriteArrayList,用这样一个并发容器在写操作时创建底层数组的新副本,读操作直接返回原数组
// GameUI.java
CopyOnWriteArrayList<Fighter> fighters = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<Bullet> enBullets = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<Bullet> myBullets = new CopyOnWriteArrayList<>();
解析
所有集合声明为
CopyOnWriteArrayList,并传递到各个线程。在
GameThread中遍历时,不再需要手动同步,也不会出现并发异常。
但是,它的代价是写操作(添加/删除)有额外开销,(附上:比如他add方法的源代码)
public boolean add(E e) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
但还好是现在游戏中元素数量不多。
总体来说性能比较低,只适合小游戏,读多写少的情况。(后续还需要调整)
2. 图片管理:ImageManager 封装统一加载
问题背景
V1中每个类都单独加载自己的图片,且使用绝对路径,重复代码多、移植性差。
解决方法
创建 ImageManager 类,集中加载所有图片,并提供静态字段供全局访问。
public class ImageManager {
public static Image myFighterImage;
public static Image enemyImage;
public static Image myBulletImage;
public static Image enemyBulletImage;
public static Image backgroundImage;
static {
String myFighterPath = "C:\\Users\\zgr\\Desktop\\代码库\\飞机大战\\GameV1\\pictures\\myfighternobg.png";
// ... 其他路径
try {
myFighterImage = ImageIO.read(new File(myFighterPath));
// ... 加载其他图片
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private ImageManager() {}
}
解析
静态初始化块在类加载时执行,确保所有图片只加载一次。
其他类(如
Fighter、Bullet)不再自己加载图片,直接使用ImageManager.xxxImage。
但路径仍然是绝对路径,后续将优化改为相对路径或类路径资源。
3.滚动背景:双图无缝滚动
问题背景
想让游戏画面真实自然一些
解决方案
在 GameThread 中添加一个滚动偏移量 scrollY,每帧增加2,并绘制两张背景图(一张在上方,一张在下方)。背景图片从下往上循环滚动,模拟飞机向前飞行的视觉效果。
// GameThread.java 部分代码
private int scrollY = 0;
public void run() {
while (true) {
Thread.sleep(30);
scrollY += 2;
if (scrollY >= Data.UIHEIGHT) {
scrollY -= Data.UIHEIGHT;
}
// 绘制第一张背景(上方)
bufferGraphics.drawImage(ImageManager.backgroundImage, 0, scrollY - Data.UIHEIGHT, null);
// 绘制第二张背景(下方)
bufferGraphics.drawImage(ImageManager.backgroundImage, 0, scrollY, null);
// ... 绘制游戏元素
}
}
解析
当
scrollY超过窗口高度时,减去UIHEIGHT实现循环。两张图交替位置,保证任何时候都能覆盖整个窗口,形成连续滚动。
4. 碰撞检测(核心玩法)
V2实现了两类碰撞检测:
-
敌方子弹与玩家飞机的碰撞
-
我方子弹与敌方飞机的碰撞
//从后向前遍历,避免删除元素时索引混乱
for (int i = enBullets.size() - 1; i >= 0; i--) {
enBullets.get(i).draw(bufferGraphics);
enBullets.get(i).move();
//判断子弹是否超出边界
if (enBullets.get(i).getBlood() == 0) {
enBullets.remove(i);
} else {
//遍历所有我方战机的位置,判断是否发生碰撞
if (enBullets.get(i).getX() >= myFighter.getX() && enBullets.get(i).getX() <= myFighter.getX() + myFighter.getWidth() &&
enBullets.get(i).getY() >= myFighter.getY() && enBullets.get(i).getY() <= myFighter.getY() + myFighter.getHeight()){
myFighter.setBlood(myFighter.getBlood() - 1);
enBullets.get(i).setBlood(0);
}
}
}
//用fori循环
for (int i = myBullets.size() - 1; i >= 0; i--) {
myBullets.get(i).draw(bufferGraphics);
myBullets.get(i).move();
//判断子弹是否超出边界
if (myBullets.get(i).getBlood() == 0) {
myBullets.remove(i);
continue;
}
//遍历所有敌方战机的位置,判断是否发生碰撞
for (int j = fighters.size() - 1; j >= 0; j--) {
//坐标判断
if (myBullets.get(i).getX() >= fighters.get(j).getX() && myBullets.get(i).getX() <= fighters.get(j).getX() + fighters.get(j).getWidth() &&
myBullets.get(i).getY() >= fighters.get(j).getY() && myBullets.get(i).getY() <= fighters.get(j).getY() + fighters.get(j).getHeight()){
fighters.get(j).setBlood(0);
myBullets.get(i).setBlood(0);
score += 10;
break;
}
}
}
解析
采用简单的矩形(AABB)碰撞检测,比较子弹左上角坐标是否落在玩家矩形内。
一旦命中,玩家血量减1,子弹血量置0(子弹消失),积分加10。
5.生命值系统与可视化血条
玩家生命值
在 Fighter 构造方法中,玩家血量为100,敌方为10。被击中时,玩家血量减少1(
血条绘制
在 GameThread 中绘制血条背景和当前血量对应的绿色条。
// 绘制血条背景(黑色)
bufferGraphics.setColor(Color.BLACK);
bufferGraphics.fillRect(25, 55, 200, 20);
// 绘制血量(绿色)
bufferGraphics.setColor(Color.GREEN);
bufferGraphics.fillRect(26, 56, myFighter.getBlood() * 2 - 2, 18);
解析
血条宽度200,对应最大血量100,因此每个血量单位占2像素。
计算时减2是为了边框留白,视觉效果更好。
生命值文本
// 绘制血条背景(黑色)
bufferGraphics.setColor(Color.BLACK);
bufferGraphics.fillRect(25, 55, 200, 20);
// 绘制血量(绿色)
bufferGraphics.setColor(Color.GREEN);
bufferGraphics.fillRect(26, 56, myFighter.getBlood() * 2 - 2, 18);
6. 积分系统
积分累计
定义变量 int score = 0,每击中一架敌机增加10分。
score += 10;
积分显示
积分直接以文字形式写在窗口右上角
bufferGraphics.setColor(Color.RED);
bufferGraphics.drawString("积分:" + score, 370, 55);
7. 游戏难度:敌机随机走位 + 自动射击
随机水平移动
在 AutoShootThread 中,每0.9秒遍历所有敌方,并随机改变其水平速度。
Fighter fighter = fighters.get(i);
if (count % 2 == 0) {
int speedx = random.nextInt(7) - 3;
fighter.setSpeedx(speedx);
}
解析
每两次循环改变一次敌方水平速度,模拟敌方左右飘移。
速度值在 -3 到 3 之间,使移动平滑且不至于太快飞出屏幕。
自动射击
在同一个循环中,为每个敌方创建子弹并添加到 enBullets。
Bullet bullet = new Bullet(true, fighter.getX() + fighter.getWidth() / 2 - 15,
fighter.getY() + fighter.getHeight() / 2 + 12, 0, 5);
enBullets.add(bullet);
解析
子弹从飞机中心偏下位置发射,向下速度5。
8. 玩家射击
触发方式
在 GameListener 中增加对空格键的处理:
case KeyEvent.VK_SPACE:
myFighter.shoot(myBullets);
break;
Fighter.shoot() 方法
public void shoot(CopyOnWriteArrayList<Bullet> myBullets) {
Bullet bullet = new Bullet(isEnemy, x + width/2 - 12, y + height/2 - 38, 0, -10);
myBullets.add(bullet);
}
解析
子弹从玩家飞机中心偏上位置发出,向上速度10。
注意
isEnemy此时为false,所以子弹图片会是玩家子弹。该方法被调用时,将子弹加入传入的
myBullets列表。
项目展示
GameV2
项目优化方向
增加功能:
1.增加爆炸特效、击中特效等
2.增加游戏背景音效、打击音效等
3.增加飞机飞行特效姿势等
4.增加boss战斗,设置关卡难度
5.支持更多敌人类型,引入不同血量、速度、射击方式的敌机
6.增加游戏状态管理,设置开始界面、暂停功能、游戏结束检测
7.开设道具系统,比如护盾、双倍子弹等
优化功能:
1.将所有图片打包放入资源根目录里
2.使用更精确的碰撞检测,避免矩形检测的误差
3.线程管理方面的优化
项目总结
V2版本在V1的基础上实现了质的飞跃,从一个“飞机移动演示”变成了一个具有基本玩法的可玩游戏。主要成果包括:
-
✅ 完整的游戏循环(双缓冲 + 滚动背景)
-
✅ 碰撞检测(子弹命中、敌机撞玩家)
-
✅ 生命值与积分系统(血条可视化)
-
✅ 敌方AI(自动射击 + 随机走位)
-
✅ 玩家交互(WASD移动 + 空格射击)
-
✅ 线程安全(使用
CopyOnWriteArrayList解决并发问题) -
✅ 资源管理(
ImageManager统一管理图片)
为后续版本(V3)增加更多特效、道具、难度关卡等扩展打下了坚实基础。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)