《UIS》第 5 篇 輸入處理、存檔、互動與 UI 框架
本篇文章、圖片出處來自 Ultimate Inventory System
軟體版本與本翻譯文件可能會有落差,本翻譯文件僅供參考。
本譯文為本站譯者原創翻譯內容,文字著作權歸本站所有。
未經授權,請勿任意轉載、改作或商業使用。
輸入處理、存檔、互動與 UI 框架
輸入處理器(Handlers)
Handler 元件使用 PlayerInput 元件透過輸入觸發 UIS 的 API,並以 PlayerInput 作為抽象層,讓你可以隨時更換底層輸入系統而不需要修改任何遊戲邏輯。
| Handler 元件 | 用途 |
|---|---|
| Display Panel Manager Handler | 透過輸入開啟與關閉 Display Panels。 |
| Usable Equipped Item Handler | 設定輸入以使用預設裝備系統中已裝備的 Usable Item Object。 |
| Inventory Interactor | 可設定透過輸入觸發互動。 |
| Hotbar Handler | 提供輸入以使用快捷列特定槽位的物品。 |
| Item View Slot Container Item Action Handler | 將輸入映射至 Item Action,並對目前選取的 Item View Slot 觸發。 |
| Grid Base | Item Info Grid 和其他格子元件的基礎類別,處理切換至下一個或前一個 Tab 的輸入。 |
分割畫面多人(Split Screen Co-op)
UIS 支援分割畫面的多人合作 UI。兩名玩家各自擁有獨立的 Canvas 和 Inventory。若兩個 Display Panel Manager 都將「開啟面板時暫停時間縮放為 0」設為關閉,就可以讓一個玩家在村莊購物時,另一個玩家同時在場景中自由移動。商店、合成等選單也可以同時各自開啟使用。
分割畫面的限制條件
- 兩個角色必須各自擁有獨立的 UI Canvas。
- 必須使用 Unity 新版 Input System 或 Rewired。
- 使用 Unity 新版 Input System 時,只有遊戲手把(Gamepad)有效,鍵盤和滑鼠在 UI 中無法使用(這是 Unity Input System 目前的已知限制)。
設定步驟(以 Demo 場景為例)
步驟一:匯入輸入系統整合套件
Unity 新版 Input System:
- 從 Package Manager 匯入 Input System 套件,並匯入對應的 UIS 整合套件。
- 在 Player Character GameObject 上,將「Unity Input」元件替換為整合套件中的「Unity Input System」元件。
- 確認 Player Character 上有 Input System 的 Player Input 元件,並參照 Input Action Asset(建議使用整合套件提供的 Character Input)。
- 測試 Demo 確認功能正常。
Rewired:
- 從 Asset Store 或 Package Manager 匯入 Rewired,並匯入 UIS 整合套件。
- 在 Player Character 上,將「Unity Input」元件替換為「Rewired Input」元件。
- 將整合套件中的「Rewired Input Manager Co-op」Prefab 加入場景(Player1 ID = 0,Player2 ID = 1)。
- 測試 Demo 確認功能正常。
步驟二:複製玩家與 UI
- 複製玩家 GameObject 和 Inventory Canvas。
- 將頂層 Panel 縮放至半個畫面大小。
- 為兩個玩家設定不同的 Inventory Identifier ID(建議設為 1 和 2)。若使用 Dynamic Panel Owner,Panel Manager ID 需與 Inventory Identifier ID 相同。
- 移除原有的 Event System GameObject,替換為兩個分別的 Event System 1 和 Event System 2。
步驟三:設定 Event System(Unity Input System)
- 在兩個 Event System GameObjects 上各加入 Multiplayer Event System 和 Input System UI Input Module 元件(均來自 Unity Input System 套件)。
- 在 Multiplayer Event System 的「Player Root」欄位指派對應的 Canvas。
- 移除 Input System UI Input Module 預設的 UI Action Asset,改為參照角色 Player Input 上的 Input System UI Input Module,且每個玩家需指向不同的 Event System GameObject。
- 在 Player Input 上將 Default Scheme 從 <Any> 改為 Gamepad(鍵盤輸入不支援多人 Event System)。
步驟四:設定 Canvas
- 為每個 Inventory Canvas 設定不同的 ID,應與對應玩家的 Inventory Identifier ID 相同。
- 在 Canvas 上加入 Event System Identifier 元件,並參照正確的 Event System。
- 在 Game GameObject 上加入 Event System Manager(與其他 Managers 一起)。
音效(Audio)
UIS 的音效系統與 Ultimate Character Controller 共用同一套 Audio System。詳細的音效設定說明請參閱官方 Audio 文件。
在版本 1.2 的更新中,Audio Manager 已全面重寫為共用元件,支援 Audio Config ScriptableObject 定義音效清單、Audio Mixer、音量、音調(固定值或隨機值),也可以繼承 Audio Manager Module 接入自訂或第三方音效系統(如 FMOD、Wwise)。
存檔系統(Save System)
存檔系統可以將庫存的當前狀態(以及其他元件的狀態)儲存至磁碟,並在之後載入還原。UIS 的存檔系統設計上是用來擴展或嵌入現有存檔系統中使用的。
Save System Manager
Save System Manager 負責將資料序列化並寫入磁碟。序列化資料透過 SaverBase 元件向 Manager 註冊,存檔時 Manager 會遍歷所有 SaverBase 元件取得資料再序列化存入。
預設存檔路徑使用 Unity 的 Application.persistentDataPath。在 Save System Manager 上按右鍵可以在 Console 顯示你電腦的存檔資料夾路徑。
內建 Saver 元件
主要 Saver,儲存所有 Item 的狀態(屬性值、名稱等)。其他 Saver 只儲存 Item ID,必須有這個 Saver 才能正確還原(Immutable & Common 物品除外)。
儲存 Inventory 元件的內容(各 Item Collection 中的 Item ID 和數量)。
儲存 Currency Owner 的貨幣內容。
儲存物品在 Inventory Grid 中的位置。
通用 Saver,可儲存 GameObject 的位置、旋轉、縮放和啟用/停用狀態。
儲存 Item Shape Grid Data,記錄物品在形狀格子中的位置。
整合用(UCC):儲存綁定至 Ultimate Character Controller 角色的 Inventory 內容。
InventorySystemManagerItemSaver 必須存在,其他 Saver 才能正確儲存和載入物品。若只有 InventorySaver 而沒有這個 Saver,Unique/Mutable 物品的屬性值將無法被正確還原。
Save Data 與 Metadata
存檔資料分為兩個檔案:Save Data(完整序列化狀態)和 Save Meta Data(簡短的存檔摘要)。這樣可以避免一次載入所有存檔到記憶體,只需要在 Save Menu 中顯示摘要時才讀取。
Save Meta Data 和 Save Meta Data Creator 可以繼承擴展,加入自訂的遊戲時間、玩家進度等資訊,並在 Save Menu UI 的 Save Views 中顯示。
自訂 Save Meta Data 範例
// 自訂 Save Meta Data Creator(ScriptableObject)
[CreateAssetMenu(fileName = "BasicSaveMetaDataCreator",
menuName = "Opsive/Save System/Save Meta Data Creator.")]
public class BasicSaveMetaDataCreator : SaveMetaDataCreator
{
public override SaveMetaData CreateMetaData(
SaveSystemManager saveSystemManager, SaveDataInfo saveDataInfo)
{
return new BasicSaveMetaData(saveSystemManager, saveDataInfo);
}
public override SaveMetaData CreateEmpty() => new BasicSaveMetaData();
}
// 自訂 Save Meta Data(記錄存檔時間)
[Serializable]
public class BasicSaveMetaData : SaveMetaData
{
[SerializeField] protected long m_DateTimeTicks;
public long DateTimeTicks => m_DateTimeTicks;
public BasicSaveMetaData() : base()
{ m_DateTimeTicks = new DateTime().Ticks; }
public BasicSaveMetaData(SaveSystemManager mgr, SaveDataInfo info) : base(mgr, info)
{ m_DateTimeTicks = DateTime.Now.Ticks; }
public void SetDateTime(DateTime newDateTime)
{ m_DateTimeTicks = newDateTime.Ticks; }
}
Save System Manager API
// 存入第 0 號存檔槽
SaveSystemManager.Save(0);
// 從第 0 號槽載入
SaveSystemManager.Load(0);
// 刪除第 0 號存檔
SaveSystemManager.DeleteSave(0);
// 取得目前的存檔資訊
var saveData = SaveSystemManager.GetCurrentSaveDataInfo();
// 取得特定 Saver 的序列化資料
SaveSystemManager.TryGetSaveData("The Saver Key", out var serializedData);
嵌入第三方存檔系統
若你的遊戲已有自訂的存檔系統,仍可以嵌套使用 UIS 的存檔系統,只需在第三方系統的 Save/Load 介面中呼叫 UIS 的 Save/Load 即可:
public class NestedInventorySaver : ISave
{
public int saveSlot = 0;
public object RecordData()
{
// false 表示不寫入磁碟,只在記憶體中序列化
SaveSystemManager.Save(saveSlot, false);
var saveDataInfo = SaveSystemManager.GetCurrentSaveDataInfo();
return saveDataInfo.Data; // 可再用自訂序列化器處理
}
public void ApplyData(object data)
{
if (data == null) { return; }
var saveData = data as SaveData;
// 直接傳入 SaveData 跳過磁碟讀取
SaveSystemManager.Load(saveSlot, saveData);
}
}
自訂 Saver 元件
public class ExampleSaver : SaverBase
{
[System.Serializable]
public struct ExampleSaveData
{
// 在此加入可序列化的欄位
// 若要儲存物品或貨幣,使用 IDAmountSaveData
}
/// <summary>序列化存檔資料。</summary>
public override Serialization SerializeSaveData()
{
var saveData = new ExampleSaveData() {
// 設定要儲存的資料
};
return Serialization.Serialize(saveData);
}
/// <summary>反序列化並載入存檔資料。</summary>
public override void DeserializeAndLoadSaveData(Serialization serializedSaveData)
{
var savedData = serializedSaveData
.DeserializeFields(MemberVisibility.All) as ExampleSaveData?;
if (!savedData.HasValue) { return; }
// 將載入的資料套用至目標元件
}
}
互動系統(Interaction System)
UIS 的互動系統使用 IInteractor(互動者)和 IInteractable(可互動物件)兩個介面。可互動物件可以被「選取」、「取消選取」和「互動」;互動者可以新增和移除可互動物件的參照。
注意:Interactor 需要一個 Collider 才能偵測到 Interactable。
注意:Interactable 需要一個 Trigger Collider 才能偵測到 Interactor。
自訂 Interactable Behavior
public class ExampleInteractableBehavior : InteractableBehavior
{
public override bool CanInteract(IInteractor interactor)
=> base.CanInteract(interactor);
public override void OnSelect(IInteractor interactor)
{
base.OnSelect(interactor);
// 角色靠近時的行為(例如顯示提示 UI)
}
public override void OnDeselect(IInteractor interactor)
{
base.OnDeselect(interactor);
// 角色離開時的行為
}
protected override void OnInteractInternal(IInteractor interactor)
{
// 若需要確認 Interactor 具有 Inventory
if (!(interactor is IInteractorWithInventory withInventory)) { return; }
var inventory = withInventory.Inventory;
// 執行互動邏輯
}
}
UI 系統總覽
UIS 的 UI 系統採用高度模組化的設計,大量依賴元件、Prefab 和 ScriptableObject,讓你可以靈活地客製化每一個 UI 細節。四個最核心的類別是:
- Item View:顯示物品的視覺元件。
- Item View Slot:偵測點擊、選取、拖放的互動容器。
- Item View Slots Container:管理多個 Item View Slots 的容器(Inventory Grid、Item Hotbar、Equipment Panel 等)。
- Display Panel:控制 UI 面板的開啟與關閉。
Display Panel & Manager
Display Panel 和 Manager 系統讓你可以完整控制面板的開啟/關閉順序與回滾邏輯。每次開啟面板時,可以傳入前一個面板的參照,關閉時會自動回到前一個面板。
Display Panel Manager
Display Panel Manager 管理其子物件中的所有 Display Panels,確保同一時間只有一個 Menu Panel 開啟。
Display Panel Manager API
// 以 ID 從 ISM 取得 Display Panel Manager
var displayPanelManager = InventorySystemManager.GetDisplayPanelManager(ID);
// 取得 Panel Owner(通常是角色 GameObject)
var panelOwner = displayPanelManager.PanelOwner;
// 以名稱取得面板
var panel = displayPanelManager.GetPanel(panelName);
// 開啟面板(使用目前選取的面板作為前一個面板)
displayPanelManager.OpenPanel(panel);
// 取得目前選取的面板和選單
var selectedPanel = displayPanelManager.SelectedDisplayPanel;
var selectedMenu = displayPanelManager.SelectedDisplayMenu;
// 關閉目前選取的面板
displayPanelManager.CloseSelectedPanel();
// 監聽面板開啟/關閉事件
EventHandler.RegisterEvent<OpenClosePanelInfo>(m_PanelOwner,
EventNames.c_GameObject_OnPanelOpenClose_OpenClosePanelInfo,
HandlePanelOpenedOrClosed);
Display Panel 屬性
| 屬性 | 說明 |
|---|---|
| Unique Name | 讓 Display Panel Manager 建立名稱到面板的字典,方便從程式碼任意位置存取。 |
| Is Menu Panel | 設為 true 時作為 Menu,同一時間只能有一個 Menu 開啟。 |
| Is Non Selectable | 開啟/關閉時不更改 Panel 鏈(適合 Tooltip 類面板)。 |
| Start Enabled | 預設所有面板以停用狀態開始,勾選此項可使面板從啟用狀態開始。 |
| Open on Start | 類似 Start Enabled,但會在啟動時觸發 OnOpen 事件。 |
| Set Active On Open | 開啟時設定 GameObject 為 active。 |
| Set Disable On Close | 關閉時設定 GameObject 為 inactive。 |
| Selectable On Open | 面板開啟後自動選取的 UI 元素。 |
| Main Content | UI Designer 用來知道 Prefab 應生成在哪個 Transform 下。 |
| Bindings | Panel Bindings 會在同一 GameObject 上自動找到。面板的 Open/Close 等事件都由 Bindings 處理。 |
Display Panel API
// 透過 Panel Manager 的上下文開啟/關閉(推薦)
panel.SmartOpen();
panel.SmartClose();
// 取得面板狀態
DisplayPanel previousPanel = panel.PreviousePanel;
Selectable prevSelectable = panel.PreviousSelectable;
bool isOpen = panel.IsOpen;
var panelManager = panel.Manager;
string panelName = panel.UniqueName;
// 指定前一個面板和 Selectable 開啟
panel.Open(previousPanel, previousSelectable);
// 關閉(true = 關閉後選取前一個面板)
panel.Close(true);
Item View Slots Container
Item View Slots Container 是 Inventory Grid、Item Hotbar、Item Slot Collection View 等元件共用的基礎類別,提供統一的與 Item View Slots 互動方式:選取、點擊、移動、新增、移除和交換。
核心屬性
| 屬性 | 說明 |
|---|---|
| Container Name | 用來區分不同的 Container,在 Drop Action 條件中特別有用。 |
| Slot Cursor | 參照 Item View Slot Cursor Manager(選用)。 |
| Item View Drawer | 依物品類別選擇對應的 Item View Prefab 來顯示。Container 的 Content Transform 和 Drawer 的 Content Transform 必須指向同一個物件。 |
| Content | 包含所有 Item View Slots 的父 Transform。 |
Item View Slots Container API
// 監聽 Item View Slot 事件
m_ItemViewSlotContainer.OnItemViewSlotSelected += HandleSelected;
private void HandleSelected(ItemViewSlotEventData slotEventData) {
var itemInfo = slotEventData.ItemViewSlot.ItemInfo;
}
m_ItemViewSlotContainer.OnItemViewSlotClicked += HandleClicked;
m_ItemViewSlotContainer.OnItemViewSlotEndDragE += HandleEndDrag;
// 取得所有 Item View Slots
var slots = m_ItemViewSlotContainer.ItemViewSlots;
// 新增、移除、移動物品
m_ItemViewSlotContainer.AddItem(itemInfo, slotIndex);
m_ItemViewSlotContainer.RemoveItem(itemInfo, slotIndex);
m_ItemViewSlotContainer.MoveItem(sourceIndex, destinationIndex);
// 取得特定槽位的物品
var slot = m_ItemViewSlotContainer.GetItemViewSlot(slotIndex);
var itemView = m_ItemViewSlotContainer.GetItemView(slotIndex);
var itemInfo = m_ItemViewSlotContainer.GetItemAt(slotIndex);
// 選取槽位
m_ItemViewSlotContainer.SelectSlot(slotIndex);
var selected = m_ItemViewSlotContainer.GetSelectedSlot();
// 手動重繪 Container
m_ItemViewSlotContainer.Draw();
Item View Slots Container Bindings(附加元件)
Inventory Grid
Inventory Grid 是 Item View Slots Container 的子類別,整合了格子系統的 Tab 切換、導航和高效捲動機制。格子使用固定數量的按鈕,透過映射物品索引到格子視圖索引來實現高效捲動,不需要為每個物品都建立 UI 元素。
Inventory Grid 可以顯示玩家庫存、商店庫存、倉庫等各種 Inventory 的內容。使用 UI Designer 的「Inventory Grid」Tab 可以快速建立和設定。
必要元件
- Item Info Grid:包含所有格子資訊,定義格子何時以及如何刷新。
- Grid Event System:偵測按鈕的所有事件(選取、點擊等),並在 Unity Event System 嘗試超出格子邊界時觸發事件,讓格子可以捲動或切換 Tab。
- Item View Drawer:依物品類別選擇正確的 Item View Prefab。
Item Info Grid 重要欄位
| 欄位 | 說明 |
|---|---|
| Grid Size | 格子大小,初始化後執行期間不會改變。 |
| Set Real Element Count As Max | true:格子元素數量隨物品增減動態調整;false:使用 Max Element Count 的固定值。 |
| Max Element Count | 格子可顯示的最大元素數量。 |
| Disable Element Option | 超出上限的 Item Slots 的隱藏方式選項。 |
| Grid Navigator | 格子導航器,支援捲動、分頁、用 Scroll View 導航等。 |
Inventory Grid API
// 取得格子 ID
var gridID = m_InventoryGrid.GridID;
// 設定 Inventory
m_InventoryGrid.SetInventory(inventory);
// 對格子中的物品排序
m_InventoryGrid.SortItemIndexes(itemInfoComparer);
// 綁定篩選器/排序器
m_InventoryGrid.BindGridFilterSorter(itemInfoSorterFilter);
Item Shape Grid
Item Shape Grid 允許物品在有限的格子中佔用多個(任意形狀的)格位,類似《暗黑破壞神》風格的背包系統。它與一般 Inventory Grid 在架構上完全不同,需要 Inventory 本身持有格子資料。
雙層結構
- Background Layer:顯示每個格子的選取或拖放預覽(透過顏色濾鏡)。點擊事件在此層處理。
- Foreground Layer:顯示依物品形狀縮放的物品圖示,此層不可點擊(點擊穿透到 Background Layer)。
Item Shape 屬性
物品的形狀由 Shape 屬性(類型為 ItemShape)定義,使用布林值格子加上一個錨點(錨點必須在設為 true 的格子上)。可設定在 Item Category 或 Item Definition 屬性上。
核心元件
Item Shape Grid 專用的 Item View Modules
| Module | 說明 |
|---|---|
| Item Shape Item View | 依物品形狀正確縮放並顯示圖示;可依層(Background/Foreground)啟用/停用不同 GameObject。 |
| Item Shape Drop Preview Item View | 拖放時預覽物品能否放置(改變 Background Layer 格子的顏色濾鏡)。 |
| Item Shape Selected Item View | 選取時在 Background Layer 對應格子加上顏色標示。 |
| Item Shape Rect Place Item View | 在不規則形狀的物品中,精確定位數量顯示文字的位置(例如放在右下角)。 |
| Canvas Group | 控制圖示的顯示/隱藏和互動狀態(Foreground Layer 不可互動時用)。 |
Item Hotbar
Item Hotbar 使用 Item View Slots Container 顯示物品,並允許透過 Item Action 或拖放來指派物品至槽位。Item Hotbar 透過事件從 Inventory Input 接收輸入,可以在每次綁定的 Inventory 更新時刷新,保持物品數量和狀態的最新顯示。
三種內建 Hotbar 類型
Item Hotbar API
// 指派物品至槽位
hotbar.AssignItemToSlot(itemInfo, itemSlotIndex);
// 使用槽位中的物品
hotbar.UseItem(itemSlotIndex);
Item Slot Collection View(Equipment Panel)API
// 以槽位名稱取得 Item View Slot
var headSlot = m_ItemSlotCollectionView.GetItemViewSlot("Head");
// 取得 Item View Slot 對應的 Item Slot
var headItemSlot = m_ItemSlotCollectionView.GetItemSlot(headSlot);
// 綁定 Inventory 至 Item Slot Collection View
m_ItemSlotCollectionView.SetInventory(m_Inventory);
拖放系統(Move Items / Drag & Drop)
物品可以透過滑鼠拖放或 Item View Slot 的 Move Cursor(鍵盤/手把)在 Item View Slots Containers 之間移動。系統由以下核心元件組成:
Item View Slot Drop Action Set
建立路徑:
Create → Ultimate Inventory System → UI → Item View Slot Drop Action Set
內建放置條件(Drop Conditions)
| 條件 | 說明 |
|---|---|
| Drop Container Can Add | 檢查來源/目標 Container 是否可以接受交換的物品。 |
| Drop Container Can Give | 檢查 Container 是否可以將物品給予另一個 Container。 |
| Drop Container Can Move | 檢查物品是否可以在同一 Container 內移動索引。 |
| Drop Container Has Name | 檢查來源/目標 Container 的名稱是否符合指定名稱。 |
| Drop From Item Collection | 檢查來源 Item Collection 是否符合指定的 Item Collection。 |
| Drop Item Amount Condition | 比較來源物品數量是否符合最小/最大值。 |
| Drop Null Item | 檢查來源/目標物品是否為 null。 |
| Drop Same Container | 檢查兩個 Item View Slots 是否來自同一個 Container。 |
| Drop Container Can Smart Exchange | 智慧交換條件,考慮多種情境。 |
| Item View Shape Drop | 使用 Item Shape Grid 時,檢查物品形狀是否可以放入目標位置。 |
| 動作 | 說明 |
|---|---|
| Drop Action To Item | 從 Drop Action 觸發一個 Item Action。 |
| Drop Container Exchange | 在兩個 Container 之間交換物品(移除再新增)。 |
| Drop Container Give | 將物品從來源給予目標,可選擇是否移除/新增。 |
| Drop Container Smart Exchange | 智慧交換,考慮多種情境。 |
| Drop Inventory Exchange | 同 Container Exchange,但直接操作 Inventory(繞過 Item View Slots Container)。 |
| Drop Inventory Give | 同 Container Give,但直接操作 Inventory。 |
| Drop Move Index | 在同一 Container 內移動物品的 Stack 索引。 |
| Drop Spawn Item Object | 以放下的物品生成一個 Item Object。 |
| Item View Shape Drop | 使用 Item Shape Grid 的函式放置物品,考慮物品形狀。 |
自訂 Drop 條件與動作
// 自訂條件:檢查來源和目標是否在同一 Container
[Serializable]
public class ItemViewDropSameContainerCondition : ItemViewDropCondition
{
public override bool CanDrop(ItemViewDropHandler handler)
=> handler.SourceContainer == handler.DestinationContainer;
}
// 自訂動作:移動物品在 Container 中的索引
[Serializable]
public class ItemViewDropMoveIndexAction : ItemViewDropAction
{
public override void Drop(ItemViewDropHandler handler)
{
handler.SourceContainer.MoveItem(
handler.StreamData.SourceIndex,
handler.StreamData.DestinationIndex);
}
}
Filter & Sorters
Item Info Filter 和 Sorter 用於 Item Info Grid、Inventory Grid 等元件,對物品清單進行篩選和排序。可在 UI Designer 中以下拉選單輕鬆新增,自訂的 Filter/Sorter 也會自動出現在清單中。
內建 Filter & Sorter
| 名稱 | 說明 |
|---|---|
| Inventory Search Filter | 透過輸入欄位搜尋物品名稱,同時套用指定的排序器。 |
| Item Info Category Filter | 依 Item Category 篩選。 |
| Item Info Item Collection Filter | 依所在 Item Collection 篩選。 |
| Item Info Amount Sorter | 依數量排序。 |
| Item Info Attribute Value Sorter | 依屬性值排序(屬性值需實作 IComparable)。 |
| Item Info Category Name Sorter | 依 Item Category 名稱排序。 |
| Item Info Name Sorter | 依物品名稱排序。 |
| Item Info Multi Filter Sorter | 組合多個 Filter/Sorter,依序套用(順序重要)。 |
自訂 Filter / Sorter 範例
// 多重篩選器(組合多個 Filter Sorter)
public class ItemInfoMultiFilterSorter : ItemInfoFilterSorterBase
{
[SerializeField] internal List<ItemInfoFilterSorterBase> m_GridFilters;
public override ListSlice<ItemInfo> Filter(ListSlice<ItemInfo> input, ref ItemInfo[] outputPooledArray)
{
var list = input;
foreach (var filter in m_GridFilters)
list = filter.Filter(list, ref outputPooledArray);
return list;
}
public override bool CanContain(ItemInfo input)
{
foreach (var filter in m_GridFilters)
if (!filter.CanContain(input)) { return false; }
return true;
}
}
// 依名稱排序器
public class ItemInfoNameSorter : ItemInfoSorterBase
{
[SerializeField] protected bool m_Ascending = false;
protected Comparer<ItemInfo> m_ItemNameComparer;
public override Comparer<ItemInfo> Comparer => m_ItemNameComparer;
protected override void Awake()
{
base.Awake();
m_ItemNameComparer = Comparer<ItemInfo>.Create((i1, i2) => {
if (i1.Item == null && i2.Item == null) { return 0; }
if (i1.Item == null) { return 1; }
if (i2.Item == null) { return -1; }
return m_Ascending
? i2.Item.name.CompareTo(i1.Item.name)
: i1.Item.name.CompareTo(i2.Item.name);
});
}
}
// 依 Item Collection 篩選器
public class ItemInfoItemCollectionFilter : ItemInfoFilterBase
{
[SerializeField] protected ItemCollectionID[] m_ShowItemCollections;
[SerializeField] protected ItemCollectionID[] m_HideItemCollections =
{ ItemCollectionPurpose.Loadout, ItemCollectionPurpose.Hide };
public override bool Filter(ItemInfo itemInfo)
{
var show = m_ShowItemCollections.Length == 0;
foreach (var id in m_ShowItemCollections)
if (id.Compare(itemInfo.ItemCollection)) { show = true; break; }
if (!show) { return false; }
foreach (var id in m_HideItemCollections)
if (id.Compare(itemInfo.ItemCollection)) { return false; }
return true;
}
}
Views 系統
View 和 View Module 類別提供了高度可擴展的 UI 顯示架構。View 是一個泛型基礎類別,Item View 繼承自 View,負責將操作(Clear、Select、Click、Hide、SetValue、Refresh 等)傳遞給其所有的 View Module 子元件。
View Module 元件每個只負責一件事,多個 Module 可以在同一個 Prefab 上組合使用。這種設計讓 View Module 極易撰寫且高度可重用。
Item View Drawer & Category Item View Set
Item View Drawer 配合 Category Item View Set(ScriptableObject)運作,將 Item Category 映射至對應的 Item View Prefab。這讓 View Drawer 知道該使用哪個 Prefab 來顯示特定物品。Drawer 也提供了「繪製前」和「繪製後」的事件,例如商店選單可以監聽「繪製後」事件,顯示依商店或角色計算的特殊價格。
內建 Item View Modules
| Module | 說明 |
|---|---|
| Name Item View | 顯示物品名稱。 |
| Icon Item View | 顯示物品的 Icon 屬性圖示。 |
| Amount Item View | 顯示庫存中的物品數量。 |
| Select Image View | 依選取狀態改變 Image 的 Sprite。 |
| Equipped Select Item View | 依物品所在的 Item Collection(是否已裝備)改變圖示。 |
| Int Attribute Item View | 顯示整數屬性的值。 |
| Item Slots Item View | 依物品所在 Item Collection 啟用/停用 GameObjects。 |
| Item Shape Item View | Item Shape Grid 專用,依形狀顯示物品圖示。 |
| Cooldown Item View | 顯示 Use Item Action Set Attribute 物品動作的冷卻時間。 |
自訂 Item View Module 範例
/// <summary>顯示物品數量的 Item View Module。</summary>
public class AmountItemView : ItemViewModule
{
[SerializeField] protected Text m_AmountText;
[SerializeField] protected bool m_HideAmountIfSingle;
public override void SetValue(ItemInfo info)
{
m_AmountText.text = (m_HideAmountIfSingle && info.Amount <= 1)
? ""
: $"x {info.Amount}";
}
public override void Clear() { m_AmountText.text = ""; }
}
特殊 Item View Module 介面
| 介面 / 抽象類別 | 說明 |
|---|---|
| IInventoryDependent | 讓 Module 可以參照外部 Inventory(例如數量比較用)。 |
| IViewModuleSelectable | 接收 Item View 的 Select 事件。 |
| IItemViewSlotDropHoverSelectable | 接收 Drop Handler 的 Hover 事件,可顯示放置預覽。 |
Item Description & Attribute View
Item Description
Item Description 是一種特殊的 Item View,專門顯示物品的詳細說明。可以搭配任何 Item View Module,最常見的搭配是 Category Attribute View Set Item View module——依據被描述的物品動態生成對應的 Attribute Views。
Item Description 有三種綁定方式:
- Container 綁定:以 Item View Slots Container Description Binding 元件綁定至任何 Item View Slots Container,顯示目前選取槽位的物品說明。
- Panel 綁定:以 Item Description Panel Binding 元件綁定至 Display Panel,確保說明面板在使用前完成初始化。
- Tooltip:使用 Item View Slot Panel To Tooltip 將 Item Description 作為 Tooltip 使用。
Attribute View
Attribute View 的設計與 Item View 相同,但用來顯示屬性值。通常在 Item Description 中使用。內建的 Attribute View Modules:
| Module | 說明 |
|---|---|
| Float Value Attribute Box | 顯示 float 屬性,可自訂小數位數(Format String)。 |
| Int Value Attribute Box | 顯示整數屬性。 |
| String Attribute Box | 顯示字串屬性。 |
| Name Value Attribute Box | 顯示屬性名稱和值(支援任意物件類型)。 |
Recipe View
Recipe View 的設計與 Item View 相同,但用來顯示 Crafting Recipe。內建的 Recipe View Module:
- First Output Recipe View:參照 Item Box 物件,顯示配方的第一個輸出結果。
Currency View & Monitor
Multi Currency View
Multi Currency View 將多種 Currency 對應至各自的 Currency View 元件。設定 Currency Amount 或 Currency Collection 後,會自動顯示每種貨幣的數量。
// 依 Currency Collection 繪製貨幣
m_MultiCurrencyView.DrawCurrency(currencyCollection);
// 或依 Currency Amount 陣列繪製
m_MultiCurrencyView.DrawCurrency(currencyAmounts);
// 設定文字顏色(例如:購物時資金不足顯示紅色)
m_MultiCurrencyView.SetTextColor(color);
Currency View
// 設定要顯示的貨幣數量
m_CurrencyView.SetValue(currencyAmount);
Currency Owner Monitor
Currency Owner Monitor 使用 Multi Currency View 顯示一個 Currency Owner 的貨幣數量,可透過 Inventory Identifier ID 自動找到 Currency Owner。
// 設定要監視的 Currency Owner
m_CurrencyOwnerMonitor.SetCurrencyOwner(currencyOwner);
Save View
Save View 的設計與 Item View 相同,由 Save Menu 用來顯示 Save Data Info。若使用自訂 Save Meta Data,可以建立自訂 Save View Modules 來客製化 Save Menu 中顯示的資訊。
- 第 1 篇:入門指南與基本概念
- 第 2 篇:編輯器與資料庫設定
- 第 3 篇:物品系統核心
- 第 4 篇:屬性、貨幣、商店與合成系統
- 第 5 篇:輸入處理、存檔、互動與 UI 框架
- 第 6 篇:進階功能與第三方整合