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

手機網遊客戶端界面開發:狀態機模式與分層渲染架構

     編輯:Java 相關知識

手機網遊客戶端界面開發面臨屏幕適配、幀率優化、狀態管理等挑戰。本文以《瘋狂的炸彈》為例,講解狀態機模式管理多界面、分層渲染優化繪製、輸入事件分發機制。

狀態機模式管理界面流轉


public class GameCanvas extends Canvas implements Runnable {
    // 界面狀態枚舉
    private static final int STATE_LOADING   = 0;
    private static final int STATE_LOGIN     = 1;
    private static final int STATE_WAITING   = 2;  // 匹配等待
    private static final int STATE_PLAYING   = 3;  // 遊戲主界面
    private static final int STATE_GAMEOVER  = 4;
    private static final int STATE_SETTINGS  = 5;
    
    private int currentState = STATE_LOADING;
    private int nextState = -1; // 待切換狀態
    
    // 狀態切換(線程安全)
    public synchronized void changeState(int newState) {
        this.nextState = newState;
    }
    
    // 主循環中處理狀態切換
    public void run() {
        while (running) {
            // 處理狀態轉換
            if (nextState != -1) {
                handleStateTransition(currentState, nextState);
                currentState = nextState;
                nextState = -1;
            }
            
            // 當前狀態邏輯更新
            updateCurrentState();
            
            // 觸發重繪
            repaint();
            
            // 幀率控制
            sleepFrame(1000 / 20); // 20 FPS
        }
    }
    
    private void handleStateTransition(int from, int to) {
        // 退出舊狀態清理
        switch (from) {
            case STATE_PLAYING: stopGameLoop(); break;
            case STATE_WAITING: stopWaitAnimation(); break;
        }
        // 進入新狀態初始化
        switch (to) {
            case STATE_PLAYING: initGameResources(); startGameLoop(); break;
            case STATE_WAITING: startWaitAnimation(); break;
            case STATE_GAMEOVER: showResult(); break;
        }
    }
}

分層渲染架構


protected void paint(Graphics g) {
    // 1. 背景層 (靜態,可緩存)
    drawBackground(g);
    
    // 2. 地圖靜態層 (障礙物/裝飾)
    drawMapStaticLayers(g);
    
    // 3. 動態實體層 (玩家/炸彈/道具)
    drawEntities(g);
    
    // 4. 特效層 (爆炸/粒子)
    drawEffects(g);
    
    // 5. UI覆蓋層 (分數/血量/按鈕)
    drawUIOverlay(g);
    
    // 6. 狀態專屬UI (等待動畫/結算面板)
    drawStateOverlay(g);
}

private void drawMapStaticLayers(Graphics g) {
    // 只繪製可見區域
    int startRow = cameraY / TILE_H;
    int endRow = Math.min(MAP_ROWS, (cameraY + screenH) / TILE_H + 1);
    
    for (int r = startRow; r < endRow; r++) {
        for (int c = 0; c < MAP_COLS; c++) {
            int x = c * TILE_W - cameraX;
            int y = r * TILE_H - cameraY;
            
            // 分層繪製,後繪製蓋前
            drawTile(g, map.groundLayer[r][c], x, y);
            drawTile(g, map.objectLayer[r][c], x, y);
            drawTile(g, map.decorLayer[r][c], x, y);
        }
    }
}

private void drawEntities(Graphics g) {
    // 玩家按Y排序實現偽3D遮擋
    List sorted = new ArrayList<>(entities);
    sorted.sort(Comparator.comparingInt(e -> e.getY()));
    
    for (Entity e : sorted) {
        if (e.isVisible(cameraX, cameraY, screenW, screenH)) {
            e.draw(g, cameraX, cameraY);
        }
    }
}

資源管理與圖片緩存


public class ResourceManager {
    private static final Map imageCache = new HashMap<>();
    private static final Map spriteCache = new HashMap<>();
    
    public static Image getImage(String path) {
        return imageCache.computeIfAbsent(path, p -> {
            try {
                return Image.createImage(p);
            } catch (IOException e) {
                log.error("加載圖片失敗: " + p, e);
                return Image.createImage(1, 1); // 佔位圖
            }
        });
    }
    
    public static Sprite getSprite(String imgPath, int frameW, int frameH) {
        return spriteCache.computeIfAbsent(imgPath + "#" + frameW + "x" + frameH, 
            k -> new Sprite(getImage(imgPath), frameW, frameH));
    }
    
    // 遊戲結束時釋放
    public static void dispose() {
        imageCache.clear();
        spriteCache.clear();
    }
}

輸入事件處理


protected void keyPressed(int keyCode) {
    // 通用鍵處理
    if (keyCode == KEY_SOFT_LEFT) { // 左軟鍵
        if (currentState == STATE_PLAYING) pauseGame();
        else if (currentState == STATE_WAITING) cancelMatch();
        else exitGame();
        return;
    }
    
    // 狀態專屬處理
    switch (currentState) {
        case STATE_PLAYING:
            handleGameInput(keyCode);
            break;
        case STATE_LOGIN:
            handleLoginInput(keyCode);
            break;
        case STATE_SETTINGS:
            handleSettingsInput(keyCode);
            break;
    }
}

private void handleGameInput(int keyCode) {
    int action = getGameAction(keyCode);
    switch (action) {
        case Canvas.UP:
            if (playerY > TOP_MARGIN) 
                sendMove(DIR_UP);
            break;
        case Canvas.DOWN:
            if (playerY + SPRITE_H < screenH - BOTTOM_MARGIN)
                sendMove(DIR_DOWN);
            break;
        case Canvas.LEFT:
            if (playerX > 0) sendMove(DIR_LEFT);
            break;
        case Canvas.RIGHT:
            if (playerX + SPRITE_W < screenW) sendMove(DIR_RIGHT);
            break;
        case Canvas.FIRE:
            sendLayBomb();
            break;
    }
}

性能優化清單

優化點方案收益
重複繪製臟矩形/可見區域剪裁減少50%+繪製調用
圖片創建資源管理器單例緩存避免GC抖動/OOM
幀率不穩固定時間步長+插值渲染流暢度提升
內存佔用圖片壓縮/PNG索引色/釋放不用資源適配低端機
按鍵響應客戶端預判+服務端校驗降低延遲感知

適配策略


// 屏幕適配:以基準分辨率(240x320)縮放
float scaleX = screenWidth / 240f;
float scaleY = screenHeight / 320f;
float scale = Math.min(scaleX, scaleY); // 保持比例

// 繪製時應用縮放
g.translate((screenWidth - 240*scale)/2, (screenHeight - 320*scale)/2);
g.scale(scale, scale);
// ... 繪製基準座標內容 ...
g.scale(1/scale, 1/scale);
g.translate(-(screenWidth - 240*scale)/2, -(screenHeight - 320*scale)/2);

狀態機模式讓複雜界面流轉變得清晰可控,分層渲染配合資源緩存是實現流暢遊戲體驗的基石。現代開發建議使用LibGDX/Unity等引擎替代手工Canvas開發。

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