點擊查看更多
點擊關閉

訪客計數

--- 次訪問

快速統計

15
專案
1
更新
1
活躍專案
3
已完成

本日名言

"在這個世界上,我們於苦難中纏結"
— 伊馮那·塞赫
返回專案列表
已封存 2024年11月 - 2024年12月

數字潛流 - 倉庫番推箱遊戲

使用 Unity 引擎開發的 2D 倉庫番推箱遊戲,結合經典推箱機制與資訊系統概念(CPU 五階段流程)。玩家需收集鑰匙、推動箱子、避開陷阱並與終端互動以解鎖出口。專案採用矩陣碰撞檢測系統、狀態堆疊(undo 機制)、協程場景轉場與打字機對話效果。

Unity C# Game Design School Project

專案簡介

Digital Subcurrent (數位潛流) 是一個倉庫番類型推箱遊戲,透過 Unity 引擎開發的 2D 益智遊戲。遊戲結合了經典倉庫番推箱機制與資訊系統概念(指令擷取 IF、指令解碼 ID、執行 EX、記憶體存取 MEM、寫回 WB),玩家需要收集鑰匙碼(Keycodes)、推動箱子、避開陷阱並與終端機互動來解鎖出口。專案採用 C# 開發,整合動態過場效果、進度條系統、地圖生成器與物件控制邏輯。

核心理念

  • 格子基礎移動系統:玩家與物件對齊格子中心,精準的位置計算
  • 邏輯推理挑戰:結合推箱子與收集要素,需策略性規劃移動路徑
  • 資訊系統教育:關卡設計融入 CPU 五階段概念(IF/ID/EX/MEM/WB)
  • 流暢過場體驗:黑屏淡入淡出、進度條動畫、場景轉換管理

團隊協作

本專案為課程作業協作開發,團隊成員包含:

  • 我(專案負責人):負責核心遊戲邏輯、系統架構設計、物件控制、UI 與場景管理
  • @lin0813you:協助引擎開發與遊戲機制構想
  • @calculusfkyou:協助引擎開發與遊戲機制構想

我的職責

作為專案的主要開發者,我負責:

系統架構設計

  • 設計單例模式遊戲管理器(GameManager Singleton)
  • 實作矩陣基礎地圖系統(objectMatrix + floorMatrix)
  • 設計關卡載入器(LevelLoader)與場景轉換管理
  • 建立狀態堆疊系統(Stack<GameState>)支援撤銷功能

核心遊戲邏輯開發 (約 2,062 行 C#)

  • GameManager.cs (262 行)
    • 矩陣管理:objectMatrix(物件層)、floorMatrix(地板層)
    • 碰撞檢測:PlayerTryMove()、BoxTryMove()
    • 狀態管理:SaveState()、RestoreState()
    • 物件查詢:HasBox()、GetBox()、FindPlayerPosition()
  • PlayerController.cs (223 行)
    • 格子移動系統:TryMove()、MoveTowardsTarget()
    • 輸入處理:HandleMovement()(WASD 控制)
    • 動畫控制:Animator 方向與移動狀態
    • 鑰匙管理:hasKey 布林值追蹤
    • 箱子推動邏輯:呼叫 BoxController.TryMove()
  • LoadMapInfo.cs (177 行)
    • 地圖資料載入:ReadMapInfo()
    • 物件實例化:InstantiateObjects()
    • 矩陣轉換:將文字地圖轉為 int[,] 陣列
    • 位置計算:MatrixToWorld()、WorldToMatrix()
  • DialogueManager.cs (155 行)
    • 對話系統:DisplayNextSentence()
    • 打字機效果:TypeSentence() 協程
    • 對話佇列管理:Queue<string>
  • TransitionManager.cs (126 行)
    • 場景過場:FadeToBlack()、FadeFromBlack()
    • 進度條控制:UpdateProgress()
    • 協程管理:LoadSceneAsync()

物件控制系統

  • BoxController.cs (66 行)
    • 箱子推動:TryMove()
    • 平滑移動動畫:MoveTowardsTarget()
    • 與 GameManager 互動確認移動合法性
  • TerminalController.cs (51 行)
    • 終端機互動:OnTriggerEnter2D()
    • 門解鎖邏輯:檢查玩家是否收集所有鑰匙
  • KeyController.cs
    • 鑰匙拾取:碰撞檢測
    • 銷毀物件:Destroy(gameObject)
  • HoleController.cs (44 行)
    • 陷阱機制:偵測箱子掉入洞中
    • 遊戲失敗觸發

UI 與場景管理

  • LevelLoader.cs (94 行)
    • 關卡載入協程:LoadLevel()
    • 地圖生成器整合
    • 過場動畫觸發
  • StoryManager.cs (83 行)
    • 劇情觸發器管理
    • 對話系統整合
  • SceneManager.cs (51 行)
    • 場景切換:LoadScene()
    • 遊戲重啟:RestartLevel()
  • PauseMenu.cs (64 行)
    • 暫停選單:Time.timeScale 控制
    • 音效開關
    • 返回主選單

美術與動畫整合

  • TextMesh Pro 字型渲染
  • Sprite 動畫控制(玩家四方向動畫)
  • Tile Palette 地圖繪製
  • UI Materials 特效

核心功能

1. 格子移動系統

  • 精準對齊
    • 玩家與物件始終對齊格子中心(gridSize = 1x1)
    • 使用 Vector2.Lerp() 實現平滑移動動畫
    • moveSpeed 控制移動速度
  • 碰撞檢測
    • 矩陣基礎檢測:objectMatrix[y, x]
    • 邊界檢測:IsOutOfBounds()
    • 物件層級:牆壁 (-1)、空地 (0)、玩家 (1)、箱子 (2-9)、門 (-2)

2. 推箱子機制

  • 箱子移動規則
    • 玩家推動箱子至相鄰空格
    • 箱子前方必須為空地或洞
    • 箱子無法推動其他箱子
    • 箱子掉入洞中會消失(觸發 HoleController)
  • 策略挑戰
    • 需規劃箱子移動路徑避免阻擋自己
    • 避免將鑰匙箱推入洞中導致失敗

3. 鑰匙與終端機系統

  • 鑰匙收集
    • 玩家碰觸 KeyController 自動拾取
    • hasKey 布林值記錄持有狀態
    • 可能需要收集多把鑰匙(依關卡設計)
  • 終端機互動
    • 檢查玩家是否擁有所有鑰匙
    • 解鎖門(objectMatrix 中的 -2)
    • 觸發 doorUnlocked = true

4. 地圖生成系統

  • 矩陣架構

    int[,] objectMatrix; // 物件層(玩家、箱子、門)
    int[,] floorMatrix;  // 地板層(普通地板、洞)
    
  • 地圖載入流程

    1. 讀取文字格式地圖檔案(ReadMapInfo)
    2. 解析為整數矩陣
    3. 實例化對應 Prefab(InstantiateObjects)
    4. 設定物件位置(MatrixToWorld 轉換)
  • 動態生成

    • 支援任意大小地圖
    • 自動計算 offset 居中顯示

5. 狀態管理與撤銷系統

  • GameState 快照

    class GameState {
        int[,] objectMatrix;
        int[,] floorMatrix;
        Vector2Int playerPosition;
        bool doorUnlocked;
    }
    
  • 狀態堆疊

    • SaveState():每次移動前儲存
    • RestoreState():撤銷功能(Undo)
    • Stack<GameState> 資料結構

6. 過場與載入系統

  • TransitionManager
    • 黑屏淡入:FadeToBlack()(1 秒協程)
    • 黑屏淡出:FadeFromBlack()
    • CanvasGroup.alpha 控制透明度
  • 進度條系統
    • 圓形進度條(ProgressBarCircle)
    • Image.fillAmount 動態更新
    • 顯示關卡載入進度
  • 非同步載入
    • SceneManager.LoadSceneAsync()
    • allowSceneActivation 控制啟動時機

7. 對話系統

  • DialogueManager
    • 對話佇列:Queue<string> sentences

    • 打字機效果:

      IEnumerator TypeSentence(string sentence) {
          foreach (char letter in sentence.ToCharArray()) {
              dialogueText.text += letter;
              yield return new WaitForSeconds(0.05f);
          }
      }
      
    • 繼續對話:空白鍵或滑鼠點擊

8. 音效系統

  • AudioManager
    • 音效播放:PlaySound()
    • 背景音樂管理
    • 音量控制
  • 音效觸發
    • 玩家移動音效
    • 箱子推動音效
    • 鑰匙拾取音效
    • UI 點擊音效

使用技術

遊戲引擎

  • Unity 6000.0.26f1:遊戲引擎
  • C# 9.0:程式語言
  • Universal Render Pipeline (URP):渲染管線

Unity 核心系統

  • Physics2D:2D 碰撞檢測(Collider2D, Rigidbody2D)
  • Animator:角色動畫控制
  • Coroutines:非同步操作(移動、載入、對話)
  • SceneManagement:場景載入與切換
  • Input System:玩家輸入處理(InputSystem_Actions.inputactions)

UI 框架

  • Unity UI (uGUI):按鈕、文字、圖片、畫布
  • TextMesh Pro:高品質文字渲染
  • CanvasGroup:UI 淡入淡出效果

資料結構與設計模式

  • Singleton Pattern:GameManager、TransitionManager
  • Stack<T>:狀態堆疊(撤銷功能)
  • Queue<T>:對話佇列
  • 二維陣列 (int[,]):地圖矩陣

美術資源

  • Tile Palette:2D 地圖繪製工具
  • Sprite Animation:2D 角色動畫
  • Audio Mixer:音效混音器

開發工具

  • Visual Studio 2022:IDE
  • Git:版本控制
  • GitHub:程式碼託管

專案狀態

當前版本:1.0 (課程作業完成版)

  • 開發時間:2024 年(遊戲設計課程)

功能完成度

  • 已完成
    • 完整的格子移動系統
    • 推箱子核心機制
    • 鑰匙收集與終端機互動
    • 地圖矩陣生成系統
    • 狀態堆疊與撤銷功能
    • 過場動畫與進度條
    • 對話系統(打字機效果)
    • 音效系統
    • 暫停選單
    • 主選單與關卡選擇
    • 劇情觸發器
    • 多個關卡設計
  • 📋 課程結束後可能不再維護
    • 新關卡設計
    • 更多謎題機制
    • 成就系統
    • 關卡編輯器

開發挑戰與收穫

1. 格子對齊系統

挑戰:如何確保玩家與物件始終對齊格子中心?

解決方案

  • 使用 Vector2.Lerp() 平滑移動:

    void MoveTowardsTarget() {
        transform.position = Vector2.Lerp(
            transform.position, 
            targetPosition, 
            moveSpeed * Time.deltaTime
        );
        if (Vector2.Distance(transform.position, targetPosition) < 0.01f) {
            transform.position = targetPosition; // 精準對齊
            isMoving = false;
        }
    }
    
  • 目標位置計算:targetPosition = currentPosition + direction * gridSize

  • 移動完成後重置 isMoving 旗標

收穫

  • 理解 Lerp 平滑插值原理
  • 掌握格子座標與世界座標轉換
  • 學習協程控制移動流程

2. 矩陣基礎碰撞檢測

挑戰:如何用矩陣實現高效的碰撞檢測?

解決方案

  • 設計雙層矩陣架構:

    objectMatrix[y, x]:
      -1: 牆壁
       0: 空地
       1: 玩家
       2-9: 箱子
      -2: 門
    
    floorMatrix[y, x]:
       0: 普通地板
       1: 洞
    
  • 移動前檢測:

    Vector2Int targetPos = playerPos + direction;
    if (IsOutOfBounds(targetPos) || 
        objectMatrix[targetPos.y, targetPos.x] < 0 || 
        floorMatrix[targetPos.y, targetPos.x] == 1) {
        return false; // 無法移動
    }
    

收穫

  • 理解矩陣在遊戲開發中的應用
  • 掌握二維陣列索引技巧
  • 學習分層設計(物件層 + 地板層)

3. 狀態堆疊與撤銷功能

挑戰:如何實現 Undo 功能保存遊戲狀態?

解決方案

  • 使用 Stack<GameState> 堆疊:

    void SaveState() {
        GameState state = new GameState {
            objectMatrix = (int[,])objectMatrix.Clone(),
            floorMatrix = (int[,])floorMatrix.Clone(),
            playerPosition = playerMatrixPosition,
            doorUnlocked = doorUnlocked
        };
        stateStack.Push(state);
    }
    
    void RestoreState() {
        if (stateStack.Count > 0) {
            GameState state = stateStack.Pop();
            objectMatrix = state.objectMatrix;
            // ... 還原其他狀態
        }
    }
    

收穫

  • 理解 Stack LIFO(後進先出)資料結構
  • 掌握深拷貝(Clone)與淺拷貝差異
  • 學習快照模式(Memento Pattern)

4. 協程非同步載入

挑戰:如何實現流暢的場景載入體驗?

解決方案

  • 協程 + 非同步載入:

    IEnumerator LoadSceneAsync(string sceneName) {
        yield return StartCoroutine(FadeToBlack());
        
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
        asyncLoad.allowSceneActivation = false;
        
        while (!asyncLoad.isDone) {
            float progress = Mathf.Clamp01(asyncLoad.progress / 0.9f);
            UpdateProgress(progress);
            
            if (asyncLoad.progress >= 0.9f) {
                asyncLoad.allowSceneActivation = true;
            }
            yield return null;
        }
        
        yield return StartCoroutine(FadeFromBlack());
    }
    

收穫

  • 理解協程(Coroutine)運作原理
  • 掌握 AsyncOperation 非同步載入
  • 學習 yield return 控制流程

5. 打字機對話效果

挑戰:如何實現逐字顯示的對話效果?

解決方案

IEnumerator TypeSentence(string sentence) {
    dialogueText.text = "";
    foreach (char letter in sentence.ToCharArray()) {
        dialogueText.text += letter;
        yield return new WaitForSeconds(0.05f); // 每個字延遲 0.05 秒
    }
}

收穫

  • 掌握 foreach 遍歷字元陣列
  • 理解 WaitForSeconds() 協程延遲
  • 學習 UI 文字動態更新

6. Singleton 模式應用

挑戰:如何確保 GameManager 只有一個實例?

解決方案

public class GameManager : MonoBehaviour {
    public static GameManager Instance;
    
    void Awake() {
        if (Instance == null) {
            Instance = this;
        } else {
            Destroy(gameObject);
            return;
        }
        // DontDestroyOnLoad(gameObject); // 跨場景持續
    }
}

收穫

  • 理解單例模式(Singleton Pattern)
  • 掌握 Awake() 生命週期
  • 學習 DontDestroyOnLoad() 用法

專案亮點

技術創新

  • ✅ 矩陣基礎碰撞檢測系統(高效能)
  • ✅ 狀態堆疊撤銷機制(Stack<GameState>)
  • ✅ 協程驅動的流暢過場動畫
  • ✅ 雙層矩陣設計(物件層 + 地板層)

遊戲設計

  • ✅ 結合推箱子與收集要素
  • ✅ 資訊系統概念融入關卡設計(IF/ID/EX/MEM/WB)
  • ✅ 劇情對話系統(打字機效果)
  • ✅ 直覺的 WASD 控制

工程實務

  • ✅ Singleton Pattern 管理全域狀態
  • ✅ 協程(Coroutine)處理非同步操作
  • ✅ 模組化腳本設計(29 個 C# 腳本)
  • ✅ 清晰的程式碼架構

學習成果

  • ✅ 深入理解 Unity 2D 遊戲開發
  • ✅ 掌握格子移動系統實作
  • ✅ 學習狀態管理與資料結構應用
  • ✅ 實作完整的解謎遊戲

應用程式執行截圖

Main Menu
Main Menu

Cutscene
Cutscene

Gameplay
Gameplay