iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Vibe Coding

從課堂半成品到完整發布:獨立遊戲的30天重生記系列 第 14

【DAY14】Unity 2D 敘事遊戲開發:辦公桌 UI 視窗置頂與陳情表單 (PaperManager) 決策機制

  • 分享至 

  • xImage
  •  

一、前言:
在前兩天(DAY 12 - 13),我們實裝了開局自訂暱稱與黑屏旁白打字機效果。今天我們要邁入遊戲重要的玩法——「辦公桌互動與決策介面」!我們將實裝兩支關鍵腳本:
1. PhoneUIController.cs: 處理電話 UI 面板的開啟、置頂層級管理與 Escape 鍵安全關閉。
2. PaperManager.cs: 處理陳情表單(Consent Form)的彈出、桌面互動按鈕遮擋,以及 ⭕ / ❌ 簽核決策對接 NewPlayerStatsManager 的數值計算與分歧轉場!

二、 辦公桌 UI 互動與決策流程需求
參考已上市2D遊戲《Papers, Please》的介面,了解到系統需要具備極佳的 UI 互動與狀態防護:
1. 電話視窗層級與彈性關閉 (PhoneUIController):

  • 點擊電話按鈕開啟 PhonePanel 並置頂(SetAsLastSibling)。
  • 支援點擊關閉按鈕,或按 Escape 鍵自動關閉當前最上層的視窗,關閉後自動恢復下方視窗的置頂順序。
    2. 桌面按鈕遮擋防護 (interactionButtonsContainer):
  • 當點開陳情文件決策面板時,自動隱藏桌上的電話/紙張等互動按鈕,防止玩家在閱讀或蓋章時誤觸其他物件。
    3. 陳情表單決策與數據對接 (PaperManager):
  • 提供 ⭕(Yes)與 ❌(No)兩組決策按鈕。
  • 點擊決策後呼叫 NewPlayerStatsManager 即時結算金錢變動與聲譽變動,並依選擇載入對應分歧場景(如 sceneIfYes / sceneIfNo)。

三、核心程式實作:PhoneUIController.cs 與 PaperManager.cs
1. 電話 UI 面板與層級控制 (PhoneUIController.cs)
PhoneUIController 負責管理電話視窗的顯隱與 UI 層級排序,並相容 Unity New/Old Input System 的 Escape 鍵快捷關閉:

using UnityEngine;
using UnityEngine.UI;
public class PhoneUIController : MonoBehaviour {
    [Header("UI 元件")]
    public GameObject phonePanel;     // 指向 Phone Panel
    public Button phoneButton;        // 點擊開啟 Phone 的按鈕
    public Button closeButton;        // Phone Panel 裡的關閉按鈕
    void Start()  {
        phonePanel.SetActive(false); // 起始隱藏
        phoneButton.onClick.AddListener(OpenPanel);
        if (closeButton != null)
            closeButton.onClick.AddListener(ClosePanel);
    }
    void Update() {
        // 支援按 Escape 鍵關閉最上層視窗
#if ENABLE_INPUT_SYSTEM
        if (phonePanel.activeSelf && IsTopPanel() &&
            UnityEngine.InputSystem.Keyboard.current.escapeKey.wasPressedThisFrame) {
            ClosePanel();
        }
#else
        if (phonePanel.activeSelf && IsTopPanel() &&
            Input.GetKeyDown(KeyCode.Escape)) {
            ClosePanel();
        }
#endif
    }
    public void OpenPanel() {
        phonePanel.SetActive(true);
        phonePanel.transform.SetAsLastSibling(); // 將視窗推至最上層置頂
    }
    public void ClosePanel() {
        phonePanel.SetActive(false);
        // 關閉後,若同父物件下有其他開啟中的 Panel,維持最上層視窗置頂
        Transform parent = phonePanel.transform.parent;
        for (int i = parent.childCount - 1; i >= 0; i--) {
            GameObject child = parent.GetChild(i).gameObject;
            if (child.activeSelf && child != phonePanel) {
                child.transform.SetAsLastSibling();
                break;
            }
        }
    }
    private bool IsTopPanel() {
        return phonePanel.transform.GetSiblingIndex() == phonePanel.transform.parent.childCount - 1;
    }
}

2. 陳情表單決策與數值結算 (PaperManager.cs)
PaperManager 負責控制陳情文件的開啟/遮擋、蓋章決策,並呼叫 NewPlayerStatsManager 進行數據結算與場景跳轉:

using UnityEngine;
using UnityEngine.SceneManagement; 
public class PaperManager : MonoBehaviour {
    [Header("UI 連接")]
    public GameObject documentPanel; // 文件決策面板 (Yes/No 按鈕的父物件)
    public GameObject interactionButtonsContainer; // 桌上互動按鈕容器 (用於隱藏遮擋)
    [Header("當前決策數值設定")]
    public int moneyChangeYes = -100000;
    public int reputationChangeYes = 2;
    public int moneyChangeNo = 0;
    public int reputationChangeNo = -3;
   
    [Header("分歧場景設定")]
    public string sceneIfYes = "SuccessScene"; 
    public string sceneIfNo = "FailureScene"; 
    
    [Header("決策處理引用")]
    public NewPlayerStatsManager decisionHandler; 
    private NewPlayerStatsManager statsManagerCache;

    void Start() {
        if (documentPanel != null) documentPanel.SetActive(false);
        FindStatsManager();
    }
    private void FindStatsManager() {
        if (decisionHandler != null) {
            statsManagerCache = decisionHandler;
            return;
        }
        statsManagerCache = FindObjectOfType<NewPlayerStatsManager>();
        if (statsManagerCache == null)
            Debug.LogError("FATAL ERROR: NewPlayerStatsManager 在場景中找不到!");
    }
    public void ShowDocumentDecision() {
        if (documentPanel == null) return; 
        documentPanel.SetActive(true);
        // 開啟陳情表單時,隱藏桌上的其他互動按鈕防止誤觸
        if (interactionButtonsContainer != null)
            interactionButtonsContainer.SetActive(false); 
    }
    public void HideDocumentDecision() {
        if (documentPanel != null) {
            documentPanel.SetActive(false);
            if (interactionButtonsContainer != null)
                interactionButtonsContainer.SetActive(true); 
        }
    }
    // 供 Document ⭕ 按鈕呼叫
    public void OnYesDecisionClick() {
        ProcessDecision(moneyChangeYes, reputationChangeYes, sceneIfYes, "Yes");
    }
    // 供 Document ❌ 按鈕呼叫
    public void OnNoDecisionClick() {
        ProcessDecision(moneyChangeNo, reputationChangeNo, sceneIfNo, "No");
    }
    /// 統一決策處理:數值更新 -> 關閉 UI -> 場景跳轉
    private void ProcessDecision(int moneyChange, int reputationChange, string targetScene, string debugTag) {
        if (statsManagerCache == null) FindStatsManager();
        if (statsManagerCache != null) {
            // 1. 執行數值變動
            statsManagerCache.HandleDecision(moneyChange, reputationChange);
            Debug.Log($"{debugTag} 決策執行:金錢 {moneyChange}, 聲譽 {reputationChange}");
            // 2. 關閉 UI
            HideDocumentDecision();
            // 3. 切換至對應分歧場景
            if (!string.IsNullOrEmpty(targetScene)) {
                SceneManager.LoadScene(targetScene);
            }
        }
    }
}

四、Unity Inspector 設定與實測驗收
在 Unity 介面中將元件拖曳連結:

1. 辦公桌 UI 視窗與發光點擊配置 (以DeskScene 9幕為例)
點擊桌面上的電話開啟 PhonePanel,PhoneUIController 會調用 SetAsLastSibling() 確保電話視窗置頂在最上層,且可隨時按下 Escape 關閉視窗。
https://ithelp.ithome.com.tw/upload/images/20260920/20184073IrY4ULwwn4.png
圖一、辦公桌場警-電話黃框點擊

https://ithelp.ithome.com.tw/upload/images/20260920/20184073njrGPQaVB3.png
圖二、電話 Panel 彈出

2. 陳情表單放大面板與 ⭕ / ❌ 決策
點擊桌面文件後呼叫 ShowDocumentDecision(),桌面互動按鈕容器自動隱藏。點擊 ⭕ 扣除 $10,000 並增加 2 點聲譽,同時自動切換至 SuccessScene;點擊 ❌ 扣除 1 點聲譽並自動切換至 FailureScene!
https://ithelp.ithome.com.tw/upload/images/20260920/20184073CtkiJvcgbi.png
圖三、接完電話後介面示意圖(此時可以選擇要重看對話或進行決策)

https://ithelp.ithome.com.tw/upload/images/20260920/201840733MZ2qadeJ6.png
圖四、點擊桌面文件後介面示意圖

https://ithelp.ithome.com.tw/upload/images/20260920/20184073vRZ9TuDVuj.png
圖五、if點擊⭕介面示意圖(金錢從980,000變970,000;聲譽17變19)

https://ithelp.ithome.com.tw/upload/images/20260920/20184073mTvUyaO6TS.png
圖六、if點擊❌介面示意圖(金錢j維持980,000;聲譽17變16)

五、開發實戰經驗:UI 防錯與數值架構設計
1. interactionButtonsContainer 視窗互斥機制: 在辦公桌這種多物件場景中,當玩家彈出大尺寸的決策表單(documentPanel)時,將背景的點擊按鈕容器暫時隱藏,是避免玩家連點誤觸非常關鍵的 UI/UX 防呆思維。
2. SetAsLastSibling() 多視窗疊加管理: 在 Unity Canvas 中,渲染順序完全取決於 Hierarchy 上的順序。使用 SetAsLastSibling() 能以最少的程式碼實現「後開啟的視窗永遠蓋在最上面」的效果,極度適合多視窗互動遊戲。

六、今日成果與小結

  • 電話 UI 視窗管理 (PhoneUIController):實裝了視窗置頂(SetAsLastSibling)與 Escape 鍵快捷關閉。
  • 陳情文件決策 (PaperManager):實現表單彈出、背景按鈕遮擋防護與 ⭕ / ❌ 決策選擇!
  • 數據對接與分歧轉場:成功對接 NewPlayerStatsManager,完成金錢/聲譽計算與 SuccessScene / FailureScene 分歧切換!

範例影片:Yes

因為這已經開始進到辦公桌互動玩法與桌面物件管理的階段,今天我們依照遊戲階段開新分支,將這個部分歸類在feature/desk-interaction分支:

1.檢查修改狀態
git status
2.加入暫存區並提交 Commit
git add .
git commit -m "feat: 實作辦公桌 PhoneUIController 視窗置頂與 PaperManager 決策結算機制"
3.推送到遠端開發分支
git push origin feature/desk-interaction


上一篇
【DAY13】Unity 2D 敘事遊戲開發:跨場景玩家暱稱繼承與黑屏旁白打字機效果 (TypewriterEffect)
下一篇
【DAY15】Unity 2D 敘事遊戲開發:JSON 紀錄點 (Save Point) 存讀檔架構實作
系列文
從課堂半成品到完整發布:獨立遊戲的30天重生記17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言