本專案採用經典的 MVC 分層思想,包含兩個核心類別:
JFrame,負責遊戲視窗建構、鍵盤事件監聽、畫面重繪、分數顯示與遊戲循環啟動。Runnable,封裝蛇身移動邏輯、食物生成、碰撞檢測、計分規則、速度控制與暫停/繼續狀態管理。public GreedSnake() {
mainFrame = new JFrame("GreedSnake");
Container cp = mainFrame.getContentPane();
// 頂部分數標籤
labelScore = new JLabel("Score:");
cp.add(labelScore, BorderLayout.NORTH);
// 中央繪圖畫布
paintCanvas = new Canvas();
paintCanvas.setSize(canvasWidth+1, canvasHeight+1);
paintCanvas.addKeyListener(this);
cp.add(paintCanvas, BorderLayout.CENTER);
// 底部操作提示面板
JPanel panelButtom = new JPanel(new BorderLayout());
// ... 新增三行 JLabel 提示鍵位 ...
cp.add(panelButtom, BorderLayout.SOUTH);
mainFrame.addKeyListener(this);
mainFrame.pack();
mainFrame.setResizable(false);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setVisible(true);
begin(); // 啟動遊戲邏輯執行緒
}
實作 KeyListener 介面,在 keyPressed() 中根據按鍵代碼分派動作:
snakeModel.changeDirection()註:方向改變邏輯在 SnakeModel.changeDirection() 中透過奇偶性判斷防止 180 度反向移動導致立即自殺。
void repaint() {
Graphics g = paintCanvas.getGraphics();
// 1. 清空背景
g.setColor(Color.WHITE);
g.fillRect(0, 0, canvasWidth, canvasHeight);
// 2. 繪製蛇身(黑色)
g.setColor(Color.BLACK);
for (Node n : snakeModel.nodeArray) {
drawNode(g, n);
}
// 3. 繪製食物(紅色)
g.setColor(Color.RED);
drawNode(g, snakeModel.food);
updateScore();
}
private void drawNode(Graphics g, Node n) {
g.fillRect(n.x * nodeWidth, n.y * nodeHeight, nodeWidth-1, nodeHeight-1);
}
採用主動繪圖模式,直接取得 Canvas 的 Graphics 進行即時渲染,而非覆寫 paint() 方法。
boolean[][] matrix:二維布林陣列標記格子佔用狀態,實現 O(1) 碰撞檢測。LinkedList<Node> nodeArray:雙向鏈表儲存蛇身節點,頭尾操作均為 O(1),完美契合蛇身「頭部新增、尾部移除」的移動特性。Node food:單一食物物件,座標即為格子索引。public SnakeModel(GreedSnake gs, int maxX, int maxY) {
// 矩陣初始化
matrix = new boolean[maxX][maxY];
for (boolean[] row : matrix) Arrays.fill(row, false);
// 蛇身初始長度:寬度>20則為10,否則為寬度一半
int initLen = maxX > 20 ? 10 : maxX / 2;
for (int i = 0; i < initLen; i++) {
int x = maxX/2 + i, y = maxY/2;
nodeArray.addLast(new Node(x, y));
matrix[x][y] = true;
}
food = createFood();
matrix[food.x][food.y] = true;
}
public boolean moveOn() {
Node head = nodeArray.getFirst();
int x = head.x, y = head.y;
// 根據方向計算新頭部座標
switch(direction) {
case UP: y--; break;
case DOWN: y++; break;
case LEFT: x--; break;
case RIGHT: x++; break;
}
// 邊界檢查
if (x < 0 || x >= maxX || y < 0 || y >= maxY) return false;
// 碰撞檢查
if (matrix[x][y]) {
// 撞到食物
if (x == food.x && y == food.y) {
nodeArray.addFirst(food); // 蛇身增長,不移除尾部
int scoreGet = (10000 - 200 * countMove) / timeInterval;
score += scoreGet > 0 ? scoreGet : 10;
countMove = 0;
food = createFood();
matrix[food.x][food.y] = true;
return true;
}
// 撞到牆壁或自己
return false;
}
// 正常移動:頭部新增、尾部移除
nodeArray.addFirst(new Node(x, y));
matrix[x][y] = true;
Node tail = nodeArray.removeLast();
matrix[tail.x][tail.y] = false;
countMove++;
return true;
}
(10000 - 200 * 移動步數) / 當前間隔時間,鼓勵玩家快速直線吃食。timeInterval 為移動間隔毫秒數,預設 200ms。加速/減速以 0.75 倍率調整(最小 20ms)。SnakeModel.run() 方法(原始碼截斷前)會在 while(running) 循環中周期性呼叫 moveOn(),根據回傳值判斷遊戲結束或繼續,並透過 Thread.sleep(timeInterval) 控制幀率。暫停狀態下進入 wait() 釋放 CPU。
LinkedList + boolean[][] 實現高效移動與碰撞檢測。running、paused、direction 狀態管理完備。Swing Timer 或獨立渲染執行緒替代 Canvas.getGraphics() 主動繪圖,避免閃爍與執行緒安全問題。BufferStrategy)提升渲染流暢度。Key Bindings API,解決焦點導致的按鍵無響應問題。這個貪食蛇實作雖然代碼簡潔,卻完整展示了 Java 遊戲開發的核心要素:幀循環、狀態管理、輸入處理、碰撞檢測、渲染管線。其模型層設計尤為優秀,資料結構選型教科書級別,是學習 Java 遊戲程式設計與 MVC 架構的極佳範例。建議讀者嘗試在保持模型不變的前提下,將視圖層遷移至 JavaFX 或 LibGDX 等現代框架,體驗架構解耦帶來的擴展性紅利。