# Unity 2048遊戲進階版:雙模式與圖鑑實作 > 本教學改編自 Brackeys 的 Unity 2048 教學影片,並進一步加入「數字/馬力歐雙模式切換」與「圖鑑系統」等功能,目的是讓初學者能從原始教學出發,進一步嘗試 UI、Prefab 與資料儲存等延伸實作。 --- ## 教學目錄 1. [導言與專案展示](#導言與專案展示) 2. [教學影片與原始專案介紹](#教學影片與原始專案介紹) 3. [遊戲主體架構概述](#遊戲主體架構概述) 4. [雙模式切換功能設計與實作](#雙模式切換功能設計與實作) 5. [Menu 系統設計與互動問題排除](#menu-系統設計與互動問題排除) 6. [圖鑑 Gallery 功能設計與解鎖邏輯](#圖鑑-gallery-功能設計與解鎖邏輯) 7. [WebGL 部署與 GitHub Pages 發佈流程](#webgl-部署與-github-pages-發佈流程) 8. [常見錯誤彙整與排錯策略分享](#常見錯誤彙整與排錯策略分享) 9. [心得回顧與學習建議](#心得回顧與學習建議) --- ## 導言與專案展示 這份教學是從 Brackeys 所製作的 2048 Unity 教學為出發點,透過不斷 debug 與創意實作,延伸出更多功能。整個製作過程中,我實際碰到許多問題(如圖片對齊錯誤、Menu 無法觸發、WebGL 部署失敗等),也都在這份教學中逐步紀錄解決過程,希望能幫助未來有同樣需求的你快速上手。 本教學特別適合以下情境的開發者: - 想從 Unity 初學進階 - 想學習如何動態切換顯示資料 - 想學會如何實作圖鑑、解鎖系統 - 想學習 WebGL 與 GitHub Pages 發佈技巧 --- ### - 教學目標 透過本專案,你將學會以下幾項技能: - ✅ 如何擴充原有 2048 專案 - ✅ 建立「數字/馬力歐」雙顯示模式的切換系統 - ✅ 動態生成圖鑑卡片並判斷解鎖狀態 - ✅ 設計可中途開啟的 Menu 面板(支援返回與跳轉) - ✅ 將 Unity 遊戲部署成 WebGL 並上傳到 GitHub Pages - ✅ 常見錯誤的排除與 UI 調整技巧 --- ### - 最終成果展示 以下是專案完成後的主要畫面展示,包含遊戲主介面與圖鑑介面,皆可支援雙模式與解鎖判定。 #### 🟦 遊戲主畫面(Mario 模式) <img src="https://hackmd.io/_uploads/BkmaJzNXlx.png" width="60%"> #### 🟨 圖鑑 Gallery 畫面(含解鎖與未解鎖邏輯) <img src="https://hackmd.io/_uploads/SyEZeM4Xeg.png" width="60%"> --- ## 開發環境說明 | 項目 | 說明 | |--------------|-------------------------------------------------------| | 引擎版本 | Unity 2021.3 | | 撰寫語言 | C# | | 圖片載入 | Resources 資料夾動態載入 | | UI 系統 | Canvas + Panel + ScrollView + Grid Layout | | 發佈方式 | WebGL ➜ GitHub Pages | | 儲存方式 | PlayerPrefs(Editor 與 WebGL 使用 localStorage) | --- ## 教學影片與原始專案介紹 本專案是改編自知名 YouTube 頻道 Brackeys 的 2048 Unity 教學影片。這支影片清楚地建立了 2048 遊戲的基礎玩法與邏輯,包括格子合併、分數計算與隨機產生 Tile,是非常推薦的起點資源。 我自己在實作的過程中,先完全照著 Brackeys 的影片一步步建構基本遊戲,並確保功能正常後,才開始加入「數字/馬力歐雙模式切換」與「圖鑑解鎖」等進階功能。這樣做的好處是能更清楚地分辨每一段程式的功能,後續擴充時也更有信心。 --- ### - 教學影片(原始專案來源) 強烈建議初學者完整觀看本影片前半段,將會對遊戲邏輯、Tile 合併規則與 UI 設計有清晰理解。 - 📺 YouTube 連結: [Brackeys - Making 2048 in Unity](https://www.youtube.com/watch?v=4NFZwPhqeRs) <iframe width="560" height="315" src="https://www.youtube.com/embed/4NFZwPhqeRs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> --- ## 原始專案功能總覽 以下是 Brackeys 教學中所完成的 2048 遊戲基本功能摘要: | 功能項目 | 功能說明 | |--------------------|--------------------------------------------------------------------------| | Tile 合併邏輯 | 根據玩家滑動方向合併相同數字的 Tile,並產生新數字 | | Tile 動畫 | 每次合併或產生新格子時有彈跳效果增加回饋感 | | 分數系統 | 每合併一次,根據產生數值累加目前分數 | | 隨機生成 Tile | 每次有效移動後隨機生成一個新的 Tile(2 或 4) | | 遊戲結束偵測 | 當沒有空格且無法合併時,顯示 Game Over 畫面 | 這些功能對於之後擴充 UI 與自定模式是重要的基礎。接下來,我們將基於這些功能,加入更豐富的互動與外觀設計。 --- ### - 延伸開發動機 以下是我在實作延伸功能(雙模式與圖鑑)時的幾個動機與考量: - 想讓畫面有更多視覺變化(馬力歐圖片更吸睛) - 希望有成就系統(讓玩家解鎖圖鑑角色) - 設計更直覺的 Menu 操作(隨時切換、重玩、查看圖鑑) --- ## 遊戲主體架構概述(Canvas 與程式邏輯總覽) 在本章中,我們將針對整體遊戲的結構進行拆解,從畫面佈局(UI Canvas)到程式腳本邏輯,建立對整體架構的清楚認知,這有助於你日後擴充功能或 Debug 錯誤。 --- ### - 畫面結構:Canvas 組織 遊戲畫面由一個主 Canvas 組成,底下包含數個 Panel 元件,分別負責不同的 UI 區塊: ```plaintext Canvas ├── GamePanel(遊戲主畫面) ├── MenuPanel(選單面板) │ ├── TitleText(標題) │ ├── NewGameButton(新遊戲) │ ├── GalleryButton(圖鑑) │ └── BackButton(返回遊戲) ├── GalleryPanel(圖鑑頁面) │ ├── ScrollView(滑動區域) │ │ └── Content(Grid Layout,自動生成卡片) │ └── BackButton(返回遊戲) ``` --- ### - 核心腳本簡介 | 腳本名稱 | 功能說明 | |------------------|--------------------------------------------------------------------------| | `GameManager.cs` | 控制遊戲流程、儲存紀錄、控制 Tile 合併與生成 | | `Tile.cs` | 控制單一 Tile 的外觀與數值(會根據模式切換數字或圖片) | | `GameModeManager.cs` | 控制數字模式與馬力歐模式切換邏輯 | | `GalleryManager.cs` | 控制圖鑑卡片生成與解鎖狀態判斷 | | `PlayerPrefsHelper.cs`| 封裝 `PlayerPrefs` 儲存與讀取最高分與最高 Tile 方法 | --- ### - 畫面切換邏輯簡介 - 使用 `MenuPanel.SetActive(true/false)` 和 `GalleryPanel.SetActive(true/false)` 控制面板顯示。 - 使用 `GameManager.ToggleMenu()` 等方法搭配按鈕事件,切換不同面板與模式。 > ✅ 小技巧:每個 Panel 的 Anchor 設為 **Middle Center**,可保證在各種螢幕尺寸下都維持置中顯示。 --- ### - UI 製作常見注意事項 - **ScrollView 設定** - `Content` 使用 **Grid Layout Group** 控制圖鑑卡片排列 - `Viewport` 加上 **Mask** 避免內容溢出 - **Responsive 設定** - 各 UI 元件設為百分比寬高 + Center Anchors,避免拉伸錯位 - **Button 設定** - 加上 `OnClick()` 對應事件,例如 `GameManager.StartNewGame()`、`GameManager.OpenGallery()` 等 --- ## 雙模式切換功能設計與實作 2048 是以數字為主體的遊戲,不過這次我加入了另一種「馬力歐變身圖鑑模式」,讓遊戲更有趣味性與視覺變化。這一章會介紹: - 如何建立雙模式架構 - 如何動態切換顯示內容(數字 or 圖片) - 如何設計切換按鈕與資料同步更新邏輯 --- ### - 功能設計目標 | 模式名稱 | 顯示內容 | 實作方式 | |----------|------------------|--------------------------------------| | 數字模式 | 顯示 2, 4, 8... 等經典數字 | Tile 文字元件顯示數值 | | 馬力歐模式 | 顯示對應的圖片(如蘑菇、火球、星星等) | 使用 SpriteRenderer 載入對應圖片 | --- ### - 關鍵控制器:GameModeManager.cs 建立一個 GameMode 控制器,作為模式切換的管理者: ```csharp public enum GameMode { Number, Mario } public static class GameModeManager { public static GameMode currentMode = GameMode.Number; public static void ToggleMode() { currentMode = currentMode == GameMode.Number ? GameMode.Mario : GameMode.Number; } } ```` > 🎯 `GameModeManager.currentMode` 是所有 Tile 判斷顯示邏輯的核心依據 --- ### - Tile 顯示邏輯調整:Tile.cs 根據目前模式,決定每個格子是要顯示數字還是圖片: ```csharp public void UpdateTileVisual() { if (GameModeManager.currentMode == GameMode.Number) { numberText.text = value.ToString(); image.sprite = null; } else { numberText.text = ""; image.sprite = Resources.Load<Sprite>("MarioTiles/" + value); } } ``` * `value` 是 Tile 的數值(如 2、4、8) * 資料夾「Resources/MarioTiles」中要放置與數字對應的圖片(例如 `2.png`, `4.png`...) * 每次合併後或新建 Tile 時都呼叫 `UpdateTileVisual()` 以刷新顯示 --- ### - 切換按鈕與更新流程 新增一顆模式切換按鈕,掛上以下邏輯: ```csharp public void ToggleGameMode() { GameModeManager.ToggleMode(); foreach (Tile tile in allTiles) { tile.UpdateTileVisual(); } } ``` 這樣就能在切換模式後,所有現有的 Tile 都能即時更新顯示內容! 實際切換動畫示意: <img src="https://hackmd.io/_uploads/S11iyNNmge.gif" width="80%"> --- ### - 實作常見錯誤與排除方式 | 問題類型 | 錯誤狀況 | 解法說明 | | ------ | -------------------------------- | ---------------------------------------- | | 圖片沒顯示 | `Resources.Load<Sprite>` 回傳 null | 檢查檔名是否為純數字(不能有空格)、放在 Resources 下 | | 切換後無變化 | 按了按鈕沒效果 | 忘記呼叫 `UpdateTileVisual()` 或 Button 沒連接事件 | | 舊數字未清除 | 圖片上還有殘留文字 | 在馬力歐模式中 `numberText.text = "";` 清空文字 | ## Menu 系統設計與互動問題排除 Menu 系統是整個遊戲的導航中心,提供玩家操作的主要入口。本章我們會介紹如何設計一個: - 支援隨時呼叫的 Menu 面板 - 能切換模式、開新局與進入圖鑑 - 自適應畫面大小的 UI 元件 - 解決互動沒反應、UI 未對齊等常見問題 --- ### - 功能目標 | 按鈕功能 | 說明 | |----------------|----------------------------------------| | `New Game` | 重新開始遊戲,清除目前場上所有 Tile | | `Gallery` | 切換到圖鑑頁面 | | `Back` | 關閉 Menu 回到遊戲畫面 | --- ### - Menu UI 架構設計 請在 Canvas 下建立一個 `MenuPanel`,其中包含: ```plaintext MenuPanel (Panel) ├── TitleText (Text) ├── NewGameButton (Button) ├── GalleryButton (Button) └── BackButton (Button) ```` --- ### - UI 對齊技巧:居中+等距排列 請將 `MenuPanel` 的 **Anchor 錨點** 設為「Middle Center」,並設置: * `RectTransform` 的 PosX / PosY 為 `0` * Width = 1000, Height = 1000(或符合你的 Canvas 設計) * Layout 可使用 `Vertical Layout Group` 搭配 `Content Size Fitter` 這樣即使 WebGL 畫面在不同瀏覽器比例中,Menu 依然能「正中顯示」。 > MenuPanel 加紅線後位置對照圖 <img src="https://hackmd.io/_uploads/H14_cQNQgg.png" width="60%"> --- ### - Button 的互動設定 請確保每個 Button 都有正確的事件觸發,例如: ```csharp public void ShowMenu() { menuPanel.SetActive(true); } public void NewGame() { GameManager.Instance.RestartGame(); } public void OpenGallery() { menuPanel.SetActive(false); galleryPanel.SetActive(true); } ``` --- ### - 常見問題排查表 | 錯誤狀況 | 原因與修正方式 | | --------------- | ------------------------------------------------------ | | 按下按鈕無反應 | 確認是否有綁定事件,是否選錯函數、漏接物件 | | Menu 出現在畫面左下角 | Anchor 沒設為 Middle Center,PosX/Y 可能偏移 | | 畫面變大時 Menu 沒有置中 | 沒有使用 Layout Group 或未設置 AutoSize 調整 UI 尺寸 | | 點擊 Menu 遮住了按鈕 | Panel Sorting Order 設錯,或遮蓋的物件沒設定互動層級 | | 畫面從遊戲返回 Menu 卡住 | 沒有切換好 GameState 或 Scene 沒重置;需檢查 GameManager 狀態控制流程是否完整 | --- ## 圖鑑 Gallery 功能設計與解鎖邏輯 本章節介紹如何設計一個具有「解鎖判斷」與「視覺展示」功能的圖鑑系統(Gallery),支援: - Tile 達到特定數值即自動解鎖對應角色 - 動態生成卡片(使用 Prefab) - 圖鑑內容自動排列,支援滾動瀏覽 - 圖片、文字、🔒 鎖圖搭配的顯示切換 --- ### - 解鎖邏輯:依據最高 Tile 值 使用 `PlayerPrefs.GetInt("highestTile", 2)` 取得最高歷史記錄,判斷哪些角色卡應該解鎖。 ```csharp int unlockedMax = PlayerPrefs.GetInt("highestTile", 2); bool unlocked = value <= unlockedMax; ```` --- ### - MarioCard 設計(Prefab) 請建立一個卡片預製物件 `MarioCard`,包含以下元件: ```plaintext MarioCard (GameObject) ├── Image (角色圖片) ├── Text (角色名稱) ├── LockIcon (🔒 小圖示) ``` --- ### - 未解鎖/已解鎖視覺效果 | 狀態 | 顯示內容 | | --- | ------------------------ | | 已解鎖 | 圖片正常顯示、加透明度、名稱常駐 | | 未解鎖 | 圖片半透明、上方加 🔒 Icon、名稱照樣顯示 | 可使用 `CanvasGroup.alpha = 0.3f` 或直接調整圖片透明度實現。 ```csharp cardImage.color = new Color(1f, 1f, 1f, unlocked ? 0.5f : 0.2f); lockIcon.SetActive(!unlocked); ``` --- ### - 圖鑑生成邏輯(GalleryManager) ```csharp [SerializeField] private GameObject cardPrefab; [SerializeField] private Transform contentParent; void GenerateCards() { for (int i = 0; i < marioTileValues.Length; i++) { GameObject cardObj = Instantiate(cardPrefab, contentParent); MarioCard card = cardObj.GetComponent<MarioCard>(); Sprite sprite = Resources.Load<Sprite>($"Sprites/MemeTiles/Tile_{value}"); bool unlocked = value <= unlockedMax; card.SetCard(sprite, name, unlocked); } } ``` > 生成後圖鑑畫面(含數張卡片、滾動條) <img src="https://hackmd.io/_uploads/Ska3WVN7xe.gif" width="80%"> --- ### - 常見錯誤排查表 | 問題狀況 | 解法與排查方向 | | ---------------- | -------------------------------------------- | | 卡片沒有顯示 | 檢查 Resources 路徑是否正確、Prefab 是否被正確 Instantiate | | Sprite 未載入 | 是否 Resources/Tile\_xxx.png 存在?副檔名要一致 | | 🔒 鎖圖沒出現/一直存在 | 是否依照 `unlocked` 控制 Icon 顯示與否 | | 所有卡片圖片都太大或不齊 | Grid Layout Group 設定錯誤,建議使用 Cell Size 固定比例 | | Scroll View 滾動異常 | 請確認 Viewport 是否有加 Mask + Content 是否超出範圍 | --- ### - 實作流程總結 1. 準備 Mario 圖片素材,命名為 `Tile_數字.png` 並放入 Resources 2. 設計卡片 Prefab(圖片+名稱+鎖圖) 3. 編寫 `GalleryManager.cs` 依照 highestTile 生成對應卡片 4. 用 Grid Layout + ScrollView 美化顯示效果 --- ## WebGL 部署與 GitHub Pages 發佈流程 將你的 Unity 專案部署為網頁版本,可以讓大家直接透過瀏覽器體驗你的遊戲,不需下載任何檔案!本章教你如何將 WebGL 遊戲部署至 GitHub Pages,並提供給他人公開連結。 --- ### - WebGL Build 設定步驟 1. 開啟 Unity,點選 `File > Build Settings` 2. 選擇平台為 `WebGL`,並點 `Switch Platform` 3. 點擊 `Player Settings` 設定畫面解析度與背景: - `Resolution and Presentation > Default Canvas Width/Height` 設為 `1000x1000` - `Publishing Settings > Compression Format` 建議選 `Disabled` 4. 點選 `Build`,選擇資料夾(例如 `webglBuild/`)輸出檔案 > Build 設定畫面截圖、壓縮格式調整處 > ![image](https://hackmd.io/_uploads/H1pNT7Vmex.png) --- ### - 結構說明:Build 後資料夾內容 ```plaintext webglBuild/ ├── Build/ │ ├── Build.data │ ├── Build.framework.js │ ├── Build.loader.js │ └── Build.wasm ├── TemplateData/ │ ├── style.css │ └── favicon.ico └── index.html ```` * `index.html` 是入口檔,負責載入 Unity 遊戲畫面 * 所有其他檔案不可遺漏,GitHub Pages 需完整上傳 --- ### - 發佈至 GitHub Pages 1. 前往 [GitHub](https://github.com) 建立一個公開儲存庫(repo) 2. 使用 Git 上傳 Build 資料夾內容(或使用 GitHub Desktop) 3. 在 repo 中: * 點選 `Settings > Pages` * 選擇分支 `main` 與資料夾來源(通常是 `/ (root)` 或 `/docs`) * 儲存後會顯示一個網址,例如: `https://yourname.github.io/your-repo-name/` --- ### - 發佈後 Debug 注意事項 | 問題描述 | 解法說明 | | ---------------- | -------------------------------------------------- | | 進入頁面後白畫面或卡住 | 確認是否少上傳 `Build/` 資料夾或 `index.html` 損壞 | | 無法正常顯示 Canvas 尺寸 | 檢查 `index.html` 中 `<canvas>` 標籤的 width/height 是否正確 | | LocalStorage 未重置 | 開啟 Chrome 開發者工具(F12),手動清除 LocalStorage | | 無法點擊按鈕/畫面不對齊 | 檢查是否有 UI Anchor 沒有設為 Center,或 Canvas 設定錯誤 | --- ### - 結果展示 🎮 遊戲打包後可以在網頁上demo(只能用電腦版,需要一些時間打開): 👉 [https://doreen1113.github.io/unity-2048-mario/](https://doreen1113.github.io/unity-2048-mario/) > 瀏覽器中成功顯示遊戲畫面 > ![image](https://hackmd.io/_uploads/SJzc0M4Xeg.png) ## 常見錯誤彙整與排錯策略分享 在專案製作與 Web 發佈過程中,我實際遇到了不少令人崩潰的錯誤。本章將整理出幾個常見問題,並說明我如何排解與修正,幫助初學者少走彎路。 --- ### - 遊戲邏輯與 UI 常見問題 | 問題描述 | 錯誤畫面 | 解決方式 | |------------------------------|--------------------------------------------------------|--------------------------------------------------------------------------| | Menu 畫面無法居中顯示 | <img src="https://hackmd.io/_uploads/Sy7347NXxl.png" width="100%"> | Canvas > RectTransform 設定錯誤,需將 MenuPanel 的 Anchor 設為 Middle Center 並加上 Layout Group | | 解鎖圖片沒有透明效果 | <img src="https://hackmd.io/_uploads/HkVUUmVmxe.png" width="100%"> | 原本只有未解鎖設透明,已解鎖圖片也應維持半透明,需統一調整 Image alpha 值 | --- ### - WebGL 發佈常見問題 | 問題描述 | 解法建議 | |--------------------------------|--------------------------------------------------------------------------| | 打開 GitHub Pages 網址顯示空白畫面 | 檢查是否漏上傳 `index.html` 或 `Build/` 資料夾,並重新部署 | | 頁面打開無法互動或 UI 錯亂 | index.html 畫面尺寸錯誤,Canvas 寬高需一致,或 HTML 中 `<canvas>` 標籤未更新 | | 紀錄無法重置 | Chrome → 開發者工具(F12)→ Application → LocalStorage → Clear All | --- ### - Unity 編輯器內部錯誤 | 錯誤訊息 / 現象 | 解決方式 | |-------------------------------|------------------------------------------------------------------------| | `PlayerPrefs` 無法寫入 | 若在 WebGL,改以 `localStorage` 模擬,測試時用 `PlayerPrefs.DeleteAll()` | | 圖片未正常顯示 | 檢查 Resources 資料夾路徑、檔名與大小寫是否完全相符 | | 畫面 Layout 跑版 | 使用 `Content Size Fitter` 與 `Layout Group` 搭配,避免重複套用 anchor 導致衝突 | --- ### - 排錯建議流程 1. **Debug.Log 是好朋友**:觀察函式觸發與變數值 2. **一層一層檢查 Hierarchy 與 UI 元件** 3. **遇到不動的畫面 → 看 EventSystem 是否存在** 4. **針對 WebGL:一定要記得壓縮格式選 Disabled,再打包!** --- ## 心得回顧與學習建議 這次從 Brackeys 的影片出發延伸設計功能,是我第一次將一個原有的 Unity 小遊戲進一步擴充成帶有個人風格與完整系統的專案。雖然一開始只是想單純練習 Script 撰寫與 UI 操作,但最終卻打造出一款具備「雙模式」與「圖鑑解鎖」的 Web 遊戲。 --- ### - 我學到的幾件事 - **Debug 是最佳朋友**:從圖片顯示錯誤到解鎖條件錯誤,每一次的 `Debug.Log()` 都是解開謎團的鑰匙。 - **Prefab 管理的重要性**:若少設一個變數或動態生成沒配好位置,遊戲畫面會瞬間失控。 - **UI 設計不只是排版而已**:Anchor、Pivot、Layout 結構若沒規劃好,遊戲在不同解析度下會完全走鐘。 - **WebGL 部署沒想像中難,但細節超多**:從 Build 設定、GitHub Pages 上傳、index.html canvas 調整,每一步都關鍵。 --- ### - 對未來學習者的建議 | 類別 | 建議內容 | |------------|--------------------------------------------------------------------------| | 初學者 | 先看完原教學影片,理解 2048 運作邏輯,再來動手加功能 | | 中階開發者 | 嘗試自己寫 `GameMode` 或 `GalleryManager`,從 0 控制圖片與資料邏輯 | | 想發佈作品 | 嘗試用 WebGL 發佈 + GitHub Pages,會學到超多「不是 Unity 的事」 | | 想學進階 | 可擴充排行榜(Firebase)、動畫過渡、手機適應等,變成更完整的作品 | --- ### - 未來想嘗試的新功能(Bonus Idea) - 增加 **動畫效果**(如 tile 合併爆炸、Gallery 閃爍解鎖特效) - 加入 **音效與 BGM**(使用 Unity AudioMixer 控制) - 增加 **排行榜功能**(搭配 Firebase Realtime Database) - 嘗試實作 **每日任務 / 限時模式** --- ### - 結語 希望這份教學不只是讓你會「做出來」,也能讓你學會「解決問題」與「創造功能」。 每次錯誤都是前進的一步,錯愈多、學愈深! 如果你也有興趣體驗這個遊戲 👉 **[點此立即遊玩 Unity 2048 雙模式版!](https://doreen1113.github.io/unity-2048-mario/)** ---