跳转至

制作中国版模组SDK界面

这一页讲中国版模组SDK里的自定义UI工作流:先用旧版MC Studio界面编辑器或手写JSON准备界面,再用Python把界面注册到游戏里,最后通过UI API动态改控件状态。它不适用于国际版@minecraft/server-ui,也不等同于国际版资源包覆盖原版JSON UI

先记住三件事

开始之前,先把最容易出错的三件事记牢:

  1. JSON文件名、界面命名空间和对应的Python文件名最好保持一致。
  2. 界面贴图优先使用PNG,并只用字母、数字和下划线命名。
  3. UI布局只是静态外观,真正的交互逻辑要写在继承ScreenNode的Python类里。

如果一开始就把资源名、命名空间和脚本类路径弄乱,后面排查会很痛苦。

第一步:准备界面资源

中国版旧版做法,是先把按钮、准心、面板背景等图片导入资源管理器,再新建界面文件。常见资源会进入资源包的ui/与贴图目录中;保存界面后,编辑器会导出可直接运行的JSON界面文件。

如果是手写或接手旧项目,至少要先确认:

  • 贴图是否真的存在;
  • 资源名是否只含字母、数字和下划线;
  • JSON文件里的命名空间是否与脚本注册时一致。

第二步:先搭结构,再调细节

做中国版UI时,通常先把结构搭出来,再处理样式。一个常见顺序是:

  1. 创建画布。
  2. 创建若干面板作为分组容器。
  3. 往面板里放图片、按钮、文本、滚动列表或网格。
  4. 最后再调锚点、位移、尺寸和层级。

旧版资料反复强调,面板更像“容器”,方便后续整体移动和批量隐藏;如果一上来就把所有控件直接挂在根节点下,后面改层级和适配会越来越乱。

第三步:理解锚点、位移、尺寸和层级

中国版旧版UI,最重要的布局概念就是四项:

  • 锚点:决定子控件与父控件的哪个位置对齐。
  • 位移:在锚点基础上继续偏移。
  • 尺寸:决定控件宽高。
  • 层级:决定遮挡顺序。

旧版界面编辑器把位移和尺寸都写成%+Px语义:百分比部分按父控件尺寸计算,像素部分再叠加固定偏移。简单界面常直接用像素,只有在需要跟随父控件尺寸变化时才大量使用百分比。

如果界面出现“按钮被图片盖住”这类问题,先查层级,不要先怀疑脚本。

第四步:需要适配时,留意基类画布

旧版,继承common.base_screen的画布会引入额外根节点路径,用来处理安全区域和异形屏适配。这样做的副作用是:控件在脚本里访问时,路径可能不再只是/panel/text这种相对短路径,而要额外拼接一段基础路径。

所以,接手旧项目时如果发现:

  • JSON明明有这个控件;
  • 界面也确实显示出来了;
  • SetVisibleSetText之类接口就是不生效;

优先检查这个界面是不是用了基类画布和安全区域包装。

第五步:把JSON接到Python

中国版模组SDK的自定义UI通常在客户端系统里完成注册和创建:

import mod.client.extraClientApi as clientApi

class MyClientSystem(clientApi.GetClientSystemCls()):
    def __init__(self, namespace, systemName):
        super().__init__(namespace, systemName)
        clientApi.RegisterUI("myMod", "fpsBattle", FpsBattleScreen, "fpsBattle.main")

    def ShowHud(self):
        return clientApi.CreateUI("myMod", "fpsBattle", {"isHud": 1})

对应的UI类需要继承ScreenNode。创建成功后,就可以通过GetUI拿到实例,再调用界面接口改控件状态。

第六步:只把“会变化的内容”放到脚本里

旧版官方教程里常见的分工是:

  • JSON负责初始布局和默认外观;
  • Python负责显示、隐藏、切换文本、响应点击和更新状态。

例如开镜按钮按下后隐藏准心、显示瞄准镜,或者点击按钮后切换弹匣图标,这些都更适合在脚本里做。这样布局与逻辑分离,后面替换贴图或微调位置时不会牵一发而动全身。

第七步:需要挂到原生界面时

如果目标不是单独弹出一个自定义界面,而是把自己的控件附加到背包、箱子或熔炉等原生界面上,旧版中国版资料建议改用NativeScreenManagerCustomUIControlProxy这一套框架。

这种做法的思路是:

  1. 先选定一个允许附加的原生界面枚举;
  2. RegisterCustomControl把自定义控件注册到该原生界面;
  3. 继承CustomUIControlProxy,在OnCreate里拿到BaseUIControl根节点并写初始化逻辑;
  4. OnDestroyOnTick里处理清理或逐帧更新。

它更适合做“在原生界面上补一块额外信息区、按钮区或提示区”,而不是重写整套原生界面逻辑。

常见问题

界面创建不出来

先检查以下四项:

  • RegisterUI里的命名空间、键和定义路径是否与资源文件一致;
  • JSON文件名、命名空间、Python类路径是否对应;
  • 客户端系统是否真的执行到了注册代码;
  • 资源包里的UI文件是否已经导出到正确目录。

控件接口不生效

先确认控件路径是否正确;如果界面继承了common.base_screen,再检查是否遗漏了安全区域带来的基础路径。

按钮层级不对

先调层级或控件树顺序。旧版资料说明,自动层级调整与手动层级调整混用时,最容易出现“界面看起来对,运行时遮挡顺序却不对”的问题。

继续阅读