最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Forge模組開發(fā)-容器類詳解

2023-03-04 12:33 作者:輕描淡寫的呵呵  | 我要投稿


引言


首先仍是了解什么是容器類,容器并非物品欄,而是將物品欄呈現(xiàn)給玩家的一個工具:

注冊容器:例如ContainerType<ChestContainer> GENERIC_9X1這是箱子容器類型,也就是上面9個格子,下面玩家背包的容器類型,使用容器類ChestContainer注冊生成用以判斷類別和參與游戲功能;

容器類:是用于生成容器實例的類,例如,ChestContainer extends Container,容器實例最主要功能是操作物品欄(IInventory)物品增刪改查,將物品欄以界面形式展現(xiàn)在玩家面前方便操作;

物品欄:實現(xiàn)了IInventory接口的類所生成的實例,是真正儲存ItemStack對象的地方,容器類操作的就是該對象。

?

本文要介紹的內(nèi)容包括Container類基本要求和有用內(nèi)容、Slot類基本要求和有用內(nèi)容,IInventory的某些典型實現(xiàn)的分析,在后續(xù)講完TileEntity后,我會講出他們之間的關(guān)系以及為什么Inventory是接口。

?

Container類


構(gòu)造器:protected Container(@Nullable ContainerType<?> type, int id)

容器類構(gòu)造器內(nèi)容很少,但不意味著我們繼承后寫的構(gòu)造器也這么簡單,我們的構(gòu)造器需要至少兩個,且滿足幾個步驟:

?

第一個構(gòu)造器,稱為注冊構(gòu)造器,CustomContainer(int id, PlayerInventory playerInventory),傳入id、玩家物品欄,意味著這兩個參數(shù)必需需要我們外部獲取,其內(nèi)部功能只需要調(diào)用第二個構(gòu)造器,將第二個構(gòu)造器需要的參數(shù)合理new出即可,不需要有實際意義。

?

第二個構(gòu)造器,稱為實例構(gòu)造器,CustomContainer(int id, PlayerInventory playerInventory , IInventory inventory, IIntArray intarry):

第一步,super調(diào)用父類構(gòu)造器,也就是上述構(gòu)造器,將注冊容器type、分配的注冊id傳入,分別寫入對應(yīng)成員變量,所以,我們的構(gòu)造器需要從容器類型注冊類獲取用我們這個容器類注冊的容器類型type,看起來有點首尾銜接死循環(huán)了,實際上是分部分錯開使用的,故而并不會出錯,不必深究,在注冊解析區(qū)會進行詳細說明,而第二個參數(shù)id則是由Forge容器管理系統(tǒng)分配的,也不必深究,此處只需知道我們使用時都可以獲得即可;

第二步,校驗形參合法性,例如assertInventorySize(inventory, 8);意思是校驗傳入的物品欄inventory是否至少有8個位置可以存放物品,除了前三個基本形參外,還可以傳入如intarry這樣數(shù)字數(shù)組類型的信息對象的額外信息參數(shù),方便實現(xiàn)更多功能,同樣,都需要校驗合理性;

第三步,將校驗好的物品欄對象、數(shù)字數(shù)組信息對象放入我們寫的對應(yīng)的成員變量中例如private final IIntArray data;與this.data = intarry;;

第四步,由于forge(mojang?)只提供了IIntArray 的同步器,所以額外信息對象一般是數(shù)字類型的,所以我們第三步是將該信息對象所在的成員變量使用trackIntArray(this.data);開始進行同步;

第五步,也是格外重要一步,為容器添加物品槽,物品槽構(gòu)造參數(shù)分為(物品欄,物品欄索引物品,x、y在容器的相對位置,其他需要的參數(shù)...),后面細講物品槽;

?

這兩個構(gòu)造器何時使用我們在講方塊實體和容器注冊時會說明。

?

必要覆寫方法


ItemStack transferStackInSlot(@NotNull PlayerEntity playerIn, int index)

在玩家使用該容器在背包inventory和目標inventory之間轉(zhuǎn)移物品時調(diào)用,例如箱子界面中,shift點擊上方物品槽,該物品槽內(nèi)物品應(yīng)當執(zhí)行怎樣的移動邏輯,查看ChestContainer的該方法可以輕易了解如何編寫,注意實際發(fā)生物品轉(zhuǎn)移后,要記得將對應(yīng)inventory標記為待更新;

boolean canInteractWith(@NotNull PlayerEntity playerIn)

根據(jù)需要不同,重寫此方法,可以借此上容器鎖,也可以實現(xiàn)其他用處;

?

非必要但覆寫有用方法


onContainerClosed(PlayerEntity playerIn)

在容器關(guān)閉時執(zhí)行的方法,重寫時應(yīng)當注意要super調(diào)用父方法,防止正常游戲線出現(xiàn)問題

onCraftMatrixChanged(IInventory inventoryIn)

容器內(nèi)出現(xiàn)合成時間后調(diào)用,需要檢測變化,執(zhí)行對應(yīng)功能并同步數(shù)據(jù)

calcRedstone(@Nullable TileEntity te)與calcRedstoneFromInventory(@Nullable IInventory inv)

紅石比較器比較容器時調(diào)用,至于為什么第一個傳入了方塊實體,講到方塊實體再說

?

還有其他能重寫的方法,但是用處不大,需要自行研究吧。

?

Slot類


構(gòu)造器:public Slot(IInventory inventoryIn, int index, int xPosition, int yPosition)

this.inventory = inventoryIn;

this.slotIndex = index;

this.xPos = xPosition;

this.yPos = yPosition;

沒啥好講,初始化參數(shù),對應(yīng)容器類構(gòu)造器二的第五步,每個物品槽都要具體設(shè)置,不論是循環(huán)還是其他方法。一般情況下一個槽對應(yīng)一個物品欄inventory的一個索引位和索引位內(nèi)的非空物品(AIR空氣也是物品)。

?

建議重寫方法


boolean isItemValid(ItemStack stack)

返回true,代表傳入的stack允許置入,例如容器的產(chǎn)物槽,不論輸入什么都會返回false

ItemStack onTake(PlayerEntity thePlayer, ItemStack stack)

在取物品后執(zhí)行的方法,一般用來寫統(tǒng)計信息,例如獲得了xxx物品數(shù)量

int getSlotStackLimit()

槽位堆疊限制,與物品欄堆疊限制同時作用

boolean canTakeStack(PlayerEntity playerIn)

是否能從槽位取走物品,例如創(chuàng)造物品欄的物品就無法取出

?

物品槽最實用的方法是isItemValid,而且槽位實例允許你儲存一些信息,例如我在CustomSlot構(gòu)造器中增加一個形參Item item,在容器構(gòu)造器中指定為Items.BOOK,然后在該方法中判斷傳入的物品是否是構(gòu)造器中傳入的item,來判斷能不能塞入;

?

所以我們可以看到,容器構(gòu)造器二的第五步是例如箱子:

for(int i1 = 0; i1 < 9; ++i1) {

???this.addSlot(new Slot(playerInventoryIn, i1, 8 + i1 * 18, 161 + i));

}

使用了一個循環(huán),將玩家背包的手持物品欄(九個)分別綁定到了九個new Slot上。

?

IInventory類


為了統(tǒng)一調(diào)用,提供了一個IInventory接口供實現(xiàn)物品欄類,其中最典型的實現(xiàn)則是Inventory,但是此處并不是講的Inventory,而是IInventory;

接口自然沒有什么構(gòu)造器要講,但接口方法也就要求了具體實現(xiàn)的構(gòu)造器的功能;

?

接口方法


int getSizeInventory();

獲取容器大小,一般對應(yīng)物品欄實際儲存物品的非空物品堆list大小

boolean isEmpty();

物品欄是否為空

ItemStack getStackInSlot(int index);

獲取索物品欄引位置物品堆

ItemStack decrStackSize(int index, int count);

將槽位中的物品堆分堆,形成兩個ItemStack實例,返回需要的實例,一般調(diào)用ItemStackHelper.getAndSplit方法即可

ItemStack removeStackFromSlot(int index)

從物品欄索引位index移除物品堆,返回該被移除的物品堆

setInventorySlotContents(int index, @NotNull ItemStack stack)

設(shè)置物品欄對應(yīng)索引位index的物品堆為stack

int getInventoryStackLimit()

返回物品欄的每個物品堆最大堆疊

void markDirty();

標記為待更新,用于從服務(wù)端同步數(shù)據(jù)到客戶端,需要自行在合適位置調(diào)用,防止更新不及時導致客戶端動效或者信息不更新或者滯后

boolean isUsableByPlayer(PlayerEntity player);

是否可用,建議不在這寫,當然根據(jù)自己需要寫

void openInventory

并非是真正的打開物品欄方法,物品欄其實是打不開的,只能借助容器才能“打開”,這個方法是用來執(zhí)行一些信息統(tǒng)計、改變、更新,調(diào)起對應(yīng)事件用的。

void closeInventory(PlayerEntity player)

同上,應(yīng)當逆序打開的信息改變或者自行設(shè)計

boolean isItemValidForSlot(int index, ItemStack stack)

后檢測,在slot校驗合法通過過還要執(zhí)行此方法校驗,判斷要存入的物品是否能存入,到此部分,一般是在塞入物品關(guān)閉容器后扔出物品而不是點擊不進去物品

int count(Item itemIn)

查詢某類型物品數(shù)量

boolean hasAny(Set<Item> set)

是否有某類型物品數(shù)量

?

為什么將IInventory設(shè)計為接口,是為了任何實現(xiàn)他的實例都可以被認作物品欄,故而很多時候,會讓方塊實體實現(xiàn)IInventory接口,這樣他既是一個功能實體,又可以很方便地儲存、調(diào)用想要的物品實現(xiàn)他的功能,方塊實體章節(jié)細講;

?

Inventory則是IInventory的標準實現(xiàn),如果沒有額外需求,直接new出一個該對象即可新建一個物品欄,也可以研究他的具體方法實現(xiàn),來編寫自己的物品欄,為什么不講其他實現(xiàn)的物品欄類型呢,因為我們發(fā)現(xiàn),物品欄一般是依附方塊或者實體的,單獨將屬實沒意義,所以我們后續(xù)再講。

?

有關(guān)容器注冊


從講容器構(gòu)造器得知我們寫了兩個構(gòu)造器,一個是注冊用,一個是實例生成用,顯然,我們這時候要用構(gòu)造器一,這時候該構(gòu)造器只是創(chuàng)建了一個空殼容器對象。

第一步 仍然要新建一個容器注冊類,寫一靜態(tài)成員:

public static final DeferredRegister<ContainerType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.CONTAINERS,MODID);

第二步?進行注冊

public static final RegistryObject<ContainerType<容器類>> 容器類型名稱=

CONTAINERS.register("類型鍵名", () ->

????????????IForgeContainerType.create(

????????????????????(int windowId, PlayerInventory inv, PacketBuffer data) ->{

????????????????????????return new 容器類(windowId, inv);

}));

其中windowId是分配好的數(shù)字id,可以用此讓服務(wù)端發(fā)包,指揮客戶端打開界面,方便同步兩端寫入物品欄數(shù)據(jù)并展示,inv是留空的玩家物品欄,容器的打開者必然是一個玩家在操作,物品欄是要傳入,但是你也可以不用,用的話例如箱子,將玩家物品欄渲染在下方,不用的話例如自己寫,data按道理是服務(wù)端發(fā)送打開容器包時附帶的信息,例如容器位置啊等,實際上哥們沒使用成功過,所以哥們本人不會,但是吧,你可以自己寫網(wǎng)絡(luò)通信代替這部分,效果是一樣的(親測可行)。

?

有關(guān)容器使用


使用是建立在注冊成功上的,我在這先只講虛空彈出容器界面的方法,至于該在什么地方彈出,我會在方塊實體部分講一種用法,這只講怎么彈;

首先起點是服務(wù)端代碼,你要有的參數(shù)是:服務(wù)端玩家對象、要打開的容器實例

僅此二者即可,其中服務(wù)端玩家對象就是對應(yīng)的要打開容器的玩家,看到該界面的玩家就是對應(yīng)的客戶端玩家實例,這個很容易理解,那么要打開的容器實例看下面;

要打開的容器實例需要new出,使用的則是我們寫的第二個構(gòu)造器,構(gòu)造參要求的id先不說,玩家物品欄,自然是傳入該玩家或者你想的話其他玩家也行;物品欄,例如我獲取了一個箱子的物品欄(虛空)就可以傳入;數(shù)據(jù),自行發(fā)揮此處示例不傳。

這樣我們就有了一個有意義的容器,我們肯定要包裝我們自己的打開物品欄方法的:

public void openExtraInventory(ServerPlayerEntity spe,IInventory inv) {

if (spe.openContainer != spe.container) {

? ? ?spe.closeScreen();

}

?????spe.getNextWindowId();

?????spe.openContainer = new CustomChestContainer(spe.currentWindowId, spe.inventory, inv);

?????spe.connection.sendPacket(new SOpenWindowPacket(spe.currentWindowId, ContainerTypeRegistry.customchestContainer.get(), new TranslationTextComponent("額外背包")));

?????spe.sendContainerToPlayer(spe.openContainer);

}

上述方法,步驟分為:

界面檢測,如果有其他界面,進行關(guān)閉

窗口Id自增,表示新的窗口要來了,方便同步數(shù)據(jù)

設(shè)置服務(wù)端玩家要打開的容器,容器是new出的,這時候我們就有了剛剛沒講的id了

獲取玩家鏈接向客戶端玩家發(fā)送打開窗口包,指定id,容器類型,以及我們預(yù)設(shè)好的容器界面的文字框內(nèi)展示的文字

然后立馬將對應(yīng)容器實例發(fā)過去,同步數(shù)據(jù),該方法有兩個數(shù)據(jù)包,分別是物品堆信息和槽位信息,這樣我們發(fā)現(xiàn),我們發(fā)過去的三個包很契合我們本文講的三個內(nèi)容,容器、物品欄、槽位,至此,方法結(jié)束,本文也就結(jié)束了。

?

至于這個方法什么時候調(diào)用,調(diào)用限制,這都屬于自由發(fā)揮,等cv模組策劃完善后,或許出一節(jié)額外背包教程,讀者就會了解到安全的額外背包了。

?


Forge模組開發(fā)-容器類詳解的評論 (共 條)

分享到微博請遵守國家法律
偃师市| 兴业县| 屏东县| 稻城县| 正定县| 贵南县| 垣曲县| 龙山县| 临澧县| 清原| 蒲江县| 陆丰市| 淳安县| 穆棱市| 济源市| 鄄城县| 嵊泗县| 泰宁县| 扶余县| 阿巴嘎旗| 城步| 色达县| 永和县| 明光市| 盖州市| 林甸县| 民丰县| 丹巴县| 澄城县| 南通市| 英山县| 宝坻区| 丰原市| 夏邑县| 界首市| 收藏| 肇庆市| 灌云县| 永川市| 勐海县| 日土县|