一、开发前准备
开发Java打地鼠小游戏不需要复杂的第三方依赖,只要本地安装了JDK 8及以上版本即可,我们主要使用Java自带的Swing图形界面库完成界面开发,用AWT包处理事件逻辑。如果你用的是IDEA、Eclipse这类开发工具,直接新建一个普通的Java项目就能开始,不需要额外导入外部jar包。
首先要明确打地鼠游戏的核心需求:界面上有多个地鼠洞,地鼠会随机从某个洞里冒出,玩家点击冒出的地鼠就能得分,游戏有固定时长,时间结束后显示最终得分。基于这个需求,我们可以把开发拆成几个模块:界面绘制模块、地鼠状态控制模块、事件监听模块、得分计时模块。

二、基础界面搭建
界面部分我们用JFrame作为主窗口,用JPanel作为游戏画布,地鼠洞和地鼠都用图形绘制的方式实现,不需要引入外部图片资源,方便新手理解。首先我们先定义一些基础常量,比如窗口大小、地鼠洞的数量和位置、地鼠的大小等。
import javax.swing.*;
import java.awt.*;
// 打地鼠游戏主类
public class WhackAMoleGame extends JFrame {
// 游戏窗口宽度
private static final int WINDOW_WIDTH = 600;
// 游戏窗口高度
private static final int WINDOW_HEIGHT = 700;
// 地鼠洞列数
private static final int HOLE_COLS = 3;
// 地鼠洞行数
private static final int HOLE_ROWS = 3;
// 单个地鼠洞的边长
private static final int HOLE_SIZE = 120;
// 地鼠洞之间的间距
private static final int HOLE_GAP = 20;
// 地鼠大小(比洞小一圈)
private static final int MOLE_SIZE = 80;
// 游戏总时长(秒)
private static final int GAME_DURATION = 30;
// 游戏画布面板
private GamePanel gamePanel;
// 得分标签
private JLabel scoreLabel;
// 计时标签
private JLabel timeLabel;
// 当前得分
private int score = 0;
// 剩余时间
private int remainTime = GAME_DURATION;
// 计时器,控制地鼠出现和游戏计时
private Timer gameTimer;
// 地鼠出现的洞索引,-1表示没有地鼠
private int activeMoleIndex = -1;
public WhackAMoleGame() {
initWindow();
initComponents();
startGame();
}
// 初始化窗口属性
private void initWindow() {
setTitle("Java打地鼠小游戏");
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null); // 窗口居中显示
setResizable(false); // 禁止调整窗口大小
}
// 初始化界面组件
private void initComponents() {
// 使用边框布局
setLayout(new BorderLayout());
// 顶部信息面板,显示得分和剩余时间
JPanel infoPanel = new JPanel(new GridLayout(1, 2));
scoreLabel = new JLabel("得分:0", SwingConstants.CENTER);
scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
timeLabel = new JLabel("剩余时间:" + GAME_DURATION + "秒", SwingConstants.CENTER);
timeLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
infoPanel.add(scoreLabel);
infoPanel.add(timeLabel);
add(infoPanel, BorderLayout.NORTH);
// 游戏画布面板
gamePanel = new GamePanel();
gamePanel.setBackground(new Color(220, 220, 220));
add(gamePanel, BorderLayout.CENTER);
}
// 其他代码后续补充
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new WhackAMoleGame().setVisible(true);
});
}
}上面的代码定义了游戏的基础常量,初始化了主窗口和顶部的得分、时间显示面板,其中GamePanel是我们自定义的游戏画布类,接下来我们需要实现这个类,完成地鼠洞和地鼠的绘制。
游戏画布类实现
游戏画布需要继承JPanel,重写paintComponent方法完成图形绘制,同时需要添加鼠标点击事件监听,判断玩家是否点击到了地鼠。
// 游戏画布类,负责绘制界面和处理鼠标点击
class GamePanel extends JPanel {
private WhackAMoleGame gameFrame;
public GamePanel() {
// 添加鼠标点击事件监听
addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseClicked(java.awt.event.MouseEvent e) {
handleMouseClick(e.getX(), e.getY());
}
});
}
// 绘制界面
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// 计算地鼠洞的起始绘制位置,让整体居中
int startX = (getWidth() - (WhackAMoleGame.HOLE_COLS * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP) - WhackAMoleGame.HOLE_GAP)) / 2;
int startY = (getHeight() - (WhackAMoleGame.HOLE_ROWS * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP) - WhackAMoleGame.HOLE_GAP)) / 2;
// 绘制所有地鼠洞
for (int row = 0; row < WhackAMoleGame.HOLE_ROWS; row++) {
for (int col = 0; col < WhackAMoleGame.HOLE_COLS; col++) {
int holeX = startX + col * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP);
int holeY = startY + row * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP);
// 绘制地鼠洞(灰色圆形)
g.setColor(new Color(100, 100, 100));
g.fillOval(holeX, holeY, WhackAMoleGame.HOLE_SIZE, WhackAMoleGame.HOLE_SIZE);
// 绘制洞的边缘(深灰色)
g.setColor(new Color(60, 60, 60));
g.drawOval(holeX, holeY, WhackAMoleGame.HOLE_SIZE, WhackAMoleGame.HOLE_SIZE);
// 计算当前洞的索引
int index = row * WhackAMoleGame.HOLE_COLS + col;
// 如果当前洞有活跃地鼠,绘制地鼠
if (index == gameFrame.activeMoleIndex) {
int moleX = holeX + (WhackAMoleGame.HOLE_SIZE - WhackAMoleGame.MOLE_SIZE) / 2;
int moleY = holeY + (WhackAMoleGame.HOLE_SIZE - WhackAMoleGame.MOLE_SIZE) / 2;
// 绘制地鼠(棕色圆形)
g.setColor(new Color(139, 69, 19));
g.fillOval(moleX, moleY, WhackAMoleGame.MOLE_SIZE, WhackAMoleGame.MOLE_SIZE);
// 绘制地鼠眼睛
g.setColor(Color.WHITE);
g.fillOval(moleX + 20, moleY + 20, 15, 15);
g.fillOval(moleX + WhackAMoleGame.MOLE_SIZE - 35, moleY + 20, 15, 15);
g.setColor(Color.BLACK);
g.fillOval(moleX + 24, moleY + 24, 7, 7);
g.fillOval(moleX + WhackAMoleGame.MOLE_SIZE - 31, moleY + 24, 7, 7);
}
}
}
}
// 处理鼠标点击事件
private void handleMouseClick(int clickX, int clickY) {
// 计算地鼠洞的起始位置
int startX = (getWidth() - (WhackAMoleGame.HOLE_COLS * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP) - WhackAMoleGame.HOLE_GAP)) / 2;
int startY = (getHeight() - (WhackAMoleGame.HOLE_ROWS * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP) - WhackAMoleGame.HOLE_GAP)) / 2;
// 遍历所有地鼠洞,判断点击位置是否在某个洞的地鼠范围内
for (int row = 0; row < WhackAMoleGame.HOLE_ROWS; row++) {
for (int col = 0; col < WhackAMoleGame.HOLE_COLS; col++) {
int holeX = startX + col * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP);
int holeY = startY + row * (WhackAMoleGame.HOLE_SIZE + WhackAMoleGame.HOLE_GAP);
int moleX = holeX + (WhackAMoleGame.HOLE_SIZE - WhackAMoleGame.MOLE_SIZE) / 2;
int moleY = holeY + (WhackAMoleGame.HOLE_SIZE - WhackAMoleGame.MOLE_SIZE) / 2;
// 判断点击位置是否在当前地鼠范围内
if (clickX >= moleX && clickX <= moleX + WhackAMoleGame.MOLE_SIZE &&
clickY >= moleY && clickY <= moleY + WhackAMoleGame.MOLE_SIZE) {
int clickedIndex = row * WhackAMoleGame.HOLE_COLS + col;
// 如果点击的是活跃地鼠,得分并隐藏地鼠
if (clickedIndex == gameFrame.activeMoleIndex) {
gameFrame.increaseScore();
gameFrame.hideMole();
}
return;
}
}
}
}
// 设置关联的 gameFrame,方便获取游戏状态
public void setGameFrame(WhackAMoleGame gameFrame) {
this.gameFrame = gameFrame;
}
}三、核心游戏逻辑实现
界面绘制完成后,我们需要实现地鼠随机出现、游戏计时、得分统计这些核心逻辑。这里我们用Swing的Timer类做定时调度,每隔一段时间刷新一次地鼠状态,同时更新剩余时间。
地鼠状态控制
地鼠的出现和隐藏需要两个定时器协同工作:一个控制地鼠出现的间隔,一个控制单只地鼠显示的时长。为了避免复杂度,我们可以用一个定时器,每次触发时先判断当前是否有活跃地鼠,如果有就隐藏,没有就随机选一个洞显示地鼠。
// 补充WhackAMoleGame类中的方法
// 开始游戏
private void startGame() {
score = 0;
remainTime = GAME_DURATION;
updateScoreLabel();
updateTimeLabel();
activeMoleIndex = -1;
// 将gameFrame关联到gamePanel
gamePanel.setGameFrame(this);
// 创建游戏定时器,每1000毫秒(1秒)触发一次
gameTimer = new Timer(1000, e -> {
// 更新剩余时间
remainTime--;
updateTimeLabel();
// 如果时间到,结束游戏
if (remainTime <= 0) {
endGame();
return;
}
// 控制地鼠出现逻辑:如果当前没有活跃地鼠,随机选一个显示
if (activeMoleIndex == -1) {
// 生成0到HOLE_COLS*HOLE_ROWS-1的随机数,作为地鼠出现的洞索引
activeMoleIndex = (int) (Math.random() * HOLE_COLS * HOLE_ROWS);
gamePanel.repaint(); // 重绘画布,显示地鼠
// 地鼠显示1秒后自动隐藏
Timer moleTimer = new Timer(1000, moleEvent -> {
hideMole();
});
moleTimer.setRepeats(false); // 只执行一次
moleTimer.start();
}
});
gameTimer.start();
}
// 隐藏地鼠
public void hideMole() {
activeMoleIndex = -1;
gamePanel.repaint();
}
// 增加得分
public void increaseScore() {
score++;
updateScoreLabel();
}
// 更新得分标签
private void updateScoreLabel() {
scoreLabel.setText("得分:" + score);
}
// 更新时间标签
private void updateTimeLabel() {
timeLabel.setText("剩余时间:" + remainTime + "秒");
}
// 结束游戏
private void endGame() {
gameTimer.stop();
JOptionPane.showMessageDialog(this, "游戏结束!你的最终得分是:" + score, "提示", JOptionPane.INFORMATION_MESSAGE);
// 可选:结束后重置游戏
int choice = JOptionPane.showConfirmDialog(this, "是否重新开始游戏?", "提示", JOptionPane.YES_NO_OPTION);
if (choice == JOptionPane.YES_OPTION) {
startGame();
} else {
System.exit(0);
}
}到这里,一个简单的打地鼠小游戏就已经可以运行了。启动main方法后,窗口会弹出,顶部显示得分和剩余时间,每隔一段时间会随机有一个地鼠从洞里冒出来,点击地鼠就能得分,30秒后游戏结束,弹出最终得分提示。

四、功能扩展优化
基础版本的游戏功能比较单一,我们可以做一些简单的扩展,让游戏体验更好。比如调整地鼠出现的频率、增加不同分值的地鼠、添加音效、增加难度梯度等。
1. 调整地鼠出现时长
现在地鼠固定显示1秒,我们可以把显示时长改成随机值,比如500毫秒到1500毫秒之间,增加游戏的随机性:
// 修改启动地鼠定时器的部分,将固定的1000毫秒改成随机值
if (activeMoleIndex == -1) {
activeMoleIndex = (int) (Math.random() * HOLE_COLS * HOLE_ROWS);
gamePanel.repaint();
// 随机生成500-1500毫秒的显示时长
int moleShowTime = 500 + (int) (Math.random() * 1000);
Timer moleTimer = new Timer(moleShowTime, moleEvent -> {
hideMole();
});
moleTimer.setRepeats(false);
moleTimer.start();
}2. 增加难度梯度
可以让游戏随着时间推移难度逐渐增加,比如剩余时间越少,地鼠出现的频率越高,显示的时间越短:
// 修改定时器中的地鼠出现逻辑,根据剩余时间调整出现概率
gameTimer = new Timer(1000, e -> {
remainTime--;
updateTimeLabel();
if (remainTime <= 0) {
endGame();
return;
}
if (activeMoleIndex == -1) {
// 剩余时间越少,地鼠出现概率越高,最高80%
double appearRate = 0.3 + (double)(GAME_DURATION - remainTime) / GAME_DURATION * 0.5;
if (Math.random() < appearRate) {
activeMoleIndex = (int) (Math.random() * HOLE_COLS * HOLE_ROWS);
gamePanel.repaint();
// 剩余时间越少,地鼠显示时间越短,最短300毫秒
int moleShowTime = 1500 - (int)((double)(GAME_DURATION - remainTime) / GAME_DURATION * 1200);
moleShowTime = Math.max(moleShowTime, 300);
Timer moleTimer = new Timer(moleShowTime, moleEvent -> {
hideMole();
});
moleTimer.setRepeats(false);
moleTimer.start();
}
}
});3. 添加特殊地鼠
可以增加一种金色地鼠,点击后得5分,但是出现概率很低,显示时间也更短:
// 定义特殊地鼠的标识,0普通地鼠,1金色地鼠
private int moleType = 0;
// 普通地鼠得分
private static final int NORMAL_SCORE = 1;
// 金色地鼠得分
private static final int GOLDEN_SCORE = 5;
// 修改地鼠出现逻辑,10%概率出现金色地鼠
if (activeMoleIndex == -1) {
double appearRate = 0.3 + (double)(GAME_DURATION - remainTime) / GAME_DURATION * 0.5;
if (Math.random() < appearRate) {
activeMoleIndex = (int) (Math.random() * HOLE_COLS * HOLE_ROWS);
// 10%概率生成金色地鼠
if (Math.random() < 0.1) {
moleType = 1;
} else {
moleType = 0;
}
gamePanel.repaint();
int moleShowTime = 1500 - (int)((double)(GAME_DURATION - remainTime) / GAME_DURATION * 1200);
moleShowTime = Math.max(moleShowTime, 300);
// 金色地鼠显示时间更短,再减200毫秒
if (moleType == 1) {
moleShowTime = Math.max(moleShowTime - 200, 100);
}
Timer moleTimer = new Timer(moleShowTime, moleEvent -> {
hideMole();
});
moleTimer.setRepeats(false);
moleTimer.start();
}
}
// 修改得分逻辑,根据地鼠类型加分
public void increaseScore() {
if (moleType == 1) {
score += GOLDEN_SCORE;
} else {
score += NORMAL_SCORE;
}
updateScoreLabel();
}
// 修改绘制逻辑,金色地鼠用黄色绘制
if (index == gameFrame.activeMoleIndex) {
int moleX = holeX + (WhackAMoleGame.HOLE_SIZE - WhackAMoleGame.MOLE_SIZE) / 2;
int moleY = holeY + (WhackAMoleGame.HOLE_SIZE - WhackAMoleGame.MOLE_SIZE) / 2;
// 根据类型选颜色,普通棕色,金色黄色
if (gameFrame.moleType == 1) {
g.setColor(new Color(255, 215, 0));
} else {
g.setColor(new Color(139, 69, 19));
}
g.fillOval(moleX, moleY, WhackAMoleGame.MOLE_SIZE, WhackAMoleGame.MOLE_SIZE);
// 绘制眼睛等其他逻辑不变
}五、开发注意事项
在开发过程中有几个需要注意的点,避免踩坑:
- Swing的所有界面操作都需要在事件调度线程(EDT)中执行,所以我们main方法里用了
SwingUtilities.invokeLater,避免界面卡顿或者异常。 - 定时器的触发间隔不要太短,否则会频繁触发重绘,导致界面卡顿,一般游戏逻辑更新的间隔在100-1000毫秒之间比较合适。
- 鼠标点击的判断要准确,因为我们是用圆形绘制地鼠,严格来说应该判断点击位置是否在圆形范围内,上面的代码为了简化用了矩形判断,你可以优化成圆形碰撞检测,更准确。
- 如果在游戏里添加音效,建议使用单独的线程播放,不要阻塞EDT,否则点击地鼠的时候会出现卡顿。
通过这个小游戏的开发,你可以熟悉Java Swing的基本用法、事件监听机制、定时器的使用,以及简单的游戏逻辑设计思路。后续你还可以继续扩展,比如添加开始暂停按钮、最高分记录、更多的地鼠类型、不同的游戏场景等,逐步提升复杂度,也能更深入理解Java编程的相关知识点。
Java游戏开发打地鼠实现Java_Swing事件监听游戏逻辑设计修改时间:2026-05-24 20:18:24