# 物品拾取/互動/背包系統 https://github.com/Cabris/ProjectGZ {%youtube EooB9iTYJ4I %} ### 需求: 透過GAS的能力系統以及資料驅動來達到可在編輯器階段加入各種可互動物件的機制 ### 架構設計: #### 物品系統: 物品由兩個class組成: * InventoryItemDefinition * InventoryItemInstance InventoryItemDefinition: 定義物品的靜態狀態,例如icon圖片、Mesh模型、名稱、預設數量以及InventoryItemInstance的Type。 InventoryItemDefinition通常只會用到其CDO,因為它是用來定義"一種"物品,多個同種物品會共用同一個InventoryItemDefinition的CDO,所以只要修改編輯器中的InventoryItemDefinition BP子類,就可以調整所有的同種物件定義。 InventoryItemInstance: 保存runtime時的個別物品狀態,例如剩餘數量、耐久度,內含一個GameplayTag&int32的map來表示套用到特定物品實例的任意屬性值。 當物品被使用/消耗時都會去InventoryItemInstance內檢查/修改物品狀態數值,這讓物品實例只保存純數值的資料,處理網路同步/存讀檔時會比較乾淨跟節省資源。 互動: 互動(Interaction)是由一個主GameplayAbility: StartInterateAbility,跟多個實際行為定義InterateAbility組成。 StartInterateAbility透過初始化PlayerCharacter時Give到Player的ASC,Controller中綁定互動鍵來觸發能力。StartInterateAbility在OnGive的時候會啟動一個Timer Loop來檢查Plaer周圍的可互動Actor,透過Actor是否有實作IInteractable來判定,並會呼叫IInteractable::OnFocus, IInteractable::OnUnfocus來讓特製化可互動提示行為。 當互動鍵按下,觸發能力時會去根據當前的IInteractable::InteractTag的GameplayTag值去查ASC中可用的對應Ability。 InteractTag: Action.Interaction.Collect 對應到拾取物品能力: CollectItemAbility InteractTag: Action.Interaction.OpenDoor 對應到拾取物品能力: OpenDoorAbility ... Tag與Ability的對應是資料驅動,存在DataAsset中,可以在編輯器直接添加新的Tag/Ability對應Row,只要是繼承InterateAbility基類的BP都可以加入,c++層不需要知道互動能力的實作,基本上互動行為的定義都可以在BP層完成,GameplayTag也可以在編輯器直接添加,所以這個架構可以提升設計師的自由度而不必依賴程式修改。 #### 背包系統: CollectItemAbility會使用InventoryManagerComponent的AddItemDefToInventory方法,當中可以放一些是否可以放物品的判定邏輯跟防作弊的相關判定,若成功就把InventoryItemDefinition轉換成實際的InventoryItemInstance實例,並存到網路同步的Array:InventoryList中。 接著InventoryManagerComponent會透過delegate把背包更新事件廣播,對應的widget controller收到之後便會依據變化去更新背包UI widget內容,目前是使用TileView。 與Interact時類似,當玩家透過背包UI使用物品(消耗/裝備)時,會透過widget controller向InventoryManagerComponent呼叫對應方法,觸發物品Definition中定義的使用能力Tag去啟動對應的能力來使用,這也是這個架構的核心理念,透過GameplayTag與Ability的mapping來降低程式碼中對Ability細節的依賴。