原本我計劃將這部分內容放在稍後講解,但是由於這個部分遲遲沒有更新,所以我調整了本系列的教程計劃,現在就先來介紹這個"block state"。它可以存儲簡單的參數,用於方塊的功能擴展時會派上用場。然而,我們必須清楚,"block state"僅能存儲少量簡單的參數。之前我們提到過,在遊戲中,所有方塊都是同一個實例,因此通過"block state"為方塊添加參數實際上是為該方塊生成了多個不同的實例。
每個實例可能具有其中的某種參數。若使用"block state"存儲大量複雜的參數,將導致遊戲生成大量實例,嚴重影響遊戲性能。接下來舉個例子讓大家更直觀地感受一下。比如我們的石頭按鈕方塊,它具有三種屬性:面朝東南西北的四個方向、依附牆面、天花板和地板、以及是否被按下的激活狀態和未激活狀態,看起來參數很簡單,只有幾種可能。
即使參數組合很少,像這樣的按鈕總共有24種不同的可能性組合。因此,在遊戲中會生成24個石頭按鈕方塊實例,每個實例對應一種參數組合。如果參數再複雜一些,情況將變得更加棘手。要記住,"block state"只能存儲簡單少量的參數。接下來舉例說明。
我製作了一個模型,是一個開關門的小門,分別有打開和關閉兩種狀態,且是雙開門。這是同一個方塊內的雙開門,默認面向東。我需要將這個註冊到遊戲中。對於複雜方塊,我們需要專門使用一個方塊類,不能再使用傳統方式。
因此,我們新建一個Java文件,思考一下,這個方塊需要哪些參數。首先是面朝的方向參數,然後是開啟和關閉狀態參數。當然,如果想要更復雜一些,還可以考慮是否含水等參數,作為含水方塊的一個特性。但在演示教學中,我們就保持簡單,只設置面向和開啟狀態這兩個參數。
那麼,我們有一個帶有面向和是否打開參數的實體。我們需要在原始版本中稍作查找。我們將繼續瀏覽這個外部庫,並且將Neo Forge的冒號複製出來。找到其中的net.minecraft下的"Word level block"(譯為:世界級方塊)、"柵欄門"(Frigate fence gate)。這裡還有一些其他內容,但我們要重點關注我們需要的內容。
它包含了一個存儲柵欄門是否打開的參數,但似乎我們沒有看到它的朝向。實際上是因為它繼承了垂直面向方塊的類。我們點開進去,可以看到對於朝向這個參數是通過繼承垂直面向類的方式實現的。分別是東南西北四個方向。為了實現這兩種參數,我們需要繼承這個類,並且自定義一個屬性,總共兩種。
我們直接將這部分複製過來。然後再往下看,這裡有很多"voile sheep"的參數,看起來像是處理方塊碰撞體積的。我們還可以看到一些使用了"block box"的參數,總共有六個,用來確定兩個對角點,就像我們在模型中需要通過確定兩個對角點來判斷長方體的碰撞體積。此外,還有"shift point",給出了兩個參數,看起來像是要將兩個方塊拼合在一起。
讓我們也嘗試寫一下關於碰撞體積的部分,與柵欄門可能略有不同,所以我們也嘗試一下。首先是參數部分,我們將其複製過來。另外,有一個名為"ZSHOX7pro"的漏掉的部分。我們可以直接看到,它稍微比正常的矮一點。在遊戲中,我們可以觀察到,如果柵欄直接放在地上,它看起來並不高,但如果放東西在上面,它的高度會增加成一個完整的方塊大小。我們模型的高度是16,所以我們不考慮這個漏掉的部分,直接使用前面的兩個。再往下,發現除了"shift",還有一個"collision onship",即碰撞體積。
"Shift"通常指鼠標指向方塊時的體積,而"collision onship"表示碰撞體積。考慮到這是一個門,不應該隨意躍過,所以總共共有四個參數:Z、X和碰撞體積的參數。當門打開時,Z、X和碰撞體積的參數會有所變化,總共有八種模型。讓我們首先寫出我們的代碼。我們先寫X軸的部分,因為默認方向是向東的,而東西方向都屬於X軸。我們先看看默認向東的模型,可以看到碰撞箱,用樞紐點可以看到座標,零點描述長方體所需獲取的點,以及右上角點的座標是700,厚度為2,因此右上角點是9666。讓我們一起編寫"block box"。
將7.0D0.0D0.0D轉換為700後,描述了東西方向XSHA的設置。然後需要設置南北方向的Z Ship,通過調整中間的樞紐點形成ZSHA。在Z形狀下,關注一個點和右上角的另一個點,分別是007和十六十六九,複製並修改這兩個點得到Z Ship。
此外,創建碰撞體積需要使用Collection進行直接複製粘貼,完成關閉狀態模型的製作。隨後製作開啟狀態的模型,合併兩個盒子。舉例說明以800和十六十六二為座標點,描述盒子的合併方式;再描述另一形狀的座標,需要拼合在一起。
指出門的朝向不能簡單使用XZ座標來衡量,開著的門會導致不同朝向的不同碰撞箱形狀,因此需要詳細描述每個朝向以確保準確。最後,調整碰撞箱高度至24,並探討將模型傳送至讀取位置的方法。
描述獲取形狀和碰撞型的方法,重寫構造函數並調整參數;提及獲取形狀時面向問題,以及獲取軸相關信息。重點強調調整碰撞箱高度至24,完成所有模型製作後,學習如何將模型傳送至讀取地點。通過get sha和death leonship獲取形狀和碰撞體積信息,重寫構造函數以確保可用性。
在面向問題中,我們只有XY兩種可能性。而需要額外判斷的是門是否打開,即block state中獲取開啟狀態的值和朝向的值。開始進行判斷,如果門是打開的,則根據朝向返回對應的開啟模型,總共有四種可能性:north ship open、west ship open、south ship open以及east ship open。此外,設定了一個default情況,如果不滿足前述條件,則根據朝向確定是X Ship還是Z Ship,並進行返回。
在對模型進行檢查時,默認情況下面向東,即默認軸為X軸,對Y軸和Z軸進行相應判斷,顛倒一下條件邏輯。針對默認朝向向東,使用default便於理解且消除錯誤。接下來是collision on ship,用於獲取碰撞體積信息。同樣方式複製並調整參數,返回COLLISION版本的結果,將相應內容加入返回值中。
再提到is path fendable方法,用於判斷生物是否可以通過該路徑。簡單地判斷門是否處於打開狀態,返回相應的block state的open值。使用use屬性時,可在方塊上掛載該屬性,確保任何物品都能實現對門的開關。掛載屬性的位置取決於開發經驗,參考原版柵欄門方法,若門處於打開狀態,則通過set value設置open狀態為false,對block state進行相應修改。
我們之前已經提到了,所有關於這個方塊狀態的可能性都在遊戲啟動時就確定了。因此,我們只是將這個方塊狀態轉換成另一個方塊狀態,並沒有真正設置數值,而只是改變了另一種可能性。因此,這個方塊在世界中並沒有發生改變。我們仍然需要通過level點set block的方式,將座標block傳遞進去。在這裡我們不需要理解特定代碼段的含義,只需要按照要求寫入,因為我們要實現的功能與其類似,所以直接模仿即可。
這個方法在開發中很常見,在遇到不清楚的部分時,直接找相似功能的模塊,然後進行參考和模仿,而將主要精力放在實現自己想要的邏輯上,而非在琢磨那些難以理解的參數上,這樣做會浪費大量時間。最好的方式是直接模仿,把注意力集中在主要邏輯上。另外,原文還提到了根據玩家面向來判斷是否能夠開門,這是合理的設計。
總之,複製粘貼整段代碼應該是個不錯的選擇,當然需要根據具體情況進行一定的修改和優化。有些部分可以省略,例如聲音相關的內容,或者可以直接複製其他模塊的實現。如果想要添加聲音,也可以在資源文件中加入自定義聲音文件,並在對應位置引用即可。此外,要小心區分哪些部分需要修改,哪些需要保留,這是一個需要經驗積累的過程。在構造函數中註冊默認狀態和設置數值的操作需要被移植到新的代碼中。
我們還可以設置面向參數(facing),應該是好東方(east)。然後將這些參數都填寫完整,這樣就沒有問題了吧。接著,我們來註冊我們的方塊。有時候,當你更新 Forge 後,這些設置可能會出現問題。你可以在編輯配置中進行調整,然後應用一下更改,問題就會解決。同樣的,測試服務器選擇測試服務器,服務器選項選擇相應的服務器,這樣就不會出現問題了。
最後,我們再創建 block state 中的內容。之前我們在我的方塊部分沒有詳細說明這些意思,現在可以解釋了。引號裡面寫的是所有方塊狀態參數的組合,在模組中指定需要使用的模型。因為我們有兩個模型,一個是關閉狀態,一個是開啟狀態,所以我們保存了兩個。參數 Y 表示繞 Y 軸旋轉的意思,因為四個方向只需要旋轉即可,無需額外處理模型。最後在語言文件中寫上對應信息,類似於 "block.block.testmod" 這樣的格式。
進入遊戲後,我們可以拿出小門檢查是否有問題。四個方向都檢查一遍,應該沒什麼問題。模型和碰撞體積也都正常。這樣我們的方塊就算是成功了,我們還可以堆疊形成大門。當然,由於我們直接複製了柵欄門的代碼,柵欄門在打開時會改變方塊朝向,這部分邏輯可以自行完成。雖然這些修改並不困難,但我覺得比較麻煩,所以選擇直接複製。