首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java Swing 遊戲開發實戰:青蛙跳石頭(方塊版)完整源碼解析

     編輯:Java 綜合教學

遊戲背景與設計思路

本遊戲改編自微信熱門網頁版「青蛙跳石頭」,移植為 Java Swing 桌面版。為專注邏輯實現,未使用圖片素材,改用彩色方塊代表青蛙。棋盤 7 個位置,初始左右各 3 只不同顏色方塊,中間空 1 格。目標:使左右兩邊方塊交換位置。

核心規則

  1. 點擊方塊使其跳躍
  2. 方塊不能向後跳,單次最多跨越 1 個方塊
  3. 將左右兩組方塊完全交換位置即獲勝

架構設計:MVC 簡化版

  • Main 類:繼承 JFrame,負責視窗初始化、遊戲主循環(重繪執行緒)、事件分發(鍵盤/滑鼠)
  • Frog 類:封裝方塊邏輯模型(位置、陣營、可見性)、繪製邏輯、移動算法、勝負判定、重置功能

關鍵代碼解析

Main 類:視窗與遊戲循環

public class Main extends JFrame {
    public static final int GAME_WIDTH = 420;
    public static final int GAME_HEIGHT = 150;
    // ... 位置常數
    
    private Frog frog; // 遊戲模型
    private DateFormat format = new SimpleDateFormat("mm:ss");
    private String startTime, endTime;
    private long elapsedTime;

    private void launchFrame() {
        // 視窗屬性設置
        setLocation(GAME_X, GAME_Y);
        setSize(GAME_WIDTH, GAME_HEIGHT);
        setTitle("Frog Jump Game");
        setResizable(false);
        getContentPane().setBackground(Color.WHITE);
        
        // 事件監聽器註冊
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) { System.exit(0); }
        });
        addMouseListener(new MouseHandler());
        addKeyListener(new KeyHandler());
        
        setVisible(true);
        // 獨立執行緒驅動重繪與計時
        new Thread(new PaintThread()).start();
    }

    public void paint(Graphics g) {
        super.paint(g);
        // 繪製 7 個位置框
        for (int i = 1; i < 8; i++) {
            g.drawRect(i * 50 - 5, 45, 30, 30);
        }
        frog.draw(g); // 委託模型繪製
        
        // 計時顯示
        try {
            elapsedTime = format.parse(endTime).getTime() - format.parse(startTime).getTime();
            g.drawString("時間:" + format.format(new Date(elapsedTime)), 250, 40);
        } catch (ParseException e) { e.printStackTrace(); }
        
        // 操作提示
        g.drawString("F2 重新開始", 100, 40);
        g.drawString("規則:1.點擊方塊跳動", 85, 100);
        g.drawString("2.不可後退,最多跨越 1 格", 120, 115);
        g.drawString("3.左右交換位置獲勝", 120, 130);
    }

    // 重繪執行緒:每秒刷新一次
    private class PaintThread implements Runnable {
        public void run() {
            while (true) {
                repaint();
                endTime = format.format(new Date());
                try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); }
            }
        }
    }
    
    // 事件分發給模型處理
    private class KeyHandler extends KeyAdapter {
        public void keyReleased(KeyEvent e) { frog.keyReleased(e); }
    }
    private class MouseHandler extends MouseAdapter {
        public void mouseClicked(MouseEvent e) { frog.mouseClicked(e); }
    }
}

Frog 類:核心邏輯模型

public class Frog {
    // 內部類:方塊屬性封裝
    private static class Block {
        int x, y;       // 座標
        boolean blueTeam; // 陣營:true=藍(左),false=紅(右)
        boolean visible;  // 是否顯示(中間位置初始隱藏)
        Block(int x, int y, boolean blue, boolean vis) { ... }
    }

    private static final int BLOCK_SIZE = 20;
    private static final int TOTAL_BLOCKS = 7;
    private static final int BASE_X = 50, BASE_Y = 50;
    
    private List<Block> blocks = new ArrayList<>();
    private Main mainRef;

    public Frog(Main m) { this.mainRef = m; initBlocks(); }

    private void initBlocks() {
        blocks.clear();
        for (int i = 1; i <= TOTAL_BLOCKS; i++) {
            if (i <= TOTAL_BLOCKS / 2) 
                blocks.add(new Block(BASE_X * i, BASE_Y, true, true));      // 左側藍色
            else if (i == TOTAL_BLOCKS / 2 + 1) 
                blocks.add(new Block(BASE_X * i, BASE_Y, true, false));     // 中間空位
            else 
                blocks.add(new Block(BASE_X * i, BASE_Y, false, true));     // 右側紅色
        }
    }

    public void draw(Graphics g) {
        Color old = g.getColor();
        for (Block b : blocks) {
            if (b.visible) {
                g.setColor(b.blueTeam ? Color.BLUE : Color.RED);
                g.fillRect(b.x, b.y, BLOCK_SIZE, BLOCK_SIZE);
            }
        }
        g.setColor(old);
    }

    // 勝負判定:左半邊全紅、中間空、右半邊全藍
    public boolean isGameOver() {
        for (int i = 0; i < TOTAL_BLOCKS; i++) {
            Block b = blocks.get(i);
            if (i < TOTAL_BLOCKS / 2) { if (b.blueTeam) return false; }
            else if (i == TOTAL_BLOCKS / 2) { if (b.visible) return false; }
            else { if (!b.blueTeam) return false; }
        }
        return true;
    }

    public void reset() {
        initBlocks();
        mainRef.repaint();
        String now = mainRef.format.format(new Date());
        mainRef.startTime = mainRef.endTime = now;
    }

    // 滑鼠點擊移動算法核心
    public void mouseClicked(MouseEvent e) {
        int x = e.getX(), y = e.getY();
        // 點擊區域檢測:Y 在方塊垂直範圍內,X 對應 7 個位置之一
        if (y > 50 && y < 70 && x % 50 <= 20) {
            int idx = x / 50 - 1; // 轉換為 0-based 索引
            Block clicked = blocks.get(idx);
            
            if (!clicked.visible) return; // 點擊空位無效
            
            if (clicked.blueTeam) { // 藍方:向右移動
                tryMoveRight(idx);
            } else { // 紅方:向左移動
                tryMoveLeft(idx);
            }
        }
        mainRef.repaint();
        if (isGameOver()) showVictoryDialog();
    }

    private void tryMoveRight(int idx) {
        // 相鄰右格有方塊,且再右格為空 → 跳躍兩格
        if (idx < TOTAL_BLOCKS - 1 && blocks.get(idx + 1).visible) {
            if (idx < TOTAL_BLOCKS - 2 && !blocks.get(idx + 2).visible) {
                blocks.set(idx, new Block(BASE_X * (idx + 1), BASE_Y, true, false));
                blocks.set(idx + 2, new Block(BASE_X * (idx + 3), BASE_Y, true, true));
                return;
            }
        }
        // 相鄰右格為空 → 移動一格
        if (idx < TOTAL_BLOCKS - 1 && !blocks.get(idx + 1).visible) {
            blocks.set(idx, new Block(BASE_X * (idx + 1), BASE_Y, true, false));
            blocks.set(idx + 1, new Block(BASE_X * (idx + 2), BASE_Y, true, true));
        }
    }

    private void tryMoveLeft(int idx) {
        // 鏡像邏輯:向左移動
        if (idx > 0 && blocks.get(idx - 1).visible) {
            if (idx > 1 && !blocks.get(idx - 2).visible) {
                blocks.set(idx, new Block(BASE_X * (idx + 1), BASE_Y, false, false));
                blocks.set(idx - 2, new Block(BASE_X * (idx - 1), BASE_Y, false, true));
                return;
            }
        }
        if (idx > 0 && !blocks.get(idx - 1).visible) {
            blocks.set(idx, new Block(BASE_X * (idx + 1), BASE_Y, false, false));
            blocks.set(idx - 1, new Block(BASE_X * idx, BASE_Y, false, true));
        }
    }

    private void showVictoryDialog() {
        Object[] options = {"OK", "CANCEL"};
        int choice = JOptionPane.showOptionDialog(null, 
            "完成遊戲\n用時:" + mainRef.format.format(new Date(mainRef.elapsedTime)),
            "遊戲結束", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, 
            null, options, options[0]);
        if (choice == JOptionPane.YES_OPTION) System.exit(0);
        else reset();
    }

    public void keyReleased(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_F2) reset();
    }
}

算法核心邏輯圖解

移動規則可抽象為狀態機:

當前位置 idx,目標方向 dir(+1 右 / -1 左)
1. 相鄰位置 idx+dir 有方塊?
   是 → 檢查 idx+2*dir 是否為空?
         是 → 跳躍兩格(原位變空,目標位置生成方塊)
         否 → 不可移動
   否 → 相鄰位置為空 → 移動一格

編譯與運行

  1. 將 Main.java、Frog.java 放入同一目錄
  2. javac Main.java Frog.java
  3. java Main

擴展建議

  • 引入圖片素材替換方塊繪製
  • 添加步數計數、最佳記錄本地存儲
  • 實現撤銷功能(命令模式)
  • 增加關卡系統:不同初始佈局、障礙物
  • 使用雙緩衝優化繪製閃爍問題
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved