Concepts of SAPI: Form & FormResponse
前言
表单(Form)是一种可供玩家操作的用户界面(UI),在SAPI中,表单则是一种承诺(Promise)对象。表单回执(FormResponse)则是表单兑现(fullfill)后的打包形式,玩家在表单上的操作最终会打包进表单回执当中。
讲述
在《借由附加包开发可获得成就的地图!》中,我介绍了SAPI 1.0中的表单、表单属性和表单方法,以及SAPI 2.0中的部分表单方法。这里来详细讲讲SAPI 2.0中的表单和表单回执,毕竟还是要与时俱进。首先,表单和资源包内的JSONUI不同,JSONUI用的是游戏内接口,通常不方便开发者自定义参数,但是可以在资源包内设计JSONUI的元素,使其看起来更美观;而表单虽然只有几个固定的控件,但是它可以与SAPI脚本内的其他参数交互,这让玩家可以在游戏内对SAPI参数实时交互。接下来介绍,目前拥有的三种表单分对应其表单数据:消息表单数据(MessageFormData),行动表单数据(ActionFormData)和模拟表单数据(ModalFormData)。表单数据有一个共通的构造方法,就是new <Action|Message|Modal>FormData()。其中,消息表单数据的结构非常简单:
只有四个设计方法(builder method),都是用来显示文字的。我们使用以下代码:import { world, system } from "@minecraft/server"
import { MessageFormData } from "@minecraft/server-ui"
world.afterEvents.playerJoin.subscribe(event => {
const player = world.getEntity(event.playerId)
var demo = new MessageFormData()
demo = demo.title("标题")
.body("整体注释")
.button1("第一个按钮")
.button2({translate: "第二个按钮"})//实际上,如果没有在语言文件定义替换的文本,就还会显示“第二个按钮”
system.runTimeout(() => {
demo.show(player)
}, 80)
})这里方法后换行再接方法的方式其实是一种简写,完整的代码应该是这样的:var demo = new MessageFormData()
demo.title("标题")
demo.body("整体注释")
demo.button1("第一个按钮")
demo.button2({translate: "第二个按钮"})只不过由于设计方法返回的还是消息表单数据类,所以可以一直在后面接设计方法。这样在玩家进入游戏4秒后就会弹出该表单,效果是这样的:
看起来像是用的服务器消息的接口,在主界面退出游戏也会弹出类似的窗口。表单有一个共同的.show()方法,将返回各种表单回执,本质是兑现后的承诺对象,存在以下共通属性:
这个canceled属性可以用来避免报错,因为如果表单被取消,表单回执就不会包括表单上的操作,所以有些表单回执的属性就不能读取,进而报错。我们来看消息表单回执(MessageFormResponse)的属性:
一个属性就足够,消息表单是最简单的表单。我们继续使用以下代码:import { world, system } from "@minecraft/server"
import { MessageFormData } from "@minecraft/server-ui"
world.afterEvents.playerJoin.subscribe(event => {
const player = world.getEntity(event.playerId)
var demo = new MessageFormData()
demo = demo.title("标题")
.body("整体注释")
.button1("第一个按钮")
.button2({translate: "第二个按钮"})//实际上,如果没有在语言文件定义替换的文本,就还会显示“第二个按钮”
system.runTimeout(() => {
demo.show(player)
.then(response => {
if (response.canceled) {
player.sendMessage("你取消了表单")
} else {
player.sendMessage(response.selection === 0? "你选择了第一个按钮" : "你选择了第二个按钮")
}
})
}, 80)
})就可以在聊天栏显示你对表单进行的操作了。接下来介绍行动表单数据。相比于消息表单数据,行动表单数据不局限于两个按钮,而是有不定的按钮可以选择,并且SAPI 2.0给它也加入了表头、标签和分隔线,它的设计方法如下:
其中图标路径是表示图片在资源包内相对文件地址的字符串。而行动表单回执(ActionFormResponse)和消息表单回执一样,只有一个selection属性,于是我们可以这样设计表单:import { world, system } from "@minecraft/server"
import { ActionFormData } from "@minecraft/server-ui"
world.afterEvents.playerJoin.subscribe(event => {
const player = world.getEntity(event.playerId)
var demo = new ActionFormData()
demo = demo.title("标题")
.body("整体注释")
.header("表头")
.button("第一个按钮")
.button({translate: "第二个按钮"})
.divider()
.label("标签")
.button("第三个按钮", "textures/blocks/torchflower")
.button("第四个按钮", "pack_icon")
system.runTimeout(() => {
demo.show(player)
.then(response => {
if (response.canceled) {
player.sendMessage("你取消了表单")
} else {
player.sendMessage(`你选择了第${response.selection+1}个按钮`)
}
})
}, 80)
})
这是实机的效果:
接下来就是最复杂的模拟表单数据,它的设计方法特别多:
里面还提到了各种设置项,其实就是几个固定属性的对象,它们的属性都是可选的:
另外还有模拟表单回执(ModalFormResponse)的属性:
这里面说的控件(control)其实就对应相应的设计方法,另外要注意,即使文本框的默认值是原始文本类型,它在表单回执中读取时返回的是替换后的字符串,而不是原始文本;而且像分隔线、表头这样的控件,在formValues中读取的值是空(null)。然后我们可以设计以下表单:import { world, system } from "@minecraft/server"
import { ModalFormData } from "@minecraft/server-ui"
world.afterEvents.playerJoin.subscribe(event => {
const player = world.getEntity(event.playerId)
const draw = ["选项1", {translate: "选项2"}]
var demo = new ModalFormData()
demo = demo.title("标题")
.header("表头")
.label({translate: "标签"})
.toggle("开关", {defaultValue: false, tooltip: "开关小贴士"})
.slider("滑块", 0, 255, {defaultValue: 0, tooltip: {translate: "滑块小贴士"}, valueStep: 5})
.divider()
.dropdown("下拉列表", draw, {defaultValueIndex: 0, tooltip: "下拉列表小贴士"})
.textField("文本框", {translate: "文本框悬挂文字"}, {defaultValue: "默认文本", tooltip: "文本框小贴士"})
.submitButton("提交按钮显示文字")
system.runTimeout(() => {
demo.show(player)
.then(response => {
if (response.canceled) {
player.sendMessage("你取消了表单")
} else {
const list = response.formValues
player.sendMessage((list?"你扳开了开关":"开关仍然关闭")+`,你将滑块滑至数值${list},`+`你选择了下拉列表中的${draw]},`+"这是你填写的文本:"+list)
}
})
}, 80)
})
然后是实机画面:
总结
表单是许多SAPI附加包的必要组件,表单回执是玩家实时操纵脚本的通道。在这里我给借助附加包那个帖打一个小补丁,表单看起来是不会读取.json定义的文件地址缩写,本来想着行为包文件里都可以用,结果试验之后还是不能用。所以代码一定要到游戏里试验才可以啊!
那个「Modal」应该翻译成「模态」而非「模拟」。 幻溯验劣 发表于 2025-9-8 02:07
那个「Modal」应该翻译成「模态」而非「模拟」。
“模态”指的是模式+状态的对应关系;“模拟”这个词算是我最早做翻译留下来的习惯译法吧,那个时候管这一类表单都叫“模拟表单”,模拟表单就是这么来的。我是觉得模态这个词不太好理解,跟那个行动表单一样,我觉得应该叫选择表单更符合这个表单的特性。还是按英文的原文标注来看吧,翻译是什么能对应上就好[哔哩_脱单]
页: [1]