阿特我自己
[email protected]
Hello WvT
Minecraft Script Engine Mod 开发教程
Minecraft Script Engine Mod 开发教程

本教程由 @阿特我自己 制作,未经授权禁止以任何形式进行引用或转载,版权所有,侵权必究!

当前修订版本:1.3.0.beta1

前言

Minecraft Script Engine 是 Mojang 为 Minecraft 基岩版 推出的跨平台 MOD API 接口,用于自定义游戏行为
由于 Script 并非通过修改源代码实现补丁,与 Java 版的 Forge 补丁相比会有一定的局限性

请在阅读本文时务必打开 API 文档对照
希望正在阅读本文的开发者都能加入学习交流群:132538683

注意:脚本引擎 目前还处于测试阶段,每次版本更新都会有变动,且不另行通知,如果您的脚本在后续版本工作不正常,请等待作者更新本文档,或查看英文版官方wiki

 

一、准备工作

Script 使用 JavaScript 语言编写,本教程不会教授 JavaScript 的任何语法,在阅读本教程前你必须掌握 JavaScript 的基本语法
如果你还不了解,请点击此处去学习(至少需要学完 JavaScript 基本结构)

1. 开发环境

如果你看到这里,则说明你已经掌握了 JavaScript,现在让我们准备一下开发环境

JavaScript 编辑器:任何一个可以编辑 JavaScript 的 IDE 或是纯文本编辑器都可用于 MOD 开发。目前为止,Mojang 并没有推出任何专用于MOD开发的插件,但官方 WIKI 推荐使用 Visual Studio 进行开发,不排除以后会推出插件的可能性。

Minecraft Bedrock Edition:本教程仅适用于 1.12 版本

  • For Windows10: 在 Microsoft Store 购买正版,并通过 Xbox 会员中心加入测试组。
  • For Android: 在 Google Play 购买正版并加入测试组,下载 BlockLauncher,通过 BlockLauncher 启动游戏
  • For iOS: 暂不支持
  • For Wii U: 暂不支持
  • For Xbox One: 暂不支持
  • For PlayStation 4: 暂不支持
  • For Nintendo Switch: 暂不支持

2. MOD文件结构

Script 属于行为包,是行为包的组件之一,这意味着你可以和 add-ons 结合使用
以下为一个只包含 Script 的行为包文件结构

以下为 manifest.json 文件的简单示例

你可以在此处在线生成UUID
你可以点击此处下载我们已经创建好的模板,但请注意,一定要将清单文件中的UUID全部换成你自己的。

3. 服务端脚本与客户端脚本

脚本引擎将脚本分为服务端客户端,通过这种结构,可以很轻松的解决联机同步问题,同时减轻服务器的压力

server 文件夹内的脚本会在服务端执行,它们通常用于生成实体、生成方块、改变实体或方块的状态等等,这些操作都需要所有玩家同步
client 文件夹内的脚本会被发送给玩家,并在玩家各自的终端上运行,它们通常用于监听该玩家的行为或是操作自定义UI

无论文件夹内的脚本文件有多少,它们都将独立运行,互不干扰
当然,Mojang 已经对其作出了限制,有些行为只能在服务端进行,有些行为只能在客户端进行,这将在后面的教程讲到

4. Hello World!

按照国际惯例,我们来编写一个 Hello World 程序了解一下脚本的基本结构
在你的客户端脚本(client.js)里写入如下代码,你暂时不需要了解每行代码的意义,我会在后面进行详细讲解

如果你成功打包并导入了 MOD,进入世界会看到如下画面
如果你的脚本出现了错误,可以对照参考我们已经制作好的 HelloWorld 程序示例,点击此处下载

5. 打包和导入MOD

  1. 将行为包根目录内的所有文件压缩为 zip 格式(不是压缩该行为包文件夹)
  2. 将压缩包的后缀改为 .mcpack
  3. Win10 端双击该文件会自动打开 Minecraft 并导入,Android 端需要在文件管理器手动指定打开方式为 Minecraft
  4. 在创建存档时勾选 使用实验玩法,并在 行为包 选项卡添加刚才导入的行为包

点击这里,学习如何便捷地打包导入MOD

 

二、脚本引擎中的系统(System)

在脚本的最开头,我们需要为脚本注册一个系统System),正如 JavaScript 的 document 对象用于操作 HTML 页面一样,System 对象定义了一些方法和属性用于操作游戏行为

1. 获取系统对象

脚本引擎内置了两个对象:clientserver,可以通过简单名称访问
通过这两个对象的 registerSystem(majorVersion, minorVersion) 方法,你可以获取到其对应的 System 对象
以下为该方法的简单描述,完整描述请查看API文档:

  • majorVersion —— Integer类型,你的脚本旨在使用的Minecraft脚本引擎的主要版本
  • minorVersion —— Integer类型,你的脚本旨在使用的Minecraft脚本引擎的修订版本

如果没有特殊需求,传入 0 即可

2. 重写系统初始化方法

在获取到 System对象 后,我们需要进行一些初始化工作,比如注册事件监听器或自定义组件

当世界已经准备好,但玩家还未进入世界时(无论客户端还是服务端),引擎会自动执行 System 对象initialize() 方法,我们需要对这个方法进行重写
注意:不要在此方法内生成任何实体或对世界进行其他的改动,因为此时玩家还没有进入世界

3. 重写系统更新方法

脚本引擎 每 tick(50ms)都会执行一次 System 对象的 update() 方法,这类似于 ModPE 中的 modTick() 钩子函数

方法体的耗时不会影响该方法的定时执行。若方法体耗时 16ms,脚本引擎将会在 34ms 而不是 50ms 后再次执行该方法
请尽量将耗时控制在 50ms 内,否则可能会导致意想不到的情况

4. 重写系统关闭方法

当脚本引擎关闭时会调用 System 对象的 shutdown() 方法,开发者可以在该方法内做一些结尾工作

对于客户端来说,就是该玩家退出世界的时候
对于服务端来说,就是所有玩家都退出世界的时候

 

 

三、事件(Event)

脚本引擎拥有事件机制,实体、世界、物品的一举一动都是事件,例如玩家加入世界、天气改变、实体受到攻击等等

开发者可以对这些由游戏发出的事件进行监听并作出反映
开发者也可以手动广播事件,当一个事件被广播后,脚本引擎将会按需捕获这些事件,并在合适的时候作出反应

1. 内置事件的分类

脚本引擎有两大类内置事件:客户端事件服务端事件,该分类决定事件是在客户端(Client)可用还是服务端(Server)可用
这些事件又分为 可监听事件可触发事件
每一个事件都有一个使用 “minecraft” 命名空间的独立标识符,方块、实体、药水效果的命名空间亦是如此

例如,“玩家进入世界”的事件即为 客户端事件 中的 可监听事件,其标识符为 “minecraft:client_entered_world”
而“在聊天栏显示消息”的事件即为 客户端事件 中的 可触发事件,其标识符为 “minecraft:display_chat_event”

所有事件的类型标识符描述都在 API文档 里查到,希望你在阅读本文的同时打开API文档

2. 监听事件

System 对象有如下方法可用于注册事件监听器
listenForEvent(identifier,callback)

  • identifier 为事件的标识符。通常情况下,事件标识符都传入一个可监听事件或自定义事件
  • callback 回调函数。需要传入一个回调函数。当监听到该事件时,脚本引擎会调用该回调函数并传入该事件的数据,因此回调函数需要定义一个形参用于接收数据

注册一个事件监听器的示例代码如下

通常情况下,事件监听器的注册都是在 系统初始化(initialize()方法) 时完成的
接下来需要读取事件数据,事件数据总是一个 EventDataObject 类型的对象
EventDataObject 是 Mojang 规定的一个内置对象类型
所有此类型的对象都有以下属性

  • __type__ —— 只读。代表该对象的类型,值为”event_data”
  • __identifier__ —— 只读。代表该事件数据属于哪个事件,如果代表一个发送聊天消息的事件,则该属性的值为”minecraft:display_chat_event”
  • data —— 只读。事件数据内容

data 是该对象的关键属性,该属性的值依然是一个对象,而该对象包含了脚本引擎所传递的事件数据的内容,不同的事件有不同的数据
例如:通过查询 API 文档可知,”minecraft:block_destruction_started” 事件数据对象包含 playerblock_position 两个属性,访问 data.playerdata.block_position 即可
以下是监听事件的完整代码示例,”minecraft:block_destruction_started” 是服务端事件,请在服务端脚本中编辑代码

3. 广播事件

同样,System 对象也提供了用于广播事件的方法。如果广播的是内置事件,游戏会按需捕获并作出反应;如果广播的是自定义事件,需要开发者手动捕获。

在此之前,我们需要了解一下事件数据模板。广播事件需要传入一个 EventDataObject 对象,该对象包含了事件数据所需的所有属性及其默认值,该对象的结构与上述的 EventDataObject 对象相同。我们需要先获取指定事件的数据模板并对其进行修改,接下来再广播指定事件并传入该对象即可

System 对象提供了如下方法用于创建一个指定对象的事件数据模板
createEventData(identifier) —— 创建一个指定事件的事件数据,identifier 为事件标识符。该方法返回一个 EventDataObject 类型的事件数据,包含了该事件所需的所有数据及其默认值

接着修改事件数据的值
例如:通过查询 API 文档可知,”minecraft:display_chat_event”事件的数据有一个 message 属性,因此修改 message 属性的值即可

使用如下方法广播一个事件
broadcastEvent(identifier, data) —— 广播一个事件,identifier 为事件的标识符,data 为 EventDataObject 类型的事件数据
以下是广播事件的完整代码示例,当监听到事件时,向聊天栏输出事件数据

 

4. 自定义事件

除了内置事件,我们还可以广播和监听自定义事件,在此之前需要注册自定义事件。通常情况下,自定义事件的注册都是在系统初始化方法内完成的
registerEventData(identifier, data) —— 注册一个自定义事件到脚本引擎,并指定其事件数据的模板

  • identifier 为事件的标识符,使用命名空间格式(namespace:event),且命名空间不能为 “minecraft”
  • data 为事件数据模板

以下为注册一个自定义事件的示例

在注册事件之后,可以使用 createEventData() 方法获取事件数据模板对象,该对象的 data 属性即为注册时指定的 template 对象

 

5. 使用自定义事件在服务端和客户端之间通信

事件的广播范围是跨终端的,服务端可以接收到客户端广播的事件,客户端也可以监听到服务端广播的事件,因此可以通过自定义事件在服务端和客户端之间通信

以下是一个进阶版的 HelloWorld 示例:

玩家加入世界的事件为客户端事件,需要在客户端进行监听,当监听到该事件后广播一个自定义事件
在服务端监听该自定义事件,当监听到事件后执行显示标题的指令

打包运行会看到如下画面

 

四、组件(Component)

如果你开发过 ModPE,你肯定知道,要获取某个实体的坐标,只需要使用 Entity.getPosition(entity) 方法即可,想要改变实体的坐标,只需要使用 Entity.setPosition(entity, x, y, z) 即可。但很可惜,这在脚本引擎里是一个全新的概念。

对于脚本引擎来说,坐标(”minecraft:position”)就是实体的一个组件(Component)。类似地,实体的生命值碰撞箱体积装备库存等等,都是实体的组件。脚本引擎内置的所有组件都可在 API文档 里查到

事件(Event)类似,内置组件也分为服务端和客户端,但不同的是,大多数组件都是仅服务端可用的,目前为止只有 “minecraft:molang” 组件在客户端可用。

System 对象提供了几个方法用于操作组件,具体描述请查看API文档中的 组件相关 分类
我着重讲一下 getComponent(),和 applyComponentChanges() 的用法

getComponent(entity, identifier) —— 从指定实体中获取指定的组件

entity —— 实体对象,EntityObject 类型

identifier —— 组件的标识符,String 类型

如果成功会返回一个 ComponentObject 类型的组件对象,该对象包含了组件的数据

失败会返回 null

一个标准的 ComponentObject 总是包含以下属性

名称 类型 描述
__type__ String 只读-对象的类型。该属性的值将会是”component”
__identifier__ String 只读-对象的命名空间标识符。例如:若该对象的类型为”component”,且代表坐标组件,则该属性的值为”minecraft:position”
data Object 只读-组件所包含的数据

当获取到一个组件对象后,可以访问对象的 data 属性,该属性即为一个数据对象

applyComponentChanges(entity, component) —— 应用更改实体的组件

entity —— 实体对象

component —— 需要应用回实体的组件对象

如果需要获取实体的 坐标 组件,只要用 getComponent(entity, “minecraft:position”) 即可
通过查询API文档可知, “minecraft:position” 组件包含了 x, y, z 三个属性

接下来,我们试着来做一个小mod —— 当世界里生成了一个苦力怕时,把苦力怕传送到 0, 100, 0 位置
由于需要操作组件,必须写在服务端脚本

注册系统,获得 System 对象

系统初始化时注册事件监听器

事件所返回的数据对象中包含了一个 entity 属性,该属性的值是一个 EntityObject 类型的对象,我们可以定义一个变量用于存储该对象。

EntityObject 对象都包含 __identifier__ 属性,该属性的值是 实体的标识符
苦力怕的实体标识符为 “minecraft:creeper”,因此可以用如下代码判断加入世界的实体是否为苦力怕

接下来我们需要获取实体的坐标组件并对其进行修改。为确保万无一失,我们也可以使用 hasComponent() 方法判断一下实体是否拥有该组件

最后一步,将修改过的组件应用回实体

以下为服务端脚本完整的代码示例:

测试一下,你会看到,无论在什么地方生成苦力怕,都将被传送到 0, 100, 0 的位置。恭喜你,你已经了解 组件 的用法了

五、方块

暂空

 

六、实体查询器(Query)

如果你开发过 ModPE,你会知道 ModPE 没有很好的办法可以查找实体,你只能通过 Level.getAllEntitys() 方法获得所有的实体,并自己添加过滤算法。不仅麻烦,性能开销也很大。这种情况将会得到改善。

脚本引擎提供了实体查询器(Query)用于查找世界中符合规则的实体。
你可以快速地指定实体必须拥有某个组件,或者组件的值必须在某个范围内的规则。

System 对象 提供了一些方法可以操作 实体查询器,请查看API文档的 实体查询 分类
在本教程中我们会用到 registerQuery() 方法和 getEntitiesFromQuery() 方法
我们来看一下这两个方法的简化描述

registerQuery(comp, field1, field2, field3) —— 注册一个实体查询器并指定过滤规则

comp —— 组件的标识符。只有当实体拥有该组件时才会被捕获

field1 —— 第一个 组件中的属性 的名称

field2 —— 第二个 组件中的属性 的名称

field3 —— 第三个 组件中的属性 的名称

注册成功后会返回该 Query 对象,类型为 QueryObject

失败会返回 null

QueryObject 类型的基本结构请查看API文档的 内置对象 分类

在接下来获取实体时,你可以再指定一个过滤规则 —— 为现在所传入的这三个属性指定最大值和最小值。
如果你需要过滤三个以上属性,或是属性无法比较大小,你只能使用原始方法,注册一个空实体查询器获取所有实体,并手动实现逻辑
通常情况下,这个方法都用于检测实体坐标

getEntitiesFromQuery(query, field_min, field2_min, field3_min, field1_max, field2_max, field3_max)

query —— 实体查询器对象,QueryObject类型

field n _min —— 在注册实体查询器时给定的第n个属性的最小值

field n _max —— 在注册实体查询器时给定的第n个属性的最大值

成功后会返回一个数组,包含所有符合规则的实体,失败返回 null

现在我们来做一个占领据点的小MOD实践一下:

  • 划定一个区域为据点,如果有实体在据点内停留10秒的时间,该据点会被占领

由于实体查询器需要获取组件,请务必写在服务端

注册一个System,并在初始化时注册实体查询器,将过滤组件指定为坐标,并将属性设置为 x, y, z

系统更新时,从实体查询器中获得实体,指定坐标的的最大值和最小值。
这个坐标的范围即为 -5, 70, -5 到 5, 100, 5

判断是否得到了实体,如果有则提醒玩家并开始计时

计时到10秒时,提醒玩家据点被占领

以下为完整的代码示例

打包运行,你会发现聊天栏出现许多错误报告,但不要紧,这不是你的问题,也没有任何影响,这只是个bug,目前(1.9.0.3)还未修复

当你站在据点内或者有其他实体在据点内时,游戏会显示据点正在被占领

当你停留10秒后,会显示据点已经被占领

恭喜你,你已经学习完了脚本引擎入门的最后一个部分——实体查询器
至此,MOD开发的入门篇已经完结了,感谢您的阅读。进阶篇会在我完全掌握脚本引擎后撰写,请保持关注。

由于时间和个人能力不足,本教程的语言可能不够简炼,概念可能有些模糊,甚至会有些错误,请你自己在实践中获得更多经验!
如果有问题,可以在本站留言或加入我的交流群。
如果你觉得本文非常好,你也可以点击赞赏按钮捐赠,请务必留下你的名称,我会在捐赠列表写下你的捐款金额和名称。

赞赏

发表评论

textsms
account_circle
email

  • 苦力怕553

    好东西,收藏了(#滑稽)

    12月前回复
  • 微风

    不知道该说什么,滑稽支持就对了

    12月前回复
  • 虚青海

    可能我启动器版本不对?进入游戏提示了有脚本,但进去啥都没看到

    11月前回复
  • Frms

    我有一个大胆的想法,可惜我没学好《编译原理》

    11月前回复
  • SNS.H

    我怎么还在写ModPE

    11月前回复
  • :mrgreen: :mrgreen:

    11月前回复
  • 万能的 N/P 硅

    关注了 /

    11月前回复
  • Dob

    支持!!!

    11月前回复
  • 祁伴怂

    明白 :neutral: :oops:

    11月前回复
  • MeowCat

    喵~

    11月前回复

Hello WvT

Minecraft Script Engine Mod 开发教程
 本教程由 @阿特我自己 制作,未经授权禁止以任何形式进行引用或转载,版权所有,侵权必究! 当前修订版本:1.3.0.beta1 前言 Minecraft Script Engine 是 Mojang 为 Minecr…
扫描二维码继续阅读
2018-12-09


没有激活的小工具