# 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 設定畫面截圖、壓縮格式調整處
>

---
### - 結構說明: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/)
> 瀏覽器中成功顯示遊戲畫面
>

## 常見錯誤彙整與排錯策略分享
在專案製作與 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/)**
---