數字潛流 - 倉庫番推箱遊戲
使用 Unity 引擎開發的 2D 倉庫番推箱遊戲,結合經典推箱機制與資訊系統概念(CPU 五階段流程)。玩家需收集鑰匙、推動箱子、避開陷阱並與終端互動以解鎖出口。專案採用矩陣碰撞檢測系統、狀態堆疊(undo 機制)、協程場景轉場與打字機對話效果。
專案簡介
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; // 地板層(普通地板、洞)
地圖載入流程:
- 讀取文字格式地圖檔案(ReadMapInfo)
- 解析為整數矩陣
- 實例化對應 Prefab(InstantiateObjects)
- 設定物件位置(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 遊戲開發
- ✅ 掌握格子移動系統實作
- ✅ 學習狀態管理與資料結構應用
- ✅ 實作完整的解謎遊戲
應用程式執行截圖