《UIS》 第 6 篇 進階功能與第三方整合

本篇文章、圖片出處來自 Ultimate Inventory System
軟體版本與本翻譯文件可能會有落差,本翻譯文件僅供參考。
本譯文為本站譯者原創翻譯內容,文字著作權歸本站所有。
未經授權,請勿任意轉載、改作或商業使用。

UIS 提供了一組開箱即用的選單元件,每個選單都是 Display Panel Binding,所有選單都可以透過 UI Designer 快速建立和設定。

Main Menu(主選單)

Main Menu 是一個可包含子面板的 Panel Binding,子面板透過 Action Buttons 開啟。設定時請特別注意 Display Panel 的「Is Menu Panel」選項:

  • 若「Set Disable On Close」取消勾選,則選取子面板時,主選單仍會持續顯示。
  • Main Menu 可被 Display Panel Manager 直接開啟/關閉。

在建立 Inventory Grid、Save Menu 等子面板時,選擇「Main Menu」Panel Option 即可讓它們作為 Main Menu 的子面板。

Shop Menu(商店選單)

Shop Menu 讓玩家從 Shop 元件買賣物品,包含以下核心元件:

元件說明
Inventory客戶端(玩家)的 Inventory。
Shop處理所有買賣邏輯,以及決定哪些物品可以買賣。
Inventory Grid顯示可購買/可販售的物品。
Multi Currency View顯示 Shop 定義的物品總價格。
Quantity Picker Panel讓玩家選擇買賣數量。
多商店共用同一 UI Shop 元件可以直接放在 Shop Menu 旁邊,也可以透過程式碼設定,讓遊戲中多個商店共用同一套 UI。最簡單的方式是使用 Shop Menu Opener 元件。

Save Menu(存檔選單)

Save Menu 讓玩家選擇存檔槽來載入、存檔或刪除,使用 Save System Manager 執行操作。使用 UI Designer 的「Save」Tab 可以快速建立。

Storage Menu(倉庫選單)

Storage Menu 通常用來在玩家 Inventory 和倉庫 Inventory 之間交換物品。它為玩家端和倉庫端各自參照一個 Inventory 和 Inventory Grid。

Inventory Grid 設定了自訂的點擊事件,開啟數量選擇面板讓玩家指定存取數量。因此 Storage Menu 與 Item Actions 不相容。

替代方案 Storage Menu 是一種解決方案,也可以完全從頭建立自訂的 Storage Menu,甚至只用 Item Actions 和拖放配合浮動 Inventory Grid 面板來實現。

Chest Menu(寶箱選單)

Chest Menu 用來從 Chest 元件取出物品。場景中的 Chest 元件在被開啟時,會自動找到 Chest Menu 並綁定自身。玩家透過 Inventory Interactor 和 Interactable 元件與寶箱互動,Chest Menu 即自動開啟。

同 Storage Menu,Inventory Grid 的點擊事件會開啟數量選擇面板,因此 Chest Menu 也與 Item Actions 不相容。

任何繼承 IChest 介面的元件都可以透過以下方式綁定至 Chest Menu:

chestMenu.SetChest(myChest);

Crafting Menu(合成選單)

Crafting Menu 讓玩家選擇配方並顯示所需材料,包含以下核心元件:

元件說明
Inventory客戶端(玩家)的 Inventory。
Crafter持有配方清單和合成處理器邏輯的元件。
Crafting Recipe Grid顯示 Recipe Views 的格子,讓玩家選擇要合成的配方。
Recipe Panel顯示合成輸出和所需材料的面板。
Quantity Picker Panel讓玩家選擇合成數量。
多合成台共用同一 UI 與 Shop Menu 類似,Crafter 可以放在 UI 內,也可透過程式碼綁定,讓多個合成台共用同一 UI。使用 Crafting Menu Opener 元件是最簡便的方式。

Inventory Monitor

Inventory Monitor 監聽 Inventory 上的 Item Collection,並在物品被加入時彈出 Item Views 顯示通知(例如「撿到物品」的提示)。

  • 可使用 Inventory Identifier ID 監視 Inventory,不需要在 Inspector 中直接建立參照。
  • 可以選擇要監視哪些 Item Collections。
  • 可以將在同一個視圖內加入的相似物品合併顯示。

啟用 / 停用 Inventory Monitor

// 透過事件停用
EventHandler.ExecuteEvent(m_Inventory.gameObject,
    EventNames.c_InventoryGameObject_InventoryMonitorListen_Bool,
    false);

// 或直接停用 GameObject
inventoryMonitorGameObject.SetActive(false);

Demo 場景中,主選單旁邊顯示了一個能即時反映裝備的角色預覽。以下是兩種建立方式:

方案一:複製角色建立 UI 角色(推薦)

建立一個視覺上展示裝備的角色,不含遊戲邏輯,透過 Render Texture 顯示在 UI 中。

建立 UI 角色

  1. 複製你的角色 GameObject,移除所有腳本,只保留 Animator。確認 Animator 未開啟 Root Motion。
  2. 加入 Equipper 腳本(請勿使用 VisualCharacterEquipper 或 CharacterEquipper,這些是 Demo 專用)。
  3. 建立 Item Slot Set:專案視窗右鍵 → Create → Ultimate Inventory System → Inventory → Item Slot Set
  4. 在 Equipper 中設定 Item Slot Set。
  5. 由於 UI 角色通常不需要使用物品,可忽略 Usable Item Prefab Attribute Name,只設定 Prefab Attribute Name(指向裝備武器時要顯示的 Prefab)。
  6. 設定要監視的 Inventory,並將 Item Collection 指向已裝備的物品 Collection。
  7. 在清單中選取每個槽位,設定物件是否為 Skinned Mesh,若否則設定物品的父 Transform。

在 UI 中顯示角色

  1. 建立 Render Texture(專案視窗右鍵 → Create → Render Texture),設定合適的解析度。
  2. 在 UI 中加入 Raw Image GameObject,指派你的 Render Texture。
  3. 在 UI 角色旁新增一個 Camera,讓它朝向角色。建議將角色和相機放在遠離遊戲世界的地方(例如地面以下),或放在另一個 Layer 並從主相機的 Culling Mask 中排除。
  4. 設定相機 Background 為 Solid Color,Clear Flags 為 Solid Color,背景 Alpha 設為 0(透明背景)。
  5. 將 Camera 的 Target Texture 欄位設為你建立的 Render Texture。
動畫支援 若需要角色播放裝備武器的動畫,目前沒有簡便的通用解法,必須建立自訂 Equipper 並手動觸發裝備動畫。官方計劃在未來提供更完善的解決方案。

方案二:直接在玩家角色前放置相機(較簡單)

  1. 建立 Render Texture 並設定解析度。
  2. 在 UI 加入 Raw Image,指派 Render Texture。
  3. 在玩家角色的 GameObject 內新增一個 Camera,旋轉使其朝向角色。
  4. 將 Render Texture 指派至相機的 Target Texture 欄位。
  5. 設定相機透明背景(同方案一步驟 4)。
  6. 調整 Culling Mask 和 Clipping Planes,只顯示角色和武器。
方案二的限制 這個方案較簡單,但有一些限制:不適用於第一人稱視角角色;由於渲染的是場景中的真實角色,遊戲世界中的事件仍會影響它;暫停遊戲時也無法繼續顯示,也無法播放 UI 專用的動畫。

ResizeableArrays & ListSlices

UIS 使用兩種自訂的資料容器類型,在正確使用的情況下,可以提升效能並增加開發靈活性。

ResizeableArray(可調整大小的陣列)
一個包含單一陣列的類別,允許動態調整大小。內部陣列長度只會在必要時增加;若縮小則只減少內部計數值,不釋放記憶體。

適用時機:大小不頻繁改變的資料集合。若不確定,使用 List 更為安全。
ListSlice(清單切片)
一個代表清單某個範圍的 struct,記錄起始和結束索引,讓你可以迭代那個範圍中的元素。

任何實作 IReadOnlyList<T> 介面的物件(Array、List、ResizeableArray)都可以轉換為 ListSlice。

重要:ListSlice 不是原始清單的複製,若原始清單內容改變,ListSlice 也會跟著改變。

事件系統(Events)

UIS 使用兩種事件機制:C# 原生事件系統,以及 Opsive 的 Event Handler(在所有 Opsive 資產中共用)。

所有事件名稱可在 EventNames.cs 中找到 Event Handler 的事件名稱以靜態常數定義,命名規則如下:
// 有目標物件的事件名稱格式
public const string c_TargetObjectType_EventDescription_Param1Type_Param2Type_...

// 範例
public const string c_ItemCollection_OnAdd_ItemAmount

// 全域事件(無目標物件)格式
public const string c_Global_EventDescription_Param1Type_Param2Type_...

通用 Event Handler API

// 必須引用正確命名空間
using Opsive.Shared.Events;

// 註冊事件(當事件被執行時呼叫 MyFunction)
EventHandler.RegisterEvent<int>(myObject, myEventName, MyFunction);

// 執行事件(帶 int 參數)
EventHandler.Execute<int>(myObject, myEventName, 7);

// 取消註冊事件
EventHandler.UnregisterEvent<int>(myObject, myEventName, MyFunction);
參數類型必須完全一致 執行事件的參數類型和監聽器的方法簽章必須完全相符,否則會發生錯誤。同一個事件名稱永遠不應以不同的參數類型執行。

UIS 專用事件範例

private void Start()
{
    // 監聽 Inventory 新增物品事件
    // 第一個參數:目標(m_Inventory)
    // 第二個參數:事件名稱(來自 EventNames 靜態類別)
    // 第三個參數:觸發時呼叫的函式
    EventHandler.RegisterEvent<ItemInfo, ItemStack>(m_Inventory,
        EventNames.c_Inventory_OnAdd_ItemInfo_ItemStack,
        HandleOnAddItem);

    // 監聽 Inventory 移除物品事件
    EventHandler.RegisterEvent<ItemInfo>(m_Inventory,
        EventNames.c_Inventory_OnRemove_ItemInfo,
        HandleOnRemoveItem);
}

/// <summary>物品被加入 Inventory 時觸發。</summary>
private void HandleOnAddItem(ItemInfo originItemInfo, ItemStack addedItemStack)
{
    if (addedItemStack == null) { return; }
    // 使用 originItemInfo 和 addedItemStack
    // 可得知物品來源、加入的 Stack、加入的數量等
}

/// <summary>物品被移除時觸發。</summary>
private void HandleOnRemoveItem(ItemInfo removedItemInfo)
{
    // removedItemInfo.Amount:被移除的數量
    // removedItemInfo.ItemStack.Amount:Stack 中剩餘的數量
}

/// <summary>務必在 OnDestroy 中取消註冊。</summary>
private void OnDestroy()
{
    EventHandler.UnregisterEvent<ItemInfo, ItemStack>(m_Inventory,
        EventNames.c_Inventory_OnAdd_ItemInfo_ItemStack, HandleOnAddItem);
    EventHandler.UnregisterEvent<ItemInfo>(m_Inventory,
        EventNames.c_Inventory_OnRemove_ItemInfo, HandleOnRemoveItem);
}

Spawn Player(動態載入角色)

預設情況下,Demo 場景中玩家 Inventory 和 UI 都在場景中。但有時你可能需要在場景載入後的不同時機動態生成 UI 或角色。以下說明各種情境:

最重要原則 Managers(通常是「Game」GameObject)必須在所有其他 Inventory System 相關元件之前載入。Managers 是被系統中許多元件參照的 Singleton,若其他元件先載入會拋出錯誤。

先載入 UI

若 Display Panel Manager 在玩家 Inventory 之前載入,它會等待 Panel Owner 被設定才進行初始化。此時所有面板都不會初始化,也無法使用,直到 Panel Owner 被設定。

在玩家 Prefab 上加入 Dynamic Panel Owner 元件,當玩家在執行期間被生成時,會自動將 Panel Owner 綁定至 Display Panel Manager。也可以透過程式碼手動設定。

先載入玩家

先載入玩家後再載入 UI 不需要任何額外設定。UI 生成後,Display Panel Manager 會自動使用 Inventory Identifier ID 找到對應的玩家。ID 必須相符;本地多人時,每個 UI 的 ID 必須與對應玩家的唯一 ID 一致。

同時載入玩家和 UI

也可以建立一個只包含玩家和 UI 的場景,以加法載入(Additive Loading)的方式加入遊戲。

將 Inventory 從玩家 GameObject 分離

在某些情況下,可以將 Inventory 與玩家 GameObject 分離,例如將 Inventory 放在 Canvas 上,讓玩家 GameObject 透過 Inventory Identifier ID 取得參照:

var inventoryIdentifier = InventorySystemManager.GetInventoryIdentifier(id);
var inventory = inventoryIdentifier.Inventory;
限制 這個方式有一些限制,例如 Inventory Interactor 元件不支援此設定。

場景轉換(Scene Transitions)

場景轉換時,通常需要將角色的物品從前一個場景帶入下一個場景。選擇方案前,必須決定要跨場景保留哪些元件(Managers、玩家、UI)。

Save System Manager 與場景轉換 若使用 Auto Save On Scene Unload 和 Auto Load On Scene Load,每個 Saver 元件必須設定「Load On Start」和「Save On Destroy」。載入新場景後,建議等待一幀再呼叫 Load,確保所有 Saver 元件已向 Manager 完成註冊。

三種場景轉換方案比較

方案一:每個場景包含所有元件

設定簡單直觀

不易擴展,修改一個元件需更新所有場景

每次切換場景前存檔,新場景載入後等一幀再載入。

方案二:Starter Scene

只在最開始初始化一次 Managers 和資料庫,更有效率

只能從 Start 場景開始測試

從關卡回到 Start 場景時 Managers 會被複製而導致錯誤

將 Managers GameObject 設為 DontDestroyOnLoad。

方案三:Smart Managers Load Component

只初始化一次,效率高

每個場景可以直接測試,不需要手動載入 Start 場景

稍微不直觀,需要自訂程式碼

需要將該元件的 Execution Order 設為 -500,確保最先執行。

方案三:Smart Managers Load 程式碼範例

public class SceneLoaderManagerExample : MonoBehaviour
{
    [SerializeField] protected GameObject m_ManagerPrefab;
    [SerializeField] protected int m_ManagerSceneBuildIndex = -1;

    private void Awake()
    {
        // 若 Manager 已存在,此元件不再需要
        if (!InventorySystemManager.IsNull) {
            Destroy(gameObject);
            return;
        }

        // 方案 A:以 Prefab 生成 Managers(記得設定 DontDestroyOnLoad)
        if (m_ManagerPrefab != null) {
            var managers = Instantiate(m_ManagerPrefab);
            // DontDestroyOnLoad(managers); // 可選:在此設定或在 Manager 元件內設定
        }

        // 方案 B:以加法方式載入包含 Managers 的場景
        if (m_ManagerSceneBuildIndex >= 0) {
            SceneManager.LoadScene(m_ManagerSceneBuildIndex, LoadSceneMode.Additive);
        }

        // Managers 載入後即可移除此元件
        Destroy(gameObject);
    }
}

第三方整合(Integrations)

UIS 與多種常用 Unity 資產和系統提供了整合支援:

Bolt(Visual Scripting)

無需額外下載。Bolt 透過反射可直接使用所有 UIS 函式和類型。需在 Bolt Setup Wizard 的 Assemblies Tab 中加入 Opsive.UltimateInventorySystem 組件,並在 Types Tab 加入 ItemInfoItemAmount 類型。

內建支援
⚠ Bolt 和 UIS 都含有 Antlr3.Runtime.dll,使用時需移除其中一個以避免衝突。
Dialogue System

讓 Dialogue System 管理庫存操作,整合由 Pixel Crushers 維護。詳細文件在整合套件中。

需下載整合套件
Unity Input System / Rewired / InControl

支援完整的控制器輸入。PlayerInput 元件作為抽象層,更換輸入系統不需修改任何遊戲邏輯。整合設定與 Ultimate Character Controller 共用。

需下載整合套件
Master Audio

以 Master Audio 取代 Unity 原生音效系統。與 Character Controllers 使用相同的整合套件。

需下載整合套件
Opsive Behavior Designer

使用 Behavior Designer 管理庫存。Tasks 可直接在標準 Behavior Designer 工作流程中使用,無需額外設定。

需下載整合套件
Opsive Character Controllers(UCC)

與 Opsive 系列角色控制器的完整整合。包含 InventoryBridgeSaver、CharacterEquipper 等整合專用元件。詳細文件在官方整合頁面。

需下載整合套件
Playmaker

使用 Playmaker 管理庫存。Action 可直接在標準 Playmaker 工作流程中使用,無需額外設定。

需下載整合套件
Quest Machine

讓 Quest Machine 管理庫存,整合由 Pixel Crushers 維護。詳細文件在整合套件中。

需下載整合套件
Unity Localization

支援多語言本地化,使用 Unity 官方 Localization 套件。包含完整的整合元件和示範場景,支援英語、西班牙語、法語等。

需下載整合套件 需安裝 Localization Package

Unity 本地化(Localization)

本地化讓你的遊戲可以支援多種語言,讓更多玩家能以母語體驗遊戲。建議在開發初期就開始建立本地化系統,因為上線前才替換所有文字會非常耗時,且可能需要修改架構。UIS 使用 Unity 官方的 Localization 套件。

安裝步驟

  1. 透過 Package Manager 安裝 Unity Localization 套件(Unity 2021.2 以前版本需使用 git url)。
  2. 從 UIS 下載頁面下載並安裝整合套件。
  3. 前往 Edit → Project Settings → Localization,確認已參照 Localization Settings Asset。
  4. 開啟整合示範場景(Assets/Opsive/UltimateInventorySystem/Integrations/UnityLocalization/Demo)確認功能。
重要:測試完畢後刪除整合示範資料夾 一個 Unity 專案只應有一個 Localization Settings。測試完畢後請刪除整合示範資料夾,並在 Project Settings → Localization 設定你自己的 Localization Settings。

整合核心元件

Localize Item View

整合的主要元件。根據格式字串建立本地化的 Reference Key,再由 Unity Localization 套件的 LocalizeStringEvent 元件使用該 Key 取得對應語言的文字。

格式字串中可使用的佔位符:

佔位符對應值
{0}Item 名稱
{1}Attribute 值
{2}Item Definition 名稱
{3}Item Category 名稱
{4}Attribute 名稱
必須在 LocalizeStringEvent 中設定 Table 參照 Localize Item View 只設定 Table Entry Reference(Key),你必須在 LocalizeStringEvent 元件中設定 Table 參照才能取得對應語言的文字。

Auto Localize String Event

繼承自 Unity Localization 的 LocalizeStringEvent 類別,自動偵測旁邊的 Text 元件並在 Inspector 中設定,省去每次手動設定的步驟。

/// <summary>設定 Table Entry Reference(Key)。</summary>
public void SetTableEntryReference(string tableEntryReference)
{
    StringReference.TableEntryReference = tableEntryReference;
    RefreshString();
}

Item Description 的特殊處理

Item Description 通常直接將物品名稱和說明寫入 Text 元件。但在使用本地化時,這會與 Localization 元件產生競爭條件(race condition)——兩個元件都嘗試寫入 Text 元件,造成最終顯示的語言不確定。

解決方式:移除 Item Description 元件中對 Text 元件的參照,改由 Localize Item View 負責所有文字的寫入。可參考整合示範場景的設定方式。

將現有資料庫本地化

若你的資料庫已包含大量內容,需要在上線前進行本地化,建議的工作流程:

  1. 使用 Inventory Editor Manager 的 Import/Export 功能,將資料庫匯出為 CSV 檔案,取得所有物品名稱和說明。
  2. 將相關資料複製到本地化表格(例如 Google Sheets,欄位包含:Key、English(en)、French(fr)、Spanish(es) 等)。
  3. 在匯出的資料庫 CSV 中,將說明文字替換為本地化表格中的 Key,再匯回資料庫,更新 Description 屬性值。
  4. 對於物品名稱,可以選擇直接使用原名作為 Key,或加上前綴/後綴並將空格替換為底線。

本地化 Demo 說明

整合示範場景使用與主要 Demo 相同的 Inventory Database,展示英語、西班牙語和法語三種語言。包含兩個本地化表格:

  • LocalizationTable_UltimateInventorySystem_LocalizedDemo_InventoryDatabase:物品名稱、說明等資料庫物件的翻譯。
  • LocalizationTable_UltimateInventorySystem_LocalizedDemo_UI:固定的 UI 文字。
說明文字 Key 命名範例
說明文字的 Key 格式:<ItemName>_Description
對應的 Localize Item View 格式字串:{0}_Description{0} 為物品名稱)

切換語言:在 Play Mode 中,Game 視窗右上角出現的語言切換下拉選單(安裝 Localization 套件後自動出現)。

🎉 文件系列完整翻譯完成!

你已完成 Ultimate Inventory System 全部六篇繁體中文文件的閱讀。以下是各篇的快速回顧:

  • 第 1 篇:系統介紹、入門設定、術語說明、Demo 場景
  • 第 2 篇:版本升級指南、Editor Window、各資料物件編輯器、UI Designer、Import/Export
  • 第 3 篇:Inventory、Item Collections、Item、Item Info、升級/技能/數值/動作/物件/裝備/撿取/掉落系統
  • 第 4 篇:Attributes 深入解析、Common Attributes、貨幣系統、商店、合成系統與客製化處理器、Input
  • 第 5 篇:Handlers、分割畫面、存檔系統、互動系統、Display Panel、Item View Slots Container、Inventory Grid、Item Shape Grid、Item Hotbar、拖放、Filter/Sorter、Views
  • 第 6 篇:選單系統、Inventory Monitor、選單角色預覽、事件系統、場景轉換、第三方整合、本地化

如有問題可前往 Opsive 官方論壇forum.opsive.com)尋求協助。祝開發順利!

此網誌的熱門文章

哥利亞遙控炸彈 (Leichter Ladungsträger Goliath)

O-I(試製120t超重戰車「オイ」)