手冊:實例/SEMod/雜項/生物顯血的實現

出自Minecraft基岩版开发Wiki

本文是一個實例,來讓一些剛接觸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:

always_accepts_input

If true, the screen will always accept and process input for as long as it is in the stack, even if other custom UI screens appear on top of it

render_game_behind

If true, the game will continue to be rendered underneath this screen

absorbs_input

If true, input will not be passed down to any other screens underneath

           如果为true,输入将不会通过你的ui到达下面的其它ui,包括游戏界面。

is_showing_menu

If true, the screen will be treated as the pause menu and the pause menu won't be allowed to show on top of this screen

should_steal_mouse

If true, the screen will capture the mouse pointer and limit its movement to the UI screen

force_render_below

If true, this screen will be rendered even if another screen is on top of it and will render over them, including the HUD
HUD是指玩家的物品欄,血條,盔甲值以及飢餓度等。原文這地方其實不太準確,其實HUD在你一進入遊戲就在最底層了,所以本來就不需要讓你的ui在HUD下面繪製,相反,咱們待會還得給HUD加一條這個屬性,讓HUD能夠在你的ui下面繪製。

render_only_when_topmost

If true, this screen will only be rendered if it is the screen at the top of the stack

再來解釋下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下載(失效連結)


大功告成!如有錯誤歡迎指出。