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

DOJA 平台貪食蛇遊戲完整源碼解析與架構設計

     編輯:Java 相關知識

項目背景

DOJA (DoCoMo Java) 是 NTT DoCoMo 定制的 J2ME 配置,針對日本功能機優化。本項目實現經典貪食蛇遊戲,採用標準 MIDP 風格架構:MIDlet 入口 → Canvas 繪製/事件 → 遊戲邏輯分離,包含五大核心類。

核心類架構圖


GreedSnake (MIDlet/主控制器)
    │
    ├─ Grid (Canvas/繪製視圖)
    │      │
    │      ├─ Snake (遊戲實體:蛇)
    │      │      └─ Node[] (身體節點)
    │      │
    │      └─ Food (遊戲實體:食物)
    │
    └─ Node (數據結構:座標點)

關鍵類詳細實現

1. GreedSnake - 遊戲主控制器


public class GreedSnake extends IApplication implements Runnable {
    // 遊戲常量
    public static final int GRID_WIDTH = 20;
    public static final int GRID_HEIGHT = 24;
    public static final int NODE_SIZE = 10; // 像素大小
    
    // 組件引用
    private Grid grid;
    private Food food;
    private Snake snake = new Snake();
    
    // 遊戲狀態
    private boolean running;   // true=遊戲結束
    private int score, level;
    private int count, countMove, timeInterval = 200; // 速度控制
    
    // 生命週期
    public void start() {
        reset();
        grid = new Grid(GRID_WIDTH, GRID_HEIGHT, this);
        Display.setCurrent(grid);
        new Thread(this).start();
    }
    
    private void reset() { /* 重置所有狀態變量 */ }
    
    // 遊戲主循環
    public void run() {
        food = grid.addFood();
        grid.addSnake(snake);
        grid.repaint();
        
        for (;;) {
            if (food == null) { food = grid.addFood(); continue; }
            try { Thread.sleep(timeInterval); } catch (Exception e) { break; }
            
            if (!running) {
                snake.move();
                Node head = snake.getHead();
                
                // 碰撞檢測: 牆壁 或 自身
                if (grid.overlapsWith(head) || snake.ptInSnake(head)) {
                    running = true; continue;
                }
                
                // 吃食物
                if (head.x == food.getX() && head.y == food.getY()) {
                    int scoreGet = (10000 - 200 * countMove) / timeInterval;
                    score += Math.max(scoreGet, 0);
                    countMove = 0;
                    snake.eatFood(food);
                    grid.reduceFood(food);
                    food = null;
                    if (++count % 5 == 0) level++; // 每5個食物升級
                } else {
                    countMove++;
                }
                grid.repaint();
            } else {
                grid.setGameOver();
                break;
            }
        }
        grid.repaint(); // 最後一幀顯示 Game Over
    }
    
    // 按鍵處理
    public void keyPressed(int key) {
        switch (key) {
            case Display.KEY_SELECT: grid.setGameOver(); break;
            case Display.KEY_LEFT:  snake.setDirection(Snake.LEFT);  break;
            case Display.KEY_RIGHT: snake.setDirection(Snake.RIGHT); break;
            case Display.KEY_UP:    snake.setDirection(Snake.UP);    break;
            case Display.KEY_DOWN:  snake.setDirection(Snake.DOWN);  break;
        }
    }
    // Getters...
}

2. Grid - 遊戲畫布與視圖


public class Grid extends Canvas {
    private int width, height;
    private GreedSnake listener;
    private Snake snake;
    private Food food;
    private boolean blDisplay, gameOver;
    private Font scoreFont, gameOverFont;
    
    public Grid(int w, int h, GreedSnake l) {
        this.width = w; this.height = h; this.listener = l; reset();
        setSoftLabel(Frame.SOFT_KEY_1, "New");
        setSoftLabel(Frame.SOFT_KEY_2, "Quit");
        scoreFont = Font.getFont(Font.FACE_MONOSPACE | Font.SIZE_SMALL);
        gameOverFont = Font.getFont(Font.FACE_PROPORTIONAL | Font.SIZE_LARGE | Font.STYLE_BOLD);
    }
    
    // 食物生成: 避開蛇身
    public Food addFood() {
        blDisplay = false;
        int gs = GreedSnake.GRID_WIDTH;
        if (snake == null) return this.food = Food.createRandomFood(gs, gs);
        for (;;) {
            this.food = Food.createRandomFood(gs, gs);
            if (!snake.ptInSnake(new Node(food.getX(), food.getY()))) break;
        }
        return this.food;
    }
    
    public void reduceFood(Food f) { blDisplay = true; } // 暫停顯示
    
    // 繪製核心
    public synchronized void paint(Graphics g) {
        g.lock();
        g.setColor(Graphics.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(Graphics.WHITE);
        g.drawLine(0, 0, 0, getHeight()); // 左邊框
        g.drawLine(201, 0, 201, getHeight()); // 右邊框
        g.drawLine(0, getHeight(), 201, getHeight()); // 底邊框
        
        if (food != null && !blDisplay) food.paint(g);
        if (snake != null) snake.paint(g);
        
        // 側邊欄分數/等級
        g.setColor(Graphics.SILVER);
        g.setFont(scoreFont);
        g.drawString("LEVEL", 203, 40);
        g.drawString(String.valueOf(listener.getLevel()), 220, 70);
        g.drawString("SCORE", 202, 100);
        g.drawString(format(listener.getScore()), 208, 130);
        
        if (gameOver) {
            g.setColor(0x00ffff);
            g.setFont(gameOverFont);
            g.drawString("GAME", 70, 110);
            g.drawString("OVER", 70, 140);
        }
        g.unlock(true);
    }
    
    private String format(int n) { // 4位補零
        String s = String.valueOf(n);
        return "0000".substring(0, 4 - s.length()) + s;
    }
    
    public void processEvent(int type, int param) {
        if (type == Display.KEY_PRESSED_EVENT) listener.keyPressed(param);
    }
}

3. Snake - 蛇實體邏輯


public class Snake {
    public static final int UP=1, DOWN=3, LEFT=2, RIGHT=4;
    private int direction;
    private Vector snakeData = new Vector(); // Node 序列
    private static Random rnd = new Random();
    
    public Snake() { // 初始 5 節,水平向左
        for (int i=4; i>=0; i--) snakeData.addElement(new Node(i, 0));
        setDirection(DOWN);
    }
    
    public void setDirection(int dir) {
        if (direction % 2 != dir % 2) direction = dir; // 禁止 180° 轉向
    }
    
    public Node getHead() { return (Node) snakeData.elementAt(0); }
    
    public Node getNextHead() { // 預測下一幀頭部位置
        Node n = new Node(getHead().x, getHead().y);
        switch (direction) {
            case UP:    n.y--; break;
            case DOWN:  n.y++; break;
            case LEFT:  n.x--; break;
            case RIGHT: n.x++; break;
        }
        return n;
    }
    
    public void move() { // 整體位移
        Node newHead = getNextHead();
        for (int i = snakeData.size()-1; i > 0; i--) {
            Node cur = (Node) snakeData.elementAt(i);
            Node prev = (Node) snakeData.elementAt(i-1);
            cur.x = prev.x; cur.y = prev.y;
        }
        Node head = (Node) snakeData.elementAt(0);
        head.x = newHead.x; head.y = newHead.y;
    }
    
    public void addNode() { // 吃食物增長
        Node tail = (Node) snakeData.lastElement();
        snakeData.addElement(new Node(tail.x, tail.y));
    }
    
    public void eatFood(Food f) { addNode(); }
    
    public boolean ptInSnake(Node n) { // 自身碰撞檢測 (排除頭部)
        for (int i=1; i

4. Food - 食物實體


public class Food {
    private int x, y;
    private static Random rnd = new Random();
    
    private Food(int x, int y) { this.x = x; this.y = y; }
    
    public static Food createRandomFood(int w, int h) {
        return new Food(Math.abs(rnd.nextInt() % w), Math.abs(rnd.nextInt() % h));
    }
    
    public int getX() { return x; }
    public int getY() { return y; }
    
    public synchronized void paint(Graphics g) {
        int gn = GreedSnake.NODE_SIZE;
        g.setColor(0x0000ff);
        g.fillRect(x * gn, y * gn, gn, gn);
    }
}

5. Node - 輕量座標類


public class Node { public int x, y; public Node(int x, int y) { this.x=x; this.y=y; } }

架構亮點與 DOJA 特性

特性實現方式
繼承體系IApplication (DOJA 入口) + Runnable (遊戲循環線程)
畫布模型Canvas 繼承,paint(Graphics) 同步繪製,processEvent 事件分發
軟鍵映射setSoftLabel(Frame.SOFT_KEY_1/2, ...) 適配日系手機雙軟鍵
雙緩衝Graphics.lock()/unlock(true) 手動控制,減少閃爍
隨機數java.util.Random 標準 API,DOJA 完整支持
向量容器Vector 存儲蛇身節點,J2ME 標準集合

擴展建議

  1. 狀態機重構:引入 GameState 枚舉 (READY/RUNNING/PAUSED/OVER) 替換布爾標誌
  2. 持久化最高分:使用 RecordStore 存儲歷史最高分
  3. 關卡設計:增加障礙物、變速道具、傳送門等地圖元素
  4. 音效集成:Manager.createPlayer() 播放吃食/撞牆/升級音效
  5. 多設備適配:動態獲取 Display.getWidth()/getHeight() 計算網格大小

總結

這個 DOJA 貪食蛇實現展示了 J2ME 遊戲開發的標準模式:主控制器調度、Canvas 視圖渲染、實體類封裝邏輯、輕量數據結構。代碼結構清晰、註釋完整、邊界處理得當 (禁止反向、食物避開蛇身、邊界檢測),是學習 DOJA/J2ME 遊戲開發的優秀範例。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved