十六、背包系統(tǒng)設(shè)計(jì)(嘗試分析的一些思考)

嘗試用面向數(shù)據(jù)的思維來分析,但本人對面向數(shù)據(jù)的理解還不夠深刻,因此這里只是一些思考。

功能描述:

  1. 有不同類型(法術(shù)、武器、藥水)和數(shù)量的道具槽,道具需裝備到槽內(nèi)才能使用。
  2. 支持同種道具的堆疊 ,如在一個藥水道具槽內(nèi)裝備100瓶生命回復(fù)藥。
  3. 在背包中保存獲取的道具,容量無限,可隨時從背包中裝備到道具槽內(nèi),或從道具槽中卸下

其實(shí),嚴(yán)格來講,道具槽感覺不應(yīng)該劃分在背包系統(tǒng)中,而是自己的一套。這里就先這樣吧。

定義數(shù)據(jù)


背包

“我”現(xiàn)在是背包,不管其他所有的系統(tǒng),為了完成職能,“我”需要知道哪些信息?

  • 我要把什么道具放進(jìn)背包? -------> item type( item name)
  • 背包里同種道具的數(shù)量是多少? ----------> int
  • 一種道具最大堆疊多少? --------------> int
  • 道具是否在背包內(nèi)/怎么將道具放進(jìn)、取出背包?------------> bool / ......

數(shù)據(jù)用來記錄游戲任一時刻的狀態(tài)??紤]某一時刻背包的狀態(tài):

  • 有哪些道具?-----------------> array/list/......
  • 每個道具的數(shù)量是多少?---------------->int

考慮數(shù)據(jù)之間的關(guān)聯(lián)性,我們可以得到一張表:

道具類型 背包中當(dāng)前擁有的數(shù)量 背包中可容納的最大數(shù)量
生命回復(fù)藥 1 10
戰(zhàn)斧 1 1
火球術(shù) 1 1

為了區(qū)分每一條記錄,我們要選取主鍵(必須唯一)。

方案一、

struct ItemData
{
    ItemType type;
    int itemCount;
    int itemMaxCount;
}
Array<ItemData> or List<ItemData> inventory; 

以前的我大概很快就會想到并決定這樣的數(shù)據(jù)布局,當(dāng)與外部系統(tǒng)交互時,就是struct ItemData的引用滿天飛了。
此時的主鍵是:道具類型 + 背包中當(dāng)前擁有的數(shù)量 + 背包中可容納的最大數(shù)量

方案二、

struct ItemData
{
    int itemCount;
    int itemMaxCount;
}
Map<ItemType, ItemData> inventory;

此時的主鍵是:道具類型


道具槽

按照上面的思路,“我”現(xiàn)在變成道具槽了,要知道的信息:

  • 道具槽的類型? -----> slot type
  • 道具槽是否被裝備? -----> bool
  • 裝備了什么道具? -----> item Type

那如果要有一堆道具槽要管理,那么我們就要為槽添加主key。

道具槽類型 是否被裝備 裝備的道具類型
藥水 生命回復(fù)藥
武器 戰(zhàn)斧
法術(shù) 火球術(shù)
藥水
法術(shù)

仔細(xì)看一上表,你會發(fā)現(xiàn)是否被裝備這一列是冗余的,于是:

道具槽類型 裝備的道具
藥水 生命回復(fù)藥
武器 戰(zhàn)斧
法術(shù) 火球術(shù)
藥水
法術(shù)

從上表中可以發(fā)現(xiàn),主鍵只能是:道具槽類型 + 裝備的道具

方案一:

struct ItemSlot
{
    SlotType slotType;
    ItemType itemType;
}
TArray<ItemSlot> slots;

當(dāng)我們要確定slot的唯一性時,是以struct ItemSlot為單元比較了。

方案二:
甲:我不想用Array,我想用Map?。?!
乙:OK。不過要加一個表項(xiàng)。

道具槽類型 索引 裝備的道具
藥水 0 生命回復(fù)藥
藥水 1
法術(shù) 0 火球術(shù)
法術(shù) 1
武器 0 戰(zhàn)斧

主鍵為:道具槽類型 + 索引

struct ItemSlot
{
  SlotType slotType;
  int slotNumber;
}

Map<ItemSlot,ItemType> slots;

背包 + 道具槽 + ......

這里我們很容易發(fā)現(xiàn)ItemType是兩個系統(tǒng)都有的數(shù)據(jù),因此可以借此將兩個系統(tǒng)的數(shù)據(jù)關(guān)聯(lián)起來。

如果要UI顯示背包中的道具,很明顯我們?nèi)鄙賵D標(biāo)資源。所以ItemType還要和圖標(biāo)資源建立聯(lián)系。但要注意的是背包的運(yùn)作并不要關(guān)心圖標(biāo)資源。

對于不同數(shù)據(jù)的關(guān)聯(lián):在ECS中,可利用entity來為不同的Component Data建立起邏輯關(guān)聯(lián);在面向?qū)ο缶幊讨?,我們通常定義一個大的Item類(確立了data的關(guān)聯(lián)性:同屬于一個類實(shí)例),然后將其傳入不同系統(tǒng),盡管系統(tǒng)只關(guān)注其中的一部分?jǐn)?shù)據(jù)。

ActionRPG的背包系統(tǒng)


看過之前code部分的同學(xué),應(yīng)該很容易就能理解。ActionRPG的背包系統(tǒng)是一個很典型的面向?qū)ο笤O(shè)計(jì)。

  • 有一個較大的ItemType類(會用這個類中數(shù)據(jù)的系統(tǒng)有:UI、資源管理、GAS、背包等等)
  • 背包的數(shù)據(jù)容器放在PlayerController的子類中,交互方法一部分放在接口Interface中,由PlayerController的子類實(shí)現(xiàn),另一部直接聲明為public方法。.
  • 其他系統(tǒng)(比如UI)與背包系統(tǒng)的交互的方法為綁定delegate,也就是消息傳遞。(因?yàn)镻layerController的實(shí)例是可以從全局訪問到的,因此獲取delegate的方法放在接口中)
/** Gets the delegate for inventory item changes */
    virtual FOnInventoryItemChangedNative& GetInventoryItemChangedDelegate() = 0;

    /** Gets the delegate for inventory slot changes */
    virtual FOnSlottedItemChangedNative& GetSlottedItemChangedDelegate() = 0;

    /** Gets the delegate for when the inventory loads */
    virtual FOnInventoryLoadedNative& GetInventoryLoadedDelegate() = 0;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容