- 致编者:请牢记我们的域名wiki.mcbe-dev.net!
- 致编者:欢迎加入本Wiki的官方交流QQ群或Discord服务器!
- 基岩版1.19.31现已发布!(了解更多)
- Inner Core现已支持Xbox模组联机!(了解更多)
- 如果您是第一次来到本Wiki,欢迎注册一个账户
- 点击顶部的“编辑”或“编辑源代码”按钮即可编辑当前页面
- 请知悉:在不登录时也可以编辑和新建页面,但是您当前的IP地址会记录在编辑历史中
手冊:實例/SEMod/雜項/生物顯血的實現
本文是一個實例,來讓一些剛接觸SE的開發人員熟悉一下script engine和ui engine。
閱讀本文前,建議閱讀:
阿特我自己的腳本引擎教學(可能與引擎最新版有出入)(失效連結)
閱讀本文的同時,建議查閱:
阿特我自己的中文腳本API文件(可能與引擎最新版有出入) (失效連結)
眾所周知,ui engine是mojang基於html5建立的方便開發人員在遊戲中建立自訂ui的引擎。你可以在html檔案中寫js以應答或監聽ui engine的事件。 這些事件可以被ui engine自己觸發,也可以被你的腳本(script)觸發。官方文件裡有對ui engine詳盡的介紹,這裡不再贅述。
在正式開始之前,先提一下,與modpe不同,script engine執行的模式是
ui引擎 ↹ 用戶端 ↹ 伺服器端
即伺服器端與用戶端/ui引擎與用戶端可以直接透過監聽的廣播通信,而ui引擎不能與伺服器端直接通信。這一點對於mc一個可聯機遊戲非常重要。 伺服器事件只能被伺服器端觸發和監聽,用戶端事件只能被用戶端觸發和監聽,ui engine同理。這確保了聯機時遊戲模組的正常執行。不過這複雜和嚴格的模式可能對於一些modpe開發人員是個噩夢。
別忘了開實驗玩法!
廢話了這麼多,下面我們正式開始。
先來考慮一下生物顯血如何實現。官方已經為我們提供了用戶端事件"minecraft:pick_hit_result_continuous",這個事件每tick都被觸發一次,它的資料裡包含了entity屬性和position屬性。見下表(我就是不翻譯(滑稽))。
Name | Type | Description |
---|---|---|
entity | Entity JS API Object | The entity that was hit or null if it fired when moving off of an entity |
position | Vector [a, b, c] | The position of the entity that was hit or null if it fired when moving off an entity |
我們只需監聽這個事件即可獲得每tick虛擬指針指向的實體。
下一步是獲得這個實體的目前生命和生命上限。很遺憾,取得實體的"minecraft:health"元件需要在伺服器端進行。我們不得不在用戶端自訂一個事件將該實體的資料傳給伺服器端。到這裡一些和我一樣的初學者應該已經意識到了modpe與se的不同。
我們之前提到,只有用戶端才能與ui引擎直接通信,所以我們還要把伺服器端獲得的資料傳給用戶端,再透過用戶端廣播自訂ui事件將生命值資料傳給ui引擎,大功告成。
還別高興的太早。我們只是將血量資料傳給了ui引擎而已,ui引擎要如何在畫面上顯示ui呢?我們需要實現:當玩家將指針指向實體時顯示血條,當玩家移開指針後3秒內關閉血條。
我的實現方法是,在用戶端定義一個計時器。
let timer = 0;
監聽"minecraft:pick_hit_result_continuous"事件並檢測指針指向的實體是否存在(即指針是否指向實體)。若不存在,timer++,若存在,timer歸零。
sys.listenForEvent("minecraft:pick_hit_result_continuous",function(eventData){ if(eventData.data.entity) timer = 0; else return; });
當timer==60(1tick=0.05s)時,關閉血條。
sys.update = function(){ timer++; if(timer === 60){ let sendData = sys.createEventData("minecraft:send_ui_event"); sendData.data.data = "{}"; sendData.data.eventIdentifier = "time_up"; sys.broadcastEvent("minecraft:send_ui_event", sendData); } };
以上只是粗略地介紹了下實現思路,下面將詳細介紹程式碼實現過程。
先來看下ui部分,不過在此之前你應該在資源包的清單檔案中加上一個屬性"capabilities":["experimental_custom_ui"]
讀者可以先查閱一下官方文件,官方文件中有的地方說的很含糊,比如:可觸發事件load_ui
minecraft:load_ui
This event is used to show a UI screen to the specific player running the client script. This event will add the UI screen to the top of the UI screen stack. The screen will be shown immediately after the event is triggered. Only screens defined in a HTML file can be shown using this event.
Event Data Parameters
Name | Type | Default Value | Description |
---|---|---|---|
path | String | The file path to the screen's HTML file 此處指html檔案相對於 資源包目錄/experimental_ui/ 的路徑 |
|
options | JSON Object | You can define the following options for the screen by setting their value to true or false:
|
再來解釋下send_ui_event事件(也是可觸發事件),原文真的是簡潔不明了。
minecraft:send_ui_event
This event is used to send UI events to the UI Engine for the specific player running the script. After the event is triggered, the UI event will be sent immediately.
Custom UI is based on HTML 5. Review the scripting demo for an example of a custom UI file.
Parameters
惜字如金的官方。。。我是看了官方維基上的rpg_game範例才懂的。
首先,script engine的特點是,無論你的mod裡有多少條腳本或html檔案,它們都是分別獨立執行的,不同檔案中通信的唯一方式是監聽和觸發事件。
而事件又分為兩類,普通的事件和UI事件。用戶端和伺服器端能夠直接監聽和觸發普通的事件,而UI engine只能直接觸發和監聽UI事件。(這樣說貌似不太確切,但我只能想到這麼一句簡潔的話來總結了。)
"普通的事件"就不多說了。UI事件的屬性如下表。
Name | Type | Description |
---|---|---|
eventIdentifier | String | The identifier of the UI event UI事件的id |
data | String | The data for the UI event being triggered UI事件的資料,沒錯,這僅僅是一個字串,而不是一個JSON物件,不過我們可以用JSON.stringify和JSON.parse將物件轉化成含JSON物件的字串,也可以把字串解析成JS物件。 |
腳本部分的所需的一些在其它開發文件中不清晰的內容我都已經介紹完了,取得元件等內容可以參閱官方文件或中文文件。下面是程式碼。
用戶端腳本client.js
let sys = client.registerSystem(0, 0); let timer = 0; sys.initialize = function () {//监听和广播事件请写在本函数或update函数中 sys.registerEventData("my:hp", {entity: null});//注册自定义客户端事件 sys.listenForEvent("minecraft:client_entered_world", function (eventData) { let uiDataTest = sys.createEventData("minecraft:load_ui");//触发load ui事件 uiDataTest.data.path = "game.html"; uiDataTest.data.options.render_game_behind = true; uiDataTest.data.options.absorbs_input = false; uiDataTest.data.options.is_showing_menu = false; uiDataTest.data.options.force_render_below = true; uiDataTest.data.options.render_only_when_topmost = false; uiDataTest.data.options.should_steal_mouse = false; uiDataTest.data.options.always_accepts_input = true; sys.broadcastEvent("minecraft:load_ui",uiDataTest); }); sys.listenForEvent("minecraft:pick_hit_result_continuous",function(eventData){//监听虚拟指针 if(eventData.data.entity)//判断是否是掉落物品 timer = 0; else return; let entity = eventData.data.ent
用戶端腳本client.js
let sys = server.registerSystem(0, 0); sys.initialize = function () { sys.registerEventData("my:hpui", {data: null});//注册自定义事件 sys.listenForEvent("my:hp",function(eventData){//监听客户端广播的事件 let entity = eventData.data.entity; let sendData = sys.createEventData("my:hpui"); let comp = sys.getComponent(entity, "minecraft:health"); sendData.data.data =comp.data.value+"/"+comp.data.max;//向客户端发送了一个形如"当前血量/血量上限"的数据 sys.broadcastEvent("my:hpui", sendData); }); };
注意,如上所述,HUD是在最底層的,所以我們在資源包加入一個json檔案使其強制在最底層繪製。
路徑:資源包目錄/ui/hud_screen.json
hud_screen.json
{ "hud_screen@common.base_screen": { "always_accepts_input": true } }
html檔案,路徑上面說過了。index.html在下面的連結裡
addon下載(失效連結)
大功告成!如有錯誤歡迎指出。