Java实现飞机大战:从零到一构建Swing游戏(含完整源码与素材)
2026/7/14 11:22:41 网站建设 项目流程

1. 飞机大战游戏开发入门指南

第一次接触Java游戏开发时,我被Swing的简单易用深深吸引。记得当时用不到200行代码就做出了会动的矩形,那种成就感至今难忘。飞机大战作为经典射击游戏,非常适合用来入门Java GUI编程。

这个项目不需要任何第三方库,只要安装了JDK就能开始。我用的是IntelliJ IDEA,但Eclipse或VS Code也同样适用。建议选择Java 8或以上版本,因为后续我们会用到一些较新的语法特性。

游戏开发最有趣的地方在于,你能立即看到代码的运行效果。比如创建一个JFrame窗口,添加键盘监听,很快就能让飞机随着按键移动。这种即时反馈对初学者特别友好,不会像学习算法那样容易感到枯燥。

2. 搭建游戏基础框架

2.1 创建主窗口

游戏窗口是第一个要解决的问题。我习惯先定义好窗口尺寸,这里设置为600x800像素:

public class GameMain { static final int WIDTH = 600; static final int HEIGHT = 800; public static void main(String[] args) { JFrame frame = new JFrame("飞机大战"); frame.setSize(WIDTH, HEIGHT); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.setLocationRelativeTo(null); GamePanel panel = new GamePanel(); frame.add(panel); frame.setVisible(true); } }

踩过的坑:一定要记得调用setVisible(true),否则窗口不会显示。早期我经常忘记这行代码,对着空白屏幕调试半天。

2.2 游戏面板设计

GamePanel继承自JPanel,这是游戏的核心绘制区域。我们需要重写paintComponent方法:

public class GamePanel extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 这里绘制游戏元素 } }

实测发现直接重绘会导致闪烁问题。解决方案是使用双缓冲技术:

// 在构造函数中添加 setDoubleBuffered(true);

3. 游戏角色系统实现

3.1 玩家飞机控制

玩家飞机需要响应鼠标移动。我创建了Hero类来封装飞机逻辑:

public class Hero { private int x, y; private Image image; public Hero() { image = new ImageIcon("images/hero.png").getImage(); x = 300; y = 600; } public void draw(Graphics g) { g.drawImage(image, x, y, null); } public void moveTo(int x, int y) { this.x = x - image.getWidth(null)/2; this.y = y - image.getHeight(null)/2; } }

在GamePanel中添加鼠标监听:

addMouseMotionListener(new MouseAdapter() { @Override public void mouseMoved(MouseEvent e) { hero.moveTo(e.getX(), e.getY()); repaint(); } });

3.2 敌机系统设计

敌机需要自动生成和移动。我用了ArrayList来管理敌机对象:

public class Enemy { private int x, y; private int speed; private Image image; public Enemy() { image = new ImageIcon("images/enemy.png").getImage(); x = (int)(Math.random() * (GameMain.WIDTH - image.getWidth(null))); y = -image.getHeight(null); speed = (int)(Math.random() * 3) + 1; } public void move() { y += speed; } }

在GamePanel中定期创建新敌机:

// 每500毫秒创建一个新敌机 new Timer(500, e -> { enemies.add(new Enemy()); }).start();

4. 游戏核心机制实现

4.1 子弹发射系统

子弹需要从飞机位置向上移动。我设计了一个子弹池来复用对象:

public class Bullet { private int x, y; private int speed = 8; private boolean active = false; public void activate(int x, int y) { this.x = x; this.y = y; active = true; } public void move() { if(active) y -= speed; if(y < 0) active = false; } }

发射子弹的代码:

// 在游戏循环中 if(System.currentTimeMillis() - lastShootTime > 200) { bulletPool.stream() .filter(b -> !b.isActive()) .findFirst() .ifPresent(b -> { b.activate(hero.getX(), hero.getY()); lastShootTime = System.currentTimeMillis(); }); }

4.2 碰撞检测优化

最初我用矩形碰撞检测,但发现不够精确。后来改用圆形碰撞:

public boolean checkCollision(Enemy e, Bullet b) { int dx = e.getCenterX() - b.getCenterX(); int dy = e.getCenterY() - b.getCenterY(); int distance = (int)Math.sqrt(dx*dx + dy*dy); return distance < (e.getRadius() + b.getRadius()); }

性能优化:不要在paint方法中做复杂计算,所有碰撞检测应该在游戏逻辑线程中完成。

5. 游戏特效与音效

5.1 爆炸动画实现

爆炸效果由多张图片组成。我创建了Animation类来处理帧动画:

public class Explosion { private Image[] frames; private int currentFrame; private int x, y; public void draw(Graphics g) { if(currentFrame < frames.length) { g.drawImage(frames[currentFrame++], x, y, null); } } public boolean isFinished() { return currentFrame >= frames.length; } }

5.2 背景音乐与音效

使用Java的Clip类播放音效:

public class Sound { private Clip clip; public Sound(String filename) { try { AudioInputStream ais = AudioSystem.getAudioInputStream( new File("sounds/"+filename)); clip = AudioSystem.getClip(); clip.open(ais); } catch (Exception e) { e.printStackTrace(); } } public void play() { clip.setFramePosition(0); clip.start(); } }

6. 游戏状态管理

6.1 分数系统设计

分数显示使用Graphics的drawString方法:

// 在paintComponent中 g.setColor(Color.WHITE); g.setFont(new Font("Arial", Font.BOLD, 24)); g.drawString("分数: "+score, 20, 30);

分数增加逻辑放在碰撞检测后:

if(checkCollision(enemy, bullet)) { score += 10; explosions.add(new Explosion(enemy.getX(), enemy.getY())); }

6.2 游戏结束判断

当敌机到达屏幕底部时游戏结束:

enemies.forEach(e -> { e.move(); if(e.getY() > HEIGHT) { gameOver = true; } });

显示游戏结束画面:

if(gameOver) { g.setColor(new Color(255, 0, 0, 128)); g.fillRect(0, 0, WIDTH, HEIGHT); g.setColor(Color.WHITE); g.drawString("游戏结束!", WIDTH/2-60, HEIGHT/2); }

7. 性能优化技巧

7.1 对象池技术

频繁创建销毁对象会产生GC压力。我预先创建了对象池:

public class ObjectPool<T> { private Queue<T> pool; private Supplier<T> creator; public ObjectPool(int size, Supplier<T> creator) { this.creator = creator; pool = new LinkedList<>(); for(int i=0; i<size; i++) { pool.add(creator.get()); } } public T borrow() { return pool.isEmpty() ? creator.get() : pool.poll(); } public void returnObj(T obj) { pool.offer(obj); } }

7.2 渲染优化

只重绘发生变化的区域可以大幅提升性能:

// 在移动方法中 repaint(x, y, width, height); // 只重绘旧位置 repaint(newX, newY, width, height); // 重绘新位置

8. 完整项目结构与源码

最终项目结构如下:

aircraft-battle/ ├── src/ │ ├── GameMain.java │ ├── GamePanel.java │ ├── Hero.java │ ├── Enemy.java │ ├── Bullet.java │ └── Explosion.java ├── images/ │ ├── hero.png │ ├── enemy.png │ ├── bullet.png │ └── explosion/ │ ├── 1.png │ ├── 2.png │ └── ... └── sounds/ ├── shoot.wav ├── explosion.wav └── bgm.wav

核心游戏循环代码:

public void gameLoop() { new Thread(() -> { while(running) { updateGame(); repaint(); try { Thread.sleep(16); // 约60FPS } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } private void updateGame() { // 更新所有游戏对象状态 hero.update(); enemies.forEach(Enemy::update); bullets.forEach(Bullet::update); explosions.removeIf(Explosion::isFinished); // 碰撞检测 checkCollisions(); }

素材准备建议:可以使用免费的像素图素材网站如itch.io或OpenGameArt,注意遵守素材的授权协议。游戏音效推荐Freesound平台。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询