- 致编者:请牢记我们的域名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下载(失效链接)
大功告成!如有错误欢迎指出。