RTM 追加包制作教程

← 返回知识库

本教程基于 RTM 2.4.24 for Minecraft 1.12.2 编写,面向RTM追加开发新手。

参考:RTM 官方 Wiki(gamerch)

1开发环境准备
工具用途获取
Metasequoia 4(水杉)3D建模——RTM社区标准工具,原生MQO格式metaseq.net(免费Standard版 / 付费EX版)
paint.net / Photoshop基础贴图绘制paint.net,PS需订阅
Substance 3D Painter(可选)PBR贴图绘制Adobe官网
VS CodeJSON / JS编辑免费
Audacity音效编辑,导出OGG免费
MCTE模组导出NGTO格式(方块→模型)mcmod.cn介绍页
Minecraft 1.12.2+Forge运行环境Forge 14.23.5.2860 + RTM + NGTLib
2追加包文件结构

在正式进行RTM追加的制作前,我们需要在项目文件夹下构建正确的追加包文件结构。

但值得注意的是,RTM本体只支持加载压缩包形式的追加包,不支持加载文件夹形式的追加,若想要加载文件夹形式的追加,可以安装fixRTM模组。

RTM追加包存在两种合法的文件结构,RTM模组均可正确加载。

结构A:带mods目录

example_dir/ ← 放入 .minecraft/mods/ ├── README.txt ├── assets/ │ ├── minecraft/ │ │ ├── models/ ← .mqo / .mqoz / .obj 模型 │ │ ├── textures/ ← .png 贴图 │ │ └── scripts/ ← .js 脚本 │ └── sounds_example/ │ ├── sounds.json │ └── sounds/ ← .ogg 音效 └── mods/ └── RTM/ ├── train/ ← ModelTrain_*.json ├── rail/ ← ModelRail_*.json ├── stuff/ ← ModelConnector_*.json ├── machine/ ← ModelMachine_*.json └── wire/ ← ModelWire_*.json

结构B:不带mods目录

example_dir ← 放入 .minecraft/mods/ ├── README.txt └── assets/ ├── minecraft/ │ ├── models/ ← .mqo / .mqoz / .obj 模型 │ │ └── json/ │ │ ├── train/ ← ModelTrain_*.json │ │ ├── rail/ ← ModelRail_*.json │ │ ├── stuff/ ← ModelConnector_*.json │ │ ├── machine/ ← ModelMachine_*.json │ │ └── wire/ ← ModelWire_*.json │ ├── textures/ ← .png 贴图 │ └── scripts/ ← .js 脚本 └── sounds_example/ ├── sounds.json └── sounds/ ← .ogg 音效

你问我这两种有什么区别?我不到啊。

3建模

你说的对,但是水杉(Metasequoia 4)是由Tetraface开发的一款基于多边形建模技术的三维建模软件,适用于游戏开发、动画制作后面忘了

水杉(Metasequoia 4)是RTM社区的常见建模工具。且RTM本体对水杉原生模型格式的支持性最好,所以建议新手使用水杉进行追加包模型的建模。

而且模型的质量往往直接决定了RTM追加包的精细程度,所以建模是RTM追加制作至关重要的一环

但是你也得注意优化,否则你做出来的包会非常之卡。

但RTM支持的模型格式不止水杉的原生模型格式一种,以下是RTM支持的模型类型:

格式扩展名说明
MQO.mqoMetasequoia原生格式,RTM最常用
MQOZ.mqozMQO的压缩版本,本质是GZip压缩的.mqo,减小文件体积
OBJ.objWavefront OBJ通用格式,可从Blender等导出
NGTO.ngtoNGT自定义格式——用MCTE模组将Minecraft方块结构导出为模型。详见MCTE介绍

了解了这些,你就可以开始建模了。

不过这里不教建模,你可以观看b站up@Huli-awsl的视频

除此之外,在进行RTM追加包的模型制作时,有些事情需要你注意:

3.1 坐标与单位

概念数值
1 米 (游戏中)= 100 水杉单位
X 轴左右(正=右),车门沿X轴滑动
Y 轴上下(正=上)
Z 轴前后(正=前/列车运行方向)
坐标演示 / COORDINATE LAB
X / 左右
Y / 上下
Z / 前后
1 米=100 水杉单位
重要:水杉的单位和MC单位换算如下:100单位=1米。例如标准轨距1435mm约等于143.5水杉单位,车轮间距在JS中用HALF_GAUGE=0.5647(米)即约56.47水杉单位。建议参考官方模型或测量现有追加包的模型来校准尺寸。

3.2 列车模型结构

一个RTM列车通常由两个独立的.mqo文件组成:

var
  • 车体文件(如 ModelTrain_NJ01M.mqo):含车身、内构、车门、窗、灯等所有对象
  • 转向架文件(如 zhuanxiangjia2.mqo):含转向架构架和车轮。车轮的旋转轴心必须位于其几何中心

车体和转向架的相对位置通过JSON中bogiePos指定。

列车结构 / BODY + BOGIE
BODY / 车体文件
bogiePos
BOGIE / 转向架文件
1

3.3 导出模型

水杉保存即为.mqo格式。如使用.mqoz(压缩MQO),可在水杉中另存为对应格式。.obj格式可从Blender等建模软件导出。所有模型放入assets/minecraft/models/。

4贴图篇:从基础绘制到 PBR

贴图制作有两个层级:基础贴图适用于所有开发者,PBR贴图是高阶选项。建议新手从基础贴图开始。

4.1 基础贴图

用水杉的UV编辑器展开UV → 导出UV布局图 → 在GIMP/Photoshop中绘制贴图 → 保存为PNG。

属性要求
格式PNG(支持Alpha透明通道)
分辨率1*1到4096*4096范围内均可

不过这里也不教画贴图,你可以观看b站up@Huli-awsl的视频

4.2 PBR贴图

PBR贴图是基于物理的渲染(Physically Based Rendering)技术中使用的一种贴图类型,旨在更真实地模拟光与物体表面的相互作用。

在我的世界中,你可以通过使用PBR贴图以及支持PBR贴图的光影来制造一些十分真实的效果。

2

RTM兼容基于labPBR标准的PBR材质,而labPBR标准对于每个材质都有三种贴图,分别为基础色贴图,反射贴图(后缀_s)和法线贴图(后缀_n)。建议使用Substance 3D Painter绘制并创建特定的导出模板来导出PBR贴图。这个导出模板遵循固定的通道布局:

文件名通道布局
$textureSet.pngR=BaseColor.R G=BaseColor.G B=BaseColor.B A=Opacity
$textureSet_s.pngR=Glossiness(粗糙度反转) G=F0(金属反射率) B=空 A=空
$textureSet_n.pngRGB=Normal OpenGL(OpenGL标准法线) A=空
PBR 通道 / TEXTURE CHANNELS
$textureSet.png / BASE
$textureSet_s.png / GLOSS
$textureSet_n.png / NORMAL
3

由于原生的Minecraft没有SSAO,在使用Substance 3D painter绘制贴图时,开发者习惯将AO贴图直接以正片叠底(Multiply)方式直接叠加到基础色贴图上,以提升贴图的质感。

5JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它用人类可读的文本存储数据对象。你可以把它理解成一张「物品配置表」——每个 .json 文件记录了 RTM 中一个物品(列车、轨道、信号机等)的所有属性:它叫什么名字、用哪个模型、贴什么图、有什么功能。

当 RTM 模组启动 Minecraft 时,它会在 mods/ 目录下寻找所有追加包文件夹,然后扫描每个追加包中的子目录,读取里面的每个 .json 文件。一个 .json 文件 = 一个物品。如果 JSON 文件命名或格式不对,RTM 就会跳过它,物品就不会出现在游戏中。

5.0 文件命名与类型总览

下面是 RTM 支持的所有物品类型,每种类型都有固定的文件前缀和存放目录。RTM 模组启动时会扫描这些目录,根据文件名前缀判断物品类型。例如所有以 ModelTrain_ 开头的 .json 文件都会被当作列车处理。

前缀类型目录
ModelTrain_列车mods/RTM/train/
ModelRail_轨道mods/RTM/rail/
ModelConnector_接触网设施mods/RTM/stuff/
ModelMachine_机器设备mods/RTM/machine/
ModelWire_线缆mods/RTM/wire/
ModelSignal_信号机mods/RTM/signal/
ModelContainer_货柜/集装箱mods/RTM/container/
ModelFirearm_武器(火炮等)mods/RTM/firearm/
ModelVehicle_载具(坦克/汽车)mods/RTM/vehicle/
ModelMechanism_机械传动部件mods/RTM/mechanism/
ModelNPC_NPCmods/RTM/npc/
ModelOrnament_装饰物mods/RTM/ornament/

5.1 列车 — ModelTrain_*.json (34 键)

列车是 RTM 追加包中最常见也最复杂的类型。它的 .json 文件定义了列车的外观模型(MQO 格式)、贴图材质(PNG 格式)、物理参数(编组间距、速度、座位等)和交互功能(车门、灯光、报站)。下面从最简版本开始,一步一步教你构建完整的列车 JSON。每一步只加几个新键,逐步累积。

做追加包就像搭积木——先搭个能跑的骨架,再往上添门窗、座位、报站。别想一口吃成胖子。

第 1 步——基础创建。你需要创建一个.json文件,名称为:ModelTrain_列车名.json,然后使用记事本或vscode打开这个.json文件,在这个文件中填入以下内容

{ "trainName": "Your_Train", "trainType": "EC", "tags": "Your Train" }

这些内容包含了RTM列车追加包最基础的信息:列车名,列车类型和列车在游戏内显示的标签名,其中列车名只能使用ASCII字符。

第 2 步——加上转向架和车体模型。在trainModel2和bogieModel2两键下的modelFile键后填入车体/转向架的模型名称,textures键后的数组中以["材质名","材质对应贴图在assets/minecraft目录下的相对路径","材质渲染选项"]的形式将对应模型的所有材质填入:

{ "trainName": "Your_Train", "trainType": "EC", "trainModel2": { "modelFile": "Your_Train.mqoz", "textures": [["default", "textures/train/Your_Train/default.png", "AlphaBlend"]], }, "bogieModel2": { "modelFile": "bogie.mqoz", "textures": [["default", "textures/train/Your_Train/default.png"]] }, "tags": "Your Train" }
关于贴图渲染选项(textures 第三列):
这一选项是决定对应材质在MC中渲染特性的选项,它是一个逗号分隔的字符串,RTM 在加载时会按逗号拆分为独立的标志位。以下是全部合法的渲染选项值:

Light——启用光照。此选项需配合对应的xxx_light0,xxx_light1,xxx_light2发光贴图使用,当填入此选项时,若在游戏中放下该列车并在RTM控制GUI开启前灯/后灯/前后灯时,RTM会自动将对应的xxx_light0,xxx_light1,xxx_light2发光贴图作为一层不受环境光亮度影响的发光贴图层覆盖到列车的模型上。
AlphaBlend——启用 Alpha 透明度混合。用于玻璃窗户、半透明隔板等需要透出后方景物的材质。必须配合带 Alpha 通道的 PNG 贴图使用。
""(空字符串)——默认行为,不启用任何特殊处理。

选项之间用逗号组合,例如 "Light,AlphaBlend" 会同时启用光照和半透明。
第 3 步——增加配置。这一步定义了一些列车的具体信息:它能跑多快,转向架在什么位置,长度是多少......都是由这些键来定义的:

{ "trainName": "Your_Train", "trainType": "EC", "trainModel2": { "modelFile": "Your_Train.mqoz", "textures": [["default", "textures/train/Your_Train/default.png", "AlphaBlend"]], }, "bogieModel2": { "modelFile": "bogie.mqoz", "textures": [["default", "textures/train/Your_Train/default.png"]] }, "buttonTexture": "textures/train/Your_Train/button_Your_Train.png",//列车在选车界面的按钮贴图目录 "trainDistance": 10.25,//车辆的半长,单位m "playerPos": [[0.8, 0.05, 8.975], [-0.8, 0.05, -8.975]],//玩家上车后的位置 "bogiePos": [[0.0, 0.0, 6.6], [0.0, 0.0, -6.6]], //转向架的位置(这一项建议只修改z轴的坐标,x轴和y轴保持为0,且第一个转向架的Z轴坐标应大于第二个的,否则会导致一些问题) "maxSpeed": [0.40, 0.84, 1.08, 1.39, 1.67],//对应列车五个挡位下的最大速度,单位m/Tick(1Tick=1/20s) "smoothing": true,//是否开启平滑着色 "doCulling": true,//是否开启单面渲染 "accelerateion": 0.0022,//列车加速度,单位m/Tick² "tags": "Your Train" }

到了这步,你的追加就可以正常进入游戏了。

不过这些内容只是最基础的,RTM还提供了许多键供开发者使用:

车门。车门是 RTM 列车最重要的交互功能。door_right 定义右侧门,door_left 定义左侧门。transform 参数控制滑动动画:

"door_right": [{ "objects": ["door1","door2"], "pos": [1.4596,0.0,12.6442], "transform": [[90.0,0.0,1.0,0.0]], "childParts": [] }], "door_left": [{ "objects": ["door3","door4"], "pos": [-1.4596,0.0,12.6442], "transform": [[-90.0,0.0,1.0,0.0]], "childParts": [] }]
transform 参数解读:[[开放程度, 保留值, 开放距离, 保留值]]。第一个值越大开门越快,第三个值的正负决定滑动方向——正值向右滑,负值向左滑。把 door_right 的第三个值设为正数、door_left 的设为负数,两侧门就会向相反方向打开。

座位和车内灯。seatPos 定义了玩家可以坐下的位置,其值的子数组的第四项控制座椅类型(0=南、1=西、2=北、3=东):

但值得注意的是,seatPos所传入的坐标并不是以m为单位的,而是以1/16m为单位。且其所传入的坐标只能是整数。如果觉得这样太麻烦且精度欠佳,可以使用seatPosF,其结构与seatPos一致。

"seatPos": [[16,2,-145,2],[-16,2,-145,2]], "interiorLights": [{"pos":[0.0,2.9,-12.5]},{"pos":[0.0,2.9,12.5]}]

方向幕和报站音效。rollsignTexture 是方向幕的贴图,rollsignNames 是可选路线名称列表,sound_Announcement 把每条报站名称关联到音效 ID:

"rollsignTexture": "textures/trains/rollersign.png", "rollsignNames": ["None","站A","站B"], "rollsigns": [{ "uv": [0.0,1.0,0.0,1.0], "pos": [[[-0.48,2.34,11.05],[-0.48,2.19,11.05],[0.48,2.19,11.05],[0.48,2.34,11.05]]], "doAnimation": false, "disableLighting": false }], "sound_Announcement": [["站A","sounds:station.go1"],["站B","sounds:station.go2"]]

当然,RTM 的大多数 JSON 键都可选,所以有些你用不到的功能直接不写就行了,缺的键 RTM 会用默认行为处理。下面是这些键的完整参考。

键必需类型说明
trainName是String列车在游戏内显示的任意名称,无字符限制
trainModel2是Object车体模型对象,包含 modelFile(MQO/MQOZ/OBJ/NGTO 文件路径)、textures(材质映射数组)和可选的 rendererPath(JS 渲染脚本路径)
trainType否String列车动力类型枚举:EC=电力动车/电力机车、DC=内燃动车/内燃机车、CC=货车、N/A=无动力(纯客车/货车靠牵引)、TC=测试专用(部分版本)、TEST/Test=开发调试用测试列车。不写默认为 EC
bogieModel2否Object转向架模型对象,结构同 trainModel2(modelFile + textures + rendererPath)。不设置此键则列车无转向架
bogieModel3否Object用于设置两组不同的转向架。结构同 bogieModel2
bogiePos否Float[][]转向架在车体坐标系下的安装位置 [[x,y,z],[x,y,z]],单位米。x=左右偏移、y=高度、z=前后距离车体中心。前后各一个,通常 z 值互为相反数
buttonTexture否String列车在选车界面的按钮贴图目录
playerPos否Float[][]玩家右键上车后的站立位置 [[x,y,z],[x,y,z]]。分别对应两个转向架
trainDistance否Float列车的半长,单位m
isSingleTrain否Boolean是否为独立的车辆
doCulling否Boolean是否启用单面渲染,默认true
smoothing否Boolean是否启用平滑着色。默认true
tags否StringRTM物品的显示标签
accuracy否String碰撞检测精度枚举:LOW=低精度、MEDIUM=中等精度、HIGH=高精度
color否Int[3]列车默认车体颜色 [R, G, B],三个整数分别对应红绿蓝通道,取值范围各为 0-255。设置了此键后,玩家可通过列车 GUI 的颜色选择器调整
customButtons否String[]自定义 GUI 按钮的文本标签列表,每项对应一个按钮,在列车 GUI 中显示额外的功能入口。与 customButtonTips 数组一一对应,一般搭配js实现功能
customButtonTips否String[]customButtons 数组中每个按钮对应的鼠标悬停提示文本列表,长度必须与 customButtons 一致
collisionParts否String[]参与物理碰撞检测的模型部件名(MQO 中的对象名)列表。适用于自定义碰撞体积的车型
headLights否Float[][]列车头灯(前照灯)的发光点位置列表 [[x,y,z],...],单位m。每个位置对应一个独立的点光源,列车的行驶方向为 Z 正方向
tailLights否Float[][]列车尾灯(红色标志灯)的发光点位置列表 [[x,y,z],...],单位m。格式与 headLights 相同,通常位于列车后端两侧
fragmentShaderPath否String片段着色器 GLSL 脚本路径,用于自定义每个像素的渲染颜色(shader-test 系列列车使用)。文件路径相对于 assets/minecraft/
interiorLights否Object[]车厢内部照明的光源位置列表 [{'pos':[x,y,z]},...],每个元素包含一个 pos 键。
jointDelay否Float铰接列车的关节延迟时间,单位是游戏刻(ticks,20 ticks = 1 秒)。数值越大后一节车厢对前一节的跟随响应越滞后。
maxSpeed否Float列车在五个挡位上的最高速度,单位为m/tick
muteJointSound否Boolean是否关闭撞轨音效
offset否Float[3]模型整体在轨道上的位置微调偏移量 [x,y,z],单位m。用于调整模型在铁轨上的视觉位置。正值在 x 方向向右移、y 方向向上移、z 方向向前移
pantoPos否Float[][]受电弓位置 [[x,y,z],...],单位m。每个位置对应一个受电弓滑板与接触网的接触点。列车运行时 RTM 根据这些位置使得受电弓高度与接触网匹配(需结合js实现该功能)
rolling否Object侧滚(左右摇摆)行为配置的父级对象,包含三个子系数键。此对象存在时列车过弯和轨道不平时车身会产生左右摇摆效果。注意 rolling 本身不是数值,其子键 rollSpeedCoefficient、rollVariationCoefficient 和 rollWidthCoefficient 才是实际控制参数。
rollSpeedCoefficient否Float侧滚速度系数,控制列车过弯时的倾斜响应速度。数值越大倾斜反应越快。取值范围约为 0 到 0.25
rollVariationCoefficient否Float侧滚变化系数,控制列车因轨道不平顺引起摇晃的随机变化程度。数值越大过弯时倾斜幅度的不规律变化越明显。取值范围约为 0.51 到 0.7
rollWidthCoefficient否Float侧滚宽度系数,控制列车过弯时车身倾斜的幅度(左右摆动的最大角度)。数值越大倾斜幅度越大。取值范围约为 0.5 到 1.25
scale否Float模型的整体缩放倍率。1.0 = 原始大小不变。注意缩放同时也会影响碰撞箱大小和物理行为
seatPos否Int[][]乘客座位位置列表 [[x,y,z,type],...]。注意其坐标并不是以m为单位的,而是以1/16m为单位。且其所传入的坐标只能是整数、type=座椅类型(0=南、1=西、2=北、3=东)
seatPosF否Float[][]seatPos的浮点数版本
serverScriptPath否String服务端专用的 JavaScript 脚本路径,用于实现需要服务器权威验证的自定义逻辑。文件路径相对于 assets/minecraft/
smoke否Float[][](DC 内燃类型使用)烟雾粒子的发射位置列表 [[x,y,z],...],单位m。每个位置是一个排烟口。set [](空数组)可关闭默认烟雾
soundScriptPath否String运行时动态音效的 JS 脚本路径,用于实现 VVVF 变频牵引声、空气压缩机声等复杂的随速变化音效。文件路径相对于 assets/minecraft/
sound_DoorOpen否String车门打开时播放的音效 ID。格式为 'sounds_XXX:xxx'(追加包内,sounds_XXX为assets目录下你存放声音文件的目录名)或'rtm:xxx'(RTM 本体)
sound_DoorClose否String车门关闭时播放的音效 ID,格式同 sound_DoorOpen
sound_Acceleration否String列车牵引加速时播放的音效 ID,例如 VVVF 牵引逆变器升频声
sound_Deceleration否String列车减速/电气制动时播放的音效 ID,例如 VVVF 回馈制动降频声
sound_Stop否String列车完全停稳时播放的音效 ID,例如气动制动缓解声
sound_BrakeRelease否String制动缓解(松刹车)时的音效 ID,例如排风声
sound_BrakeRelease2否String第二段制动缓解音效 ID,用于分段缓解声效果——先松一部分再全松
sound_Horn否String鸣笛音效 ID。
sound_Announcement否String[][]报站广播列表,格式为 [['显示名称','音效ID'],...]。玩家在列车 GUI 中看到显示名称,点击后播放对应的音效
sound_D_S否String行驶中的特殊音效 ID,例如磁轨制动声、齿轮箱啮合声等附加行驶音效
sound_S_A否String启动加速辅助音效 ID(Station Acceleration),用于播放列车起步时的辅助提示音
rollsignTexture否String方向幕(列车前/侧/尾部的路线显示屏)的贴图 PNG 路径,贴图中按 UV 坐标排列了各路线名称的图案
rollsignNames否String[]方向幕上的可选路线/目的地名称列表 ['None','站A','站B',...]。名称顺序决定了按方向幕切换按钮时循环的路线顺序
rollsigns否Object[]方向幕显示位置的定义数组。每项包含:uv[4](贴图 UV 坐标 [uMin,uMax,vMin,vMax],范围 0.0-1.0)、pos[4][3](四角顶点三维坐标)、doAnimation(是否逐行滚动切换)、disableLighting(是否忽略环境光照始终全亮)
wheelRotationSpeed否Float车轮自转速度的倍率系数。取值范围为 0.016 到 0.8。
vertexShaderPath否String顶点着色器 GLSL 脚本路径,用于自定义模型每个顶点的位置变换(shader-test 系列列车使用)。文件路径相对于 assets/minecraft/
accelerateion否Float加速度值。注意 JSON 配置键的拼写为 accelerateion(NGT的拼写错误),单位为m/tick²
type否String列车子类型标识字符串(部分测试/特殊列车使用)。取值如内部测试标识等,非必需键。注意此 type 是列车 JSON 顶层的键,与 Machine/Machine 中的 type 无关

5.2 轨道 — ModelRail_*.json (13 键)

{ "railName": "1435mm_PC", "model": { "modelFile": "ModelRail_1435mm.mqo", "textures": [["default", "textures/rail/largeRailPC.png", ""]], "rendererPath": "scripts/RenderRailStandard.js" }, "buttonTexture": "textures/rail/button_1435mm_PC.png", "defaultBallast": [{ "blockName": "gravel", "blockMetadata": 0, "height": 0.0625 }], "accuracy": "LOW", "tags": "ngt5479 1435mm PC" }
键必需类型说明
accuracy否String(LOW/MEDIUM/HIGH)碰撞检测精度,可选 LOW(低精度/高性能)、MEDIUM(中等)、HIGH(高精度/低性能),轨道一般设为 LOW
allowCrossing否Boolean是否允许行人(NPC/玩家)横穿这条轨道,true=允许横穿,false=禁止横穿
ballastWidth否Int道砟的宽度,用于无模型轨道的道砟渲染
blockMetadata否String道砟方块的元数据值,对于有不同变体的方块用于指定具体子类型
blockName否String道砟方块的 Minecraft 注册 ID,如 gravel(砂砾)、stone(石头)或 air(无道砟)
buttonTexture否String(path)创造模式物品栏中显示的图标 PNG 路径
defaultBallast否Object默认道砟设置数组,每个元素包含 blockName(方块 ID,如 gravel)、blockMetadata(方块元数据)、height(道砟高度,单位米)
height否Float/Float[]道砟层的高度,单位是米。0.0625 对应 Minecraft 的 1/16 格
model否Object模型定义对象,包含 modelFile(MQO/MQOZ/OBJ 模型路径)、textures(材质映射数组)和可选的 rendererPath(渲染脚本路径)
polygonType否String轨道的多边形渲染类型,控制轨道截面形状
railModel否Object轨条的独立模型路径,与主模型分开的单独轨条模型(部分轨道类型使用)
railName否String轨道在游戏内显示的名称,建议包含轨距信息(如 1435mm_PC 表示标准轨混凝土轨枕)
railTexture否String(path)轨条的独立贴图路径,与 railModel 配对使用
rendererPath否String(path)自定义渲染 JS 脚本路径,用于实现道岔切换动画等效果
tags否String创造模式物品栏中的分类标签,相同 tag 的物品归为一组

5.3 Connector — ModelConnector_*.json (12 键)

{ "name": "Input01", "model": { "modelFile": "Connector.mqo", "textures": [["default", "textures/connector/input.png", ""]] }, "buttonTexture": "textures/connector/button_Input01.png", "connectorType":"Input", "wirePos":[0.0, -0.25, 0.0], "smoothing": false, "doCulling": true, "accuracy": "LOW", "tags": "ngt5479" }
键必需类型说明
accuracy否String(LOW/MEDIUM/HIGH)碰撞检测精度,LOW/MEDIUM/HIGH
buttonTexture否String(path)创造模式物品栏图标路径
connectorType否String连接器类型:Relay=中继连接器(两端均可接线)、RelaySN=侧式中继、Input=输入端(接受信号/能量输入)、Output=输出端(发送信号/能量)、NoModel=无形连接点(无模型仅用于接线)
doCulling否Boolean是否启用背面剔除优化,墙上的连接器建议设为 true,独立支柱建议 false
model否Object模型定义对象,含 modelFile、textures 和可选的 rendererPath
name否String物品在游戏内显示的名称
smoothing否Boolean是否启用模型平滑着色,true=平滑过渡法线,false=保留面片棱角
tags否String物品栏分类标签
textures否String(path)材质映射数组 [[材质名, 贴图路径, 渲染选项],...],材质名必须与 MQO 模型中的材质名称完全一致
useCustomColor否Boolean是否允许玩家在游戏中自定义物品的颜色
wirePos否Float/Float[]线缆接入点的三维坐标 [x,y,z],相对于物品的本地坐标系,决定了线缆从哪个位置引出

5.4 Machine — ModelMachine_*.json (19 键)

{ "name": "Turnstile01", "model": { "modelFile": "ModelMachine_Turnstile01.mqo", "textures": [["default", "textures/machine/turnstile_green.png", "Light", "textures/machine/turnstile_light0.png"]], "rendererPath": "scripts/RenderTurnstile01.js" }, "buttonTexture": "textures/machine/button_Turnstile01.png", "machineType": "Turnstile", "sound_OnActivate": "minecraft:random.door_open", "smoothing": true, "doCulling": true, "accuracy": "LOW", "tags": "ngt5479" }
键必需类型说明
accuracy否String(LOW/MEDIUM/HIGH)碰撞精度 LOW/MEDIUM/HIGH
brightness否Float/Float[]机器的自发光亮度值,范围 0.0-1.0,用于显示器屏幕、灯具等发光设备
buttonTexture否String(path)物品栏图标路径
defaultData否Object默认可编程数据数组,每个元素包含 key(数据键名)、type(数据类型)和 value(默认值)
defaultValues否Object默认值配置数组,每个元素除了 key/type/value 外,还可包含 minmax(取值范围 [最小,最大])、suggestions(建议值列表)和 pattern(格式模式)
doCulling否Boolean背面剔除
followRailAngle否Boolean是否自动跟随轨道方向旋转朝向,true=机器角度随轨道方向调整(用于 ATC 装置、止冲挡等安装在轨道上的设备)
key否StringdefaultData/defaultValues 中的数据键名标识符
machineType否String机器功能类型标识:Antenna_Send=信号发送天线、Turnstile=闸机、Gate=屏蔽门、TicketVendor=自动售票机、Noticer=列车接近报警器、Point=转辙机、CrossingGate=道口栏木机、TrainDetector=列车检测器、SoundSignal=音响信号机、Speaker=扬声器/喇叭、Vendor=自动贩卖机、Windmill=风车、BeamGenerator=光束发生器、LighthouseLamp=灯塔灯、RevolvingLight=旋转警示灯、SearchLight=探照灯、CameraMonitor/PictureMonitor/ThinMonitor/ThinEBB/TweetMonitor=各类显示器/信息屏、MirrorBall=镜球灯、Torii=鸟居、TrainWasher=洗车机、BumpingPost=止冲挡、ATC=列车自动控制装置
minmax否Float[]数值型数据的取值范围 [最小值, 最大值]
model否Object模型定义对象,含 modelFile、textures 和可选的 rendererPath
name否String机器在游戏内显示的名称
pattern否String文本型数据的输入格式验证模式
renderAABB否String自定义渲染包围盒 [minX, minY, minZ, maxX, maxY, maxZ],超出此范围的模型部分会被裁剪掉,用于优化大型设备的渲染性能
rendererPath否String(path)自定义渲染 JS 脚本路径,用于实现闸机开合、屏蔽门滑动等动画效果
rotateByMetadata否Boolean是否根据放置时的方块元数据(metadata)自动旋转朝向,true=右键放置时自动面向玩家
serverScriptPath否String(path)服务端脚本路径,用于实现需要服务器端处理的逻辑
smoothing否Boolean模型平滑着色
sound_OnActivate否String机器被触发/激活时播放的音效 ID,例如 'minecraft:random.door_open'
sound_Running否String机器持续运行时的音效 ID,适用于道口栏木机等有持续动作的设备
suggestions否String输入时显示的建议值列表
tags否String分类标签
textures否String(path)材质映射 [[材质名,路径,选项],...]
type否String数据类型标识,用于 defaultData/defaultValues 中定义值的类型(如 string/int/float)
useCustomColor否Boolean允许玩家自定义颜色
value否String默认值或当前值

5.5 Wire — ModelWire_*.json (12 键)

{ "name":"BasicWireBlack", "model":{ "modelFile": "Model_none.mqo", "textures": [["default", "textures/wire/simpleCatenary.png", ""]], "rendererPath": "scripts/RenderBasicWire.js" }, "buttonTexture": "textures/wire/button_BasicWireBlack.png", "deflectionCoefficient":0.125, "lengthCoefficient":0.0625, "doCulling": false, "sectionLength":1.0 }
键必需类型说明
buttonTexture否String(path)物品栏图标路径
deflectionCoefficient否Float/Float[]线缆的下垂系数,0.0 表示完全绷直(接触网承力索),正值越大下垂幅度越大(如馈线、链条)
doCulling否Boolean线缆建议设为 false(双面渲染),否则从某些角度看线缆会消失
lengthCoefficient否Float/Float[]线缆的长度倍率,用于调整线缆在两端 Connector 之间的分段密度,数值越小分段越密
model否Object模型定义对象,含 modelFile、textures 和可选的 rendererPath
name否String线缆在游戏内显示的名称
rendererPath否String(path)线缆渲染脚本路径,用于实现接触网的动态视觉效果
sectionLength否Float/Float[]每小段线缆的长度(单位:米),默认 1.0,影响线缆的平滑程度
smoothing否Boolean平滑着色
textures否String(path)材质映射 [[材质名,路径,选项],...]
useCustomColor否Boolean允许玩家自定义线缆颜色
yOffset否Float/Float[]线缆的 Y 轴位置偏移量,用于微调线缆在高度方向上的位置

5.6 信号机 — ModelSignal_*.json (12 键)

{ "signalName": "3color", "signalModel": "ModelSignal_3color.mqo", "lightTexture": "textures/signal/colorSignal_3c.png", "modelPartsFixture": {"objects":["pole","body2"],"pos":[0,0,0]}, "modelPartsBody": {"objects":["body","light1","light2","light3"],"pos":[0,0,0.425]}, "lights": ["S(1) I(0) P(light1)","S(3) I(0) P(light2)"], "rotateBody": true, "smoothing": true, "doCulling": true, "tags": "ngt5479 3color" }
{ "signalName": "3color", "signalModel": "ModelSignal_3color.mqo", "signalTexture": "textures/signal/colorSignal.png", "lightTexture": "textures/signal/colorSignal_3c.png", "buttonTexture": "textures/signal/button_3c.png", "modelPartsFixture": { "objects": ["pole", "body2"], "pos": [0.0, 0.0, 0.0] }, "modelPartsBody": { "objects": ["body", "light1", "light2", "light3"], "pos": [0.0, 0.0, 0.425] }, "lights": [ "S(1) I(0) P(light1)", "S(3) I(0) P(light2)", "S(5) I(0) P(light3)" ], "rotateBody": true, "smoothing": true, "doCulling": true, "tags": "ngt5479 3color" }
键必需类型说明
buttonTexture否String(path)物品栏图标路径
doCulling否Boolean背面剔除
lights否String[]灯位定义字符串数组,每条格式为 S(信号态编号) I(初始显示态) P(对应部件名),例如 'S(1) I(0) P(light1)' 表示信号态 1 时点亮 light1 部件
lightTexture否String(path)灯光贴图路径,这张贴图的不同区域对应不同的灯位,通过 UV 坐标控制各灯位的显示图案
modelPartsBody否String信号机的可旋转灯体部件定义对象,结构与 modelPartsFixture 相同。灯体会跟随 rotateBody 设置旋转
modelPartsFixture否String信号机的固定部件定义对象,包含 objects(固定部件的 MQO 对象名列表)和 pos(位置偏移 [x,y,z])
objects否String[]模型部件名列表
pos否Float/Float[]位置偏移 [x,y,z]
rendererPath否String(path)渲染脚本路径(部分复杂信号机使用)
rotateBody否String灯体是否跟随朝向旋转,true=灯体(含 lights 灯位)始终面向轨道方向
signalModel否Object信号机模型的 MQO 路径
signalName否String信号机在游戏内的名称标识
signalTexture否String(path)信号机主体贴图路径
smoothing否Boolean平滑着色
tags否String分类标签

5.7 货柜 — ModelContainer_*.json

货柜是可以放在平板货车上运输的集装箱。它使用独立的模型/贴图字段(containerModel/containerTexture)而非通用的 model 对象。

{ "containerName": "19g_JRF_0", "containerModel": "ModelContainer_19g.obj", "containerTexture": "textures/container/19g_JRF_0.png", "containerWidth": 3.0, "containerHeight": 2.5, "containerLength": 3.71, "doCulling": true, "tags": "ngt5479 19G" }
{ "containerName": "19g_JRF_0", "containerModel": "ModelContainer_19g.obj", "containerTexture": "textures/container/19g_JRF_0.png", "buttonTexture": "textures/container/button_19g_JRF_0.png", "containerWidth": 3.0, "containerHeight": 2.5, "containerLength": 3.71, "doCulling": true, "tags": "ngt5479 19G" }
键必需类型说明
buttonTexture否String(path)物品栏图标路径
containerHeight否Float/Float[]货柜的高度(单位:米),标准 20 英尺货柜一般为 2.5 米
containerLength否String货柜的长度(单位:米),20 英尺标准货柜约为 6.058 米
containerModel否Object货柜专用的 MQO/OBJ 模型路径,注意它不使用通用 model 对象,而是用这个独立字段单独指定
containerName否String货柜在游戏内显示的名称
containerTexture否String(path)货柜专用的贴图路径,与 containerModel 配对使用
containerWidth否String货柜的宽度(单位:米),标准货柜宽度为 2.438 米(8 英尺)
doCulling否Boolean背面剔除,设为 false 时货柜内部可见
model否Object通用模型定义对象(部分货柜使用,含 modelFile、textures、rendererPath)
rendererPath否String(path)渲染脚本路径(使用通用 model 时)
tags否String分类标签
textures否String(path)材质映射(使用通用 model 时)

5.8 武器 — ModelFirearm_*.json

武器是可旋转可射击的防御装置,包括舰炮、近防炮和多管火箭等。武器通过四层级部件模型(N→Y→X→Barrel)实现多轴旋转控制。

{ "firearmName": "Phalanx", "model": {"modelFile":"ciws.mqo","textures":[["default","textures/firearm/ciws.png",""]],"rendererPath":"scripts/RenderPhalanx.js"}, "modelPartsN":{"objects":[],"pos":[0,0,0]}, "modelPartsY":{"objects":[],"pos":[0,0,0]}, "modelPartsX":{"objects":[],"pos":[0,2.5,0]}, "modelPartsBarrel":{"objects":[],"pos":[0,2.5,0]}, "muzzlePos":[0,2.5,1.98], "playerPos":[0,2.5,0], "yaw":[180,-180], "pitch":[80,-20], "rotationSpeedY":1,"rotationSpeedX":1, "recoil":0, "ammoType":4, "rateOfFire":2, "magazineSize":300, "fpvMode":true, "smoothing":true, "doCulling":true, "tags":"ngt5479" }
{ "firearmName": "Phalanx", "model": { "modelFile": "ciws.mqo", "textures": [["default", "textures/firearm/ciws.png", ""]], "rendererPath": "scripts/RenderPhalanx.js" }, "buttonTexture": "textures/firearm/button_Phalanx.png", "modelPartsN": { "objects": [], "pos": [0.0, 0.0, 0.0] }, "modelPartsY": { "objects": [], "pos": [0.0, 0.0, 0.0] }, "modelPartsX": { "objects": [], "pos": [0.0, 2.5, 0.0] }, "modelPartsBarrel": { "objects": [], "pos": [0.0, 2.5, 0.0] }, "muzzlePos": [0.0, 2.5, 1.98], "playerPos": [0.0, 2.5, 0.0], "yaw": [180.0, -180.0], "pitch": [80.0, -20.0], "rotationSpeedY": 1.0, "rotationSpeedX": 1.0, "recoil": 0.0, "ammoType": 4, "rateOfFire": 2, "magazineSize": 300, "fpvMode": true, "smoothing": true, "doCulling": true, "tags": "ngt5479" }
键必需类型说明
ammoType否String弹药类型编号,不同的整数代表不同的弹种(1=小型弹、2=中型弹等),影响伤害和弹道
buttonTexture否String(path)物品栏图标路径
doCulling否Boolean背面剔除
firearmModel否Object武器专用的 MQO 模型路径,与通用的 model 对象二者选一
firearmName否String武器在游戏内的显示名称
firearmTexture否String(path)武器专用的贴图路径
fpvMode否Boolean是否支持第一人称瞄准视角,true=玩家按 Shift 可切换到瞄准镜视角
magazineSize否Float/Float[]弹匣或弹鼓的弹药容量,打空后需要时间装填,期间无法射击
model否Object通用模型定义对象(含 modelFile、textures、rendererPath),与 firearmModel/firearmTexture 二选一
modelPartsBarrel否String炮管部件对象,可以独立于俯仰面做更精细的角度调整,结构与 modelPartsN 相同
modelPartsN否String底座部件对象,包含 objects(部件名列表)和 pos(位置偏移 [x,y,z]),底座不参与旋转
modelPartsX否String俯仰旋转层部件对象,绕 X 轴(水平轴)做俯仰旋转,结构与 modelPartsN 相同
modelPartsY否String水平旋转层部件对象,绕 Y 轴(竖直轴)做水平旋转,结构与 modelPartsN 相同
muzzlePos否Float/Float[]炮口位置的三维坐标 [x,y,z],弹道粒子和发射效果从这个位置生成
objects否String[]模型部件名列表
pitch否Float/Float[]俯仰角限制范围 [最小值, 最大值],正值为向上仰、负值为向下俯,例如 [80, -20] 表示可上仰 80 度、下俯 20 度
playerPos否Float/Float[]玩家操作武器时的站立位置 [x,y,z]
pos否Float/Float[]位置偏移 [x,y,z]
rateOfFire否Int每分钟的射速,数值越大射击间隔越短,连续射击会产生热量积累
recoil否Float/Float[]后坐力系数,影响连续射击时炮口的抬升幅度和散布增大
rendererPath否String(path)渲染脚本路径(武器旋转动画等)
rotationSpeedX否String俯仰旋转速度系数
rotationSpeedY否String水平旋转速度系数,数值越大瞄准时转动越灵敏
smoothing否Boolean模型平滑着色
tags否String分类标签
textures否String(path)材质映射
yaw否Float/Float[]水平旋转角度限制范围 [最小值, 最大值],例如 [-180, 180] 表示全向旋转,限制过大会影响瞄准范围

5.9 载具 — ModelVehicle_*.json

载具指非铁路类的可驾驶车辆——坦克、汽车、船只、矿车、盾构机等。

{ "name": "trolley", "model": {"modelFile":"trolley.mqoz","textures":[["default","textures/vehicle/trolley.png",""]]}, "vehicleType":"Trolley","size":[1,1], "playerPos":[[0,0.5,0]],"vibration":[0,0.01],"connectionDistance":0.75, "useCustomColor":true,"smoothing":true,"doCulling":true, "changeYawOnStopping":false,"accuracy":"MEDIUM", "collisionParts":["-all"], "tags":"ngt5479" }
{ "name": "trolley", "model": { "modelFile": "trolley.mqoz", "textures": [["default", "textures/vehicle/trolley.png", ""]] }, "buttonTexture": "textures/vehicle/button_trolley.png", "vehicleType": "Trolley", "size": [1.0, 1.0], "playerPos": [[0.0, 0.5, 0.0]], "vibration":[0.0, 0.01], "connectionDistance":0.75, "useCustomColor": true, "smoothing": true, "doCulling": true, "changeYawOnStopping": false, "accuracy": "MEDIUM", "collisionParts": ["-all"], "tags": "ngt5479" }
键必需类型说明
acceleration否Float/Float[]加速度系数,影响载具从静止到最高速度的快慢
accuracy否String(LOW/MEDIUM/HIGH)碰撞精度 LOW/MEDIUM/HIGH
buttonTexture否String(path)物品栏图标路径
changeYawOnStopping否Boolean设为 true 时载具停止后保持当前朝向不回正,false=停下来后自动回正方向
collisionParts否String[]参与物理碰撞的模型部件名列表,例如 ['-all'] 表示所有部件参与碰撞,也可以指定具体部件名精确定义碰撞范围
color否String默认载具颜色 [R,G,B] (0-255)
connectionDistance否Float/Float[]多节载具连接间距(米)
customButtons否String[]自定义 GUI 按钮文本列表
customButtonTips否String自定义按钮的提示文本
doCulling否Boolean背面剔除
friction否String地面摩擦力系数,影响载具的制动距离和转向抓地力
gripPos否Float/Float[]抓地力检测点位置,决定载具与地面的接触判定点
guiScriptPath否String(path)自定义 GUI 脚本路径
guiTexture否String(path)自定义 GUI 背景贴图路径
headLights否String[]头灯位置
hoveringSpeed否String悬浮/飞行载具的悬停速度
maxSpeed否Float/Float[]最高速度限制
maxYaw否Float/Float[]最大转向角度限制
model否Object模型定义对象,含 modelFile 和 textures
name否String载具在游戏内显示的名称
offset否Float/Float[]模型整体位置偏移 [x,y,z]
pitchCoefficient否Float/Float[]俯仰系数,影响载具上下坡时的车身姿态
playerPos否Float/Float[]玩家驾驶/乘坐时的位置坐标 [[x,y,z],...],多个值时玩家随机分配到其中一个
pos否Float/Float[]位置偏移 [x,y,z]
r否String旋转角度 [俯仰, 偏航, 横滚]
rendererPath否String(path)自定义渲染 JS 脚本路径,用于实现特殊动画效果
rollCoefficient否String侧倾系数,影响载具转弯时的车身倾斜程度
scale否Float/Float[]模型缩放倍率
serverScriptPath否String(path)服务端脚本路径
size否Float/Float[]载具的碰撞盒尺寸 [宽度, 高度],单位是米,决定载具能否通过狭窄通道或低矮隧道
slotPos否Float/Float[]载具内部物品槽位位置,用于存储系统
smoothing否Boolean平滑着色
soundScriptPath否String(path)运行时音效脚本路径
tags否String分类标签
tailLights否String[]车尾灯位置 [[x,y,z],...]
textures否String(path)材质映射
type否String类型标识(部分载具使用)
useCustomColor否Boolean允许玩家自定义载具的颜色
vehicleType否String载具的分类标识字符串,不同的类型决定不同的驾驶方式和物理行为(如 Car、Trolley、Tank、Ship 等)
vibration否Float/Float[]行驶时的震动参数 [振幅, 频率],振幅控制颠簸幅度,频率控制抖动的快慢(高频低幅=路面细碎震动,低频高幅=越野摇晃)
yawCoefficient否Float/Float[]转向灵敏度系数,数值越小转向越迟钝(适合重型车辆),数值越大转向越灵敏(适合轻型车辆)

5.10 机械传动部件 — ModelMechanism_*.json

机械部件用于 RTM 的动力传输系统,包括齿轮、离合器、减速器、电机、滑轮、传动轴和换向器。

{ "name": "clutch", "model": {"modelFile":"clutch.mqoz","textures":[["default","textures/mechanism/clutch.png",""]],"rendererPath":"scripts/RenderClutch.js"}, "type":"TRANSMISSION", "transmissionRatioON":[0,0,0,0,0],"transmissionRatioOFF":[1,0,0,0,0], "maxSpeed":10000,"acceleration":0,"radius":0, "smoothing":true,"doCulling":true,"accuracy":"MEDIUM", "tags":"ngt5479" }
{ "name": "clutch", "model": { "modelFile": "clutch.mqoz", "textures": [["default", "textures/mechanism/clutch.png", ""]], "rendererPath": "scripts/RenderClutch.js" }, "buttonTexture": "textures/mechanism/button_clutch.png", "type": "TRANSMISSION", "transmissionRatioON": [0.0, 0.0, 0.0, 0.0, 0.0], "transmissionRatioOFF": [1.0, 0.0, 0.0, 0.0, 0.0], "maxSpeed": 10000.0, "acceleration": 0.0, "radius": 0.0, "smoothing": true, "doCulling": true, "accuracy": "MEDIUM", "tags": "ngt5479" }
键必需类型说明
acceleration否Float/Float[]加速能力系数,数值越大部件从静止到最高转速的响应速度越快
accuracy否String(LOW/MEDIUM/HIGH)碰撞精度 LOW/MEDIUM/HIGH
buttonTexture否String(path)物品栏图标路径
doCulling否Boolean背面剔除
maxSpeed否Float/Float[]部件的最高转速限制,单位 RPM(转每分钟),超过此转速可能导致机械损坏
model否Object模型定义对象,含 modelFile、textures 和 rendererPath
name否String机械部件在游戏内显示的名称
radius否Float/Float[]部件的物理半径,用于碰撞检测和齿轮啮合时的视觉对齐
rendererPath否String(path)渲染脚本路径,用于实现齿轮旋转动画等
smoothing否Boolean平滑着色
tags否String分类标签
teethCount否Int齿数(仅用于齿轮和滑轮),两个啮合齿轮的 teethCount 比值决定实际传动比
textures否String(path)材质映射
transmissionRatioOFF否Float[]未激活状态(离合器分离)时的传动比五元数组,结构与 transmissionRatioON 相同,用于空转/分离状态
transmissionRatioON否Float[]激活状态下的传动比五元数组 [档1, 档2, 档3, 档4, 档5],五个元素对应五个不同的传动档位,数值越大该档位的输出扭矩越高
type否String部件的机械类型标识,TRANSMISSION=传动组件,不同的类型影响 RTM 引擎对部件的物理计算方式

5.11 NPC — ModelNPC_*.json

RTM 内置了 NPC 系统——乘务员、守卫、乘客、售货员、猫女仆等。NPC 有两种制作方式:使用 texture 单贴图的简易平面 NPC,或使用 model 对象的完整 3D 模型 NPC。

{ "model": {"modelFile":"cat_maid.mqo","textures":[["default","textures/npc/cat_maid.png"]],"rendererPath":"scripts/RenderBiped.js"}, "name":"CatMaid", "role":"salesperson", "smoothing":true,"doCulling":true }
{ "model": { "modelFile": "cat_maid.mqo", "textures": [["default", "textures/npc/cat_maid.png"]], "rendererPath": "scripts/RenderBiped.js" }, "name": "CatMaid", "buttonTexture": "textures/npc/button_cat_maid.png", "role": "salesperson", "doCulling": true, "smoothing":true }
键必需类型说明
buttonTexture否String(path)创造模式物品栏图标路径
damage否StringNPC 每次攻击造成的伤害值,仅对攻击型 NPC(guard 守卫)有效
doCulling否Boolean背面剔除
health否StringNPC 的生命值,受到攻击后减少,归零时 NPC 死亡并从世界中消失
lightTexture否String(path)发光层贴图路径,在 NPC 身上叠加一层不受光照影响的半透明发光层,用于角色眼睛、名牌等特殊效果
model否Object3D 模型 NPC 的模型定义对象,含 modelFile、textures 和 rendererPath。使用 model 的 NPC 需要完整的 3D 模型和骨骼动画脚本
name否StringNPC 在游戏内显示的名称
rendererPath否String(path)NPC 动画脚本路径,用于实现行走、呼吸、交互等动画效果
role否String行为角色标识字符串,不同的值决定不同的 AI 行为:attendant=乘务员(跟随玩家交互)、buyer=买家(收购物品)、guard=守卫(巡逻攻击)、passenger=乘客(随机走动)、salesperson=售货员(固定交易)、mannequin=假人(站立不动)
smoothing否Boolean平滑着色
speed否StringNPC 的移动速度倍率,1.0 为默认速度,小于 1.0 走得慢,大于 1.0 走得快
texture否String(path)简易 NPC 使用的平面贴图路径,使用此字段时 NPC 不需要 3D 模型,只用一张带透明通道的 PNG 贴图显示
textures否String(path)材质映射
useCustomColor否Boolean允许玩家自定义 NPC 的贴图颜色

5.12 装饰物 — ModelOrnament_*.json

装饰物是纯视觉效果物品,用于场景美化——树木、荧光灯、铁架、电线杆、管道、脚手架、自动扶梯等。

{ "name":"Tree01", "model":{"modelFile":"wood01.mqo","textures":[["default","textures/ornament/wood01.png",""]]}, "ornamentType":"Plant", "offset":[0,-0.5,0], "renderAABB":[-10,0,-10,11,15,11], "minRandomScale":0.75, "smoothing":true,"doCulling":true, "tags":"ngt5479" }
{ "name":"Tree01", "model":{ "modelFile": "wood01.mqo", "textures": [["default", "textures/ornament/wood01.png", ""]] }, "buttonTexture": "textures/ornament/button_Tree01.png", "ornamentType": "Plant", "doCulling": true, "smoothing":true, "offset": [0.0, -0.5, 0.0], "renderAABB": [-10.0, 0.0, -10.0, 11.0, 15.0, 11.0], "minRandomScale": 0.75 }
键必需类型说明
buttonTexture否String(path)物品栏图标路径
conveyorSpeed否Float/Float[]自动扶梯类装饰物的传送带移动速度,扶梯台阶表面根据此值驱动运动
doCulling否Boolean背面剔除
minRandomScale否Float/Float[]放置时的最小随机缩放倍率。系统在每个放置位置从 minRandomScale 到 1.0 之间随机取值,对模型进行缩放,实现同一棵植物有高矮大小的自然差异
model否Object模型定义对象,含 modelFile 和 textures
name否String装饰物在游戏内显示的名称
offset否Float/Float[]放置时的位置偏移量 [x,y,z],用于微调装饰物相对于方块中心的位置,植物类通常向下偏移使底部对齐地面
ornamentType否String装饰物的分类标识字符串,影响 RTM 对它的渲染和放置方式。Plant=植物类(支持随机缩放),LinePole=线杆类(可悬挂线缆)
renderAABB否String手动指定的渲染包围盒 [minX, minY, minZ, maxX, maxY, maxZ],超出该范围的模型部分会被裁剪掉。合理设置可以大幅减少不必要的渲染计算
rendererPath否String(path)自定义渲染脚本路径,用于实现装饰物的动态效果(如风车旋转、荧光灯闪烁)
smoothing否Boolean平滑着色
textures否String(path)材质映射 [[材质名,路径,选项],...]
useCustomColor否Boolean允许玩家自定义装饰物的颜色
6音效制作与注册

RTM 支持两种音效方式:sounds.json 注册的静态音效(报站、开关门声、运行声)和 JS 脚本控制的运行时音效(牵引/制动变频声)。

音效波形 / AUDIO DEMO

6.0 音效

RTM 追加包的音效系统是一个三步流程:准备音频文件 → 放入正确目录 → 在 sounds.json 中注册。下面从零演示完整流程。

第一步——在 assets 下创建音效目录结构。在追加包的 assets/sounds/ 下新建一个子目录用于存放音效文件。建议按类别分子目录:

assets/sounds/ ├── sounds.json ← 音效注册表(第二步创建) └── sounds/ ← 所有音效 .ogg 文件都放在这里 ├── train/ ← 列车运行声(NJ01A.ogg, NJ01H.ogg 等) ├── station/ ← 报站/车站广播(goinghome1.ogg 等) └── door/ ← 开关门声(door.ogg)

第二步——放入音频文件。将处理好的所有 .ogg 文件按类别放入 sounds/ 的对应子目录中。文件路径中的目录名必须全部小写且不含空格。

第三步——在 sounds.json 中注册。创建 assets/sounds/sounds.json,为每个音频文件定义一个唯一的音效 ID(建议采"类别.名称"格式),并指定其相对于 sounds/ 子目录的路径(不带 .ogg 扩展名)。详细格式见 6.2 节。

引用规则:在 JSON 和 JS 脚本中引用音效时,使用格式 命名空间:音效ID,例如 "sounds:train.NJ01H" 或 "rtm:train.door"。其中 sounds: 代表 assets/sounds/ 目录(即存放音效文件的目录),这是追加包内音效的标准命名空间;rtm: 代表 RTM 模组自带的音效;minecraft: 代表原版 Minecraft 音效。

6.1 OGG 格式要求

音频必须使用 OGG Vorbis 格式(.ogg),采样率 44100Hz,单声道或立体声均可。推荐使用 Adobe Audition 进行音频处理和导出——在 Audition 中完成剪辑后,通过 File → Export → Multitrack Mixdown → Entire Session,格式选择 FLAC/Ogg Vorbis (.ogg),质量设为 0.5 即可在音质和文件大小之间取得平衡。导出后的 .ogg 文件放入 assets/sounds/sounds/ 目录。

6.2 sounds.json 注册表

位置:assets/sounds/sounds.json。每行定义一个音效 ID:

{ "train.NJ01H": { // 音效 ID(建议 "类别.名称" 格式) "category": "neutral", // 类别:neutral=不受游戏音量选项影响 "sounds": ["train/NJ01H"] // ogg 文件路径(不含 .ogg,相对于 sounds/ 子目录) }, "train.door": { "category": "neutral", "sounds": ["train/door"] } }

在 JSON 中引用时使用 sounds:音效ID(追加包内音效)、rtm:音效ID(RTM 本体音效)或 minecraft:音效ID(原版音效)。注意 sounds.json 中的路径不要带 .ogg 扩展名。

6.3 运行时音效脚本(soundScriptPath)

在列车 JSON 中通过 "soundScriptPath": "scripts/sound_NJ01A.js" 指定。核心函数为每帧调用的 onUpdate(su),其中 su 是 SoundUpdater 对象,提供以下 API:

API说明
su.getSpeed()列车当前速度,单位是 m/s(米每秒)
su.getNotch()手柄级位:正值为牵引级位,负值为制动级位,0 为惰行
su.isComplessorActive()空压机是否工作中,返回布尔值(true/false)
su.complessorCount()空压机启动后的运行计数值
su.playSound('sounds', 'train.NJ01A', vol, pitch)播放音效。参数:命名空间、音效 ID、音量(0.0-1.0)、音高(倍率)
su.stopSound('sounds', 'train.NJ01A')停止播放指定的音效
提示:NJ01 地铁的 VVVF 变频声就是通过对速度分段来动态计算 vol 和 pitch 实现的——低速时播放低频牵引声,速度升高后逐渐升频,切换到制动时则降低音高模拟回馈制动的声音。参考 sound_NJ01A.js 中的分段逻辑。
[图片占位:Audacity 中编辑列车运行声的界面截图]
7渲染JS

渲染JS可以为追加包添加一些天马行空的功能,包括但不限于可动驾驶台、自定义电显、ATP系统......但这些功能的实现往往伴随着复杂的语法,所以我们建议具备一定编程基础的人来学习本章节。

7.0 文件位置

新建 .js 文件,将 .js 文件放在 assets/minecraft 下的 scripts文件夹,并在 .json 中用 trainModel2下的rendererPath 指定。

7.1 最小 JS 脚本模板

var renderClass = "jp.ngt.rtm.render.MachinePartsRenderer"; importPackage(Packages.org.lwjgl.opengl); importPackage(Packages.jp.ngt.rtm.render); var main; function init(par1, par2) { main = renderer.registerParts(new Parts("body")); } function render(entity, pass, par3) { GL11.glPushMatrix(); if (pass == 0) { main.render(renderer); } GL11.glPopMatrix(); }
关键代码解释
var renderClass = "..."宣告脚本用于哪种物品——必须是 11.0 节中列出的 10 个全限定类名之一
importPackage(...)导入扩展库:org.lwjgl.opengl=3D数学运算,jp.ngt.rtm.render=RTM API
renderer.registerParts把 MQO 对象名注册为 Parts 组。后续用 .render(renderer) 渲染
render(entity,pass,par3)每帧调用。entity=物品实例,pass=渲染通道(0/1/2),par3=补间参数
GL11.glPushMatrix()保存当前 3D 变换状态
GL11.glPopMatrix()恢复保存的状态

7.2 可导入的包速查(importPackage)

RTM 的 JS 引擎是 Java 的 Nashorn(JDK 8 内置的 JavaScript 引擎),这意味着 JS 脚本可以直接调用任何 Java 类。使用 importPackage() 或 importClass() 导入后,即可在脚本中使用该 Java 包中的所有类和方法。以下为 RTM 内置 78 个脚本中实际使用的 20 个包:

导入语句使用次数用途
Packages.org.lwjgl.opengl62OpenGL 3D 图形库——所有 glTranslatef/glRotatef/glPushMatrix 等 3D 变换函数。渲染 JS 必须导入
Packages.jp.ngt.rtm.render63RTM 渲染器核心包——包含 Parts, renderer.* 等所有渲染 API。渲染 JS 必须导入
Packages.jp.ngt.ngtlib.renderer28NGTLib 渲染工具——NGTRenderHelper, NGTTessellator(曲面细分)等高阶图形工具
Packages.jp.ngt.ngtlib.math21数学运算库——NGTMath 的 getSin/getCos/toDegrees 等角度与三角函数
Packages.jp.ngt.rtm.block8RTM 方块 API——装饰物/管道类脚本用于访问方块状态,如 entity.getAttachedSide()
Packages.jp.ngt.rtm.entity.train.util6列车实体工具——TrainState(列车状态枚举)、Formation(编组)等
Packages.jp.ngt.rtm.rail.util5轨道工具——RailDir(道岔方向枚举)、RailPosition(轨位)等
Packages.jp.ngt.ngtlib.math.NGTMath5NGTMath 类的所有静态方法——与 importPackage 版本相同但更精确
Packages.jp.ngt.rtm.render.Parts4Parts 类独立导入——可以直接 new Parts(...) 而不需要包名前缀
Packages.jp.ngt.ngtlib.util5NGTLib 工具集——MCWrapper(Minecraft 包装器,访问世界/实体/粒子等)
Packages.org.lwjgl.input3LWJGL 输入库——Keyboard.isKeyDown() 检测键盘按键
Packages.jp.ngt.ngtlib.io2NGTLib 输入输出——NGTLog(调试日志)等
Packages.jp.ngt.ngtlib.renderer.NGTRenderer5NGTRenderer 类独立导入——renderWebCamera/renderMap 等显示器功能
Packages.jp.ngt.ngtlib.renderer.GLHelper1GLHelper 类独立导入——disableLighting/enableLighting 等光照控制
Packages.jp.ngt.rtm1RTM 根包——访问 RTM 核心类的顶级接口
Packages.jp.ngt.rtm.sound1RTM 音效 API——音效播放相关的底层接口
Packages.net.minecraft.tileentity1Minecraft 方块实体——TileEntity(方块实体基类),用于访问方块的 NBT 数据
Packages.net.minecraft.util1Minecraft 工具库——ChatAllowedCharacters 等游戏内工具类
Packages.org.lwjgl.util.vector1LWJGL 向量数学——Vector2f/Vector3f 等三维向量运算
importPackage vs importClass:importPackage("Packages.org.lwjgl.opengl") 导入整个包(所有类都可用),而 importClass("Packages.org.lwjgl.opengl.GL11") 只导入 GL11 这一个类。使用 importClass 可以避免类名冲突,但需要为每个用到的类单独导入。

7.3 renderClass:10 种类型一览

#renderClass适用物品脚本数
1MachinePartsRenderer闸机、屏蔽门、旋转灯、显示器等18
2VehiclePartsRenderer列车和载具:车门、车轮、履带、受电弓16
3RailPartsRenderer轨道:道岔切换和轨道变形7
4OrnamentPartsRenderer装饰物:荧光灯、管道、脚手架、扶梯10
5NPCPartsRendererNPC:二足角色、人形生物3
6FirearmPartsRenderer武器:近防炮、舰炮、多管火箭3
7MechanismPartsRenderer机械传动:齿轮、离合器、电机5
8WirePartsRenderer线缆:接触网电线、馈线、链条3
9SignalPartsRenderer信号机:色灯信号机、时钟1
10PartsRenderer上述9种基类——通用API均可用—

7.4 渲染通道(RenderPass 枚举)

RenderPass 定义了渲染通道,来源: jp.ngt.rtm.render.RenderPass 枚举类。在 JS 中可通过 RenderPass.名称.id 获取数值:

名id渲染内容
NORMAL0不透明物体——车身、门、座位。最常用
TRANSPARENT1半透明物体——玻璃窗户,需 AlphaBlend
LIGHT2发光物体——头灯、信号灯,不受时间天气影响
LIGHT_BACK3发光物体背面——前灯亮时同时渲染尾灯,来源: Render223.js 等列车脚本
OUTLINE4轮廓线——选中有碰撞体积的物品时显示线框,来源: Render223.js
PICK5拾取判定——用于方块高亮和右键交互,来源: Render223.js
标准 JS 中既可以用 pass == 0 也可以用 pass == RenderPass.NORMAL.id。复杂列车动画通常同时覆盖 4-6 个 pass 值。

7.5 GL11 变换操作

方法作用
GL11.glPushMatrix()保存状态(所有脚本使用)
GL11.glPopMatrix()恢复状态
GL11.glTranslatef(x,y,z)平移(米)
GL11.glRotatef(a,x,y,z)绕(x,y,z)轴旋转a度
GL11.glScalef(x,y,z)缩放(1.0=原大)
GL11.glEnable/glDisable启用/禁用 GL_TEXTURE_2D 等
GL11.glColor4f(r,g,b,a)颜色RGBA(0-1)
GL11.glBlendFunc(a,b)透明混合 GL_SRC_ALPHA/GL_ONE_MINUS_SRC_ALPHA
GL11.glDepthMask(f)深度写入开关
GL11.glShadeModel(m)着色模式 GL_SMOOTH/GL_FLAT

7.6 通用 API(所有 renderClass 可用)

API说明来源
renderer.registerParts(new Parts(...))注册部件组所有脚本
renderer.bindTexture(Material)绑定材质/贴图LibRenderRail.js
renderer.renderLightEffect(pass)渲染发光效果RenderLighthouseLamp.js
renderer.renderLightSurface(pass)渲染发光面RenderLighthouseLamp.js
renderer.setBrightness(int)设置亮度LibRenderRail.js
renderer.setLightmapMaxBrightness()最大光照(pass=2用)RenderTorii.js
renderer.getWorld(entity)获取Minecraft世界对象LibRenderRail.js
renderer.getMinecraft()获取客户端实例EntityPartsRenderer
renderer.getConfig()获取物品配置对象RenderBevelGear.js
renderer.getModelName()获取模型名RenderClock.js
renderer.getModelObject()获取模型对象(含纹理信息)LibRenderRail.js
renderer.getColor(tileEntity)自定义颜色值RenderBasicWire.js
renderer.getMetadata()方块metadata(0-15)RenderBeamGenerator.js
renderer.currentMatId当前材质IDrender_crh2c_test.js
renderer.isPowered()红石信号激活?RenderClutch.js
renderer.isRidden(entity)玩家正乘坐?RenderNGT-18.js
renderer.getX/Y/Z(entity)世界坐标RenderMonorail.js
renderer.getSystemTime()系统时间(毫秒)PartsRenderer
renderer.getSystemHour/Minute/Second系统时/分/秒RenderClock.js
renderer.getSystemMillisecond系统毫秒值RenderShieldMachine.js
renderer.getSystemTimeMillis()毫秒时间戳RenderBeamGenerator.js
renderer.getMCHour/Minute游戏内时间RenderClock.js
renderer.getViewerVec()玩家视线方向PartsRenderer
renderer.setData(entity,key,val)保存持久化数据PartsRenderer
renderer.getData(entity,key)读取持久化数据PartsRenderer
renderer.renderItem(item, pass)渲染物品栏物品RenderC20FT_NC.js
renderer.sigmoid(x)平滑插值函数(S型曲线)Render223.js
renderer.renderWireDeflection(tileEntity,connection,vec,par8)渲染线缆下垂(仅Wire类)RenderIllumination.js

7.7 各类型专用 API

renderClass专用 API 及说明 (来源)
MachinegetMovingCount(entity): 开合0.0-1.0(RenderTurnstile01.js)。getLightState(entity): 灯0/1(RenderBeamGenerator.js)。getTick(entity): tick累计20tick=1秒。getYaw/Pitch(entity): 偏航/俯仰角。getNormal(entity): 法线方向(RenderLighthouseLamp.js)。getLightPos(): 灯位坐标。getRotation(entity,Axis): 指定轴旋转(RenderWindmill.js)。rotate(degs,axis,x,y,z): 绕轴旋转(RenderTurnstile01.js)
VehiclegetWheelRotationR/L(entity): 左右轮角度(RenderTest4.js)。getDoorMovementL/R(entity): 门滑动0-1(RenderOhmi220.js)。getPantographMovementFront/Back(entity): 受电弓升降(Render223.js)。getBogieId(entity): 转向架ID。getPlayerYaw(entity): 乘客视角(RenderTank.js)。getTrain(entity): 编组对象(Render223.js)。getWheelRotationR 也可以在 entity 上直接获取(entity.wheelRotationR, RenderMechaCentipede.js)
RailisSwitchRail(tileEntity): 是否道岔(LibRenderRail.js)。renderStaticParts(tileEntity,x,y,z): 固定部分渲染。renderRailMapStatic(...): 轨道段渲染。shouldRenderObject(...): 可选渲染判断。getModelObject(): 模型对象。getRailPositions/Pitch/Rotation/Height/Brightness: 轨道属性
OrnamentgetDir(entity): 朝向0-7对应8方向(RenderFluorescent.js)。isOpaqueCube(entity): 不透明方块?(RenderClock.js)
NPCsetRotationAngles(entity,tick): 设置身体旋转。rotateAndRender(parts,x,y,z,ax,ay,az): 旋转+渲染部位。18角度属性: headAngleX/Y/Z, bodyAngleX/Y/Z, rightArmAngleX/Y/Z, leftArmAngleX/Y/Z, rightLegAngleX/Y/Z, leftLegAngleX/Y/Z(RenderBiped.js)
Firearmentity.getBarrelYaw(): 炮管水平角。entity.getBarrelPitch(): 俯仰角。entity.getRecoil(): 后坐力(RenderPhalanx.js)
MechanismgetRotation(entity,Axis): 指定轴旋转。Axis: POSITIVE_X/NEGATIVE_X/POSITIVE_Y/NEGATIVE_Y/POSITIVE_Z/NEGATIVE_Z(RenderBevelGear.js)
WiregetColor(tileEntity): 颜色值(RenderBasicWire.js)。NGTTessellator.instance: 曲面细分。renderWireStatic/Dynamic 特有函数签名
SignalisOpaqueCube(entity): 不透明方块?。getMCMinute/MCHour: 游戏时间用于时钟(RenderClock.js)

7.8 扩展工具库 API(GLHelper, NGTMath, MCWrapper, NGTRenderer, Keyboard, NGTLog, PooledVec3, tessellator)

API说明来源脚本
GLHelper.disableLighting()禁用光照(用于pass=2发光渲染)RenderMechaCentipede.js
GLHelper.enableLighting()启用光照RenderMechaCentipede.js
GLHelper.preMoveTexUV(cfg,id)纹理UV预移动Render223.js
GLHelper.postMoveTexUV(cfg,id)纹理UV后移动Render223.js
GLHelper.setColor(i,r,g,b)设置渲染颜色RenderIllumination.js
NGTMath.getSin/Cos(deg)正弦/余弦(度输入)RenderMechaCentipede.js
NGTMath.getSinD/CosD(deg)正弦/余弦(度输入,D版本)LibBeamEffect.js
NGTMath.sin/cos(rad)正弦/余弦(弧度输入)RenderNGT-18.js
NGTMath.toDegrees(rad)弧度转度RenderHumanoid.js
NGTMath.toRadians(deg)度转弧度RenderHumanoid.js
NGTMath.getAtan2D(y,x)反正切(度输出)RenderTank.js
NGTMath.normalizeAngle(a)角度归一化到0-360RenderMechaCentipede.js
NGTMath.RANDOM随机数生成器RenderRailStandardGrass.js
NGTRenderer.renderDesktop(w,h)渲染桌面画面到方块RenderCameraMonitor.js
NGTRenderer.renderEBB(w,h)渲染电子公告板RenderThinEBB.js
NGTRenderer.renderFire()渲染火焰效果RenderNGT-18.js
NGTRenderer.renderMap()渲染地图RenderMapMonitor.js
NGTRenderer.renderPicture(w,h)渲染图片RenderPictureMonitor.js
NGTRenderer.renderTweet()渲染Twitter推文RenderTweetMonitor.js
NGTRenderer.renderWebCamera(w,h)渲染网络摄像头RenderCameraMonitor.js
MCWrapper.getWorld(entity)通过entity获取世界RenderNGT-18.js
MCWrapper.getPosX/Y/Z(entity)通过entity获取坐标sound_crusader.js
MCWrapper.getPitch/Yaw(entity)通过entity获取角度sound_crusader.js
MCWrapper.getDistanceSq(e1,e2)两实体距离平方RenderTorii.js
MCWrapper.getRandom(world)获取随机生成器RenderNGT-18.js
MCWrapperClient.playSound(...)客户端播放音效RenderNGT-18.js
MCWrapperClient.spawnParticle(...)生成粒子效果RenderTank.js
MCWrapperClient.execCommand(cmd)执行Minecraft命令RenderTorii.js
MCWrapperClient.getPlayer(world)获取当前玩家RenderTorii.js
Keyboard.isKeyDown(key)键盘按键是否按下RenderNGT-18.js
Keyboard.KEY_F / KEY_*键盘按键常量RenderTank.js
NGTLog.debug(msg)输出调试日志RenderTank.js
NGTLog.sendChatMessageToAll(m)发送聊天消息render_crh2c_test.js
PooledVec3.create(x,y,z)创建可复用三维向量RenderHumanoid.js
NGTTessellator.instance曲面细分器实例RenderBasicWire.js
tessellator.startDrawing(mode)开始绘制(GL_TRIANGLE_STRIP等)LibBeamEffect.js
tessellator.startDrawingQuads()开始四边形绘制RenderSimpleCatenary.js
tessellator.addVertex(x,y,z)添加顶点LibBeamEffect.js
tessellator.addVertexWithUV(x,y,z,u,v)添加带UV的顶点RenderSimpleCatenary.js
tessellator.setColorOpaque_I(i)设置不透明颜色(int)RenderBasicWire.js
tessellator.setColorRGBA_I(i,a)设置颜色+透明度LibBeamEffect.js
tessellator.draw()提交绘制RenderBasicWire.js

7.9 列车/载具渲染详解(VehiclePartsRenderer)

VehiclePartsRenderer 是最复杂也最常用的渲染器——RTM 内置 16 个脚本使用它渲染 223 系、ohmi 系、kiha 系、N700 系等列车以及坦克、台车等载具。重要:许多列车动画通过 entity.* 直接调用而非 renderer.*。以下是列车渲染的核心模式:

entity 对象完整 API(列车运行时数据)

来源: Render223.js(RenderOhmi220.js(RenderN2100.js 等 16 个列车脚本。这些方法直接从 entity 读取列车实时状态:

entity 方法/属性说明
entity.getResourceState()获取资源状态对象——从中可以读/写跨帧持久化数据(61+脚本使用,最常用)
entity.getNotch()牵引/制动级位。正值=牵引(1-5级)、负值=制动、0=惰行。用于播放对应加速/减速声
entity.getSpeed()当前速度(m/s)。配合 getNotch 实现VVVF变频声
entity.getTrainDirection()列车运行方向。0=正向、1=反向。用于判断前后端
entity.getConnectedTrain(dir)获取指定方向的邻接车厢。null=该方向无连接。用于判断车头/车尾
entity.getVehicleState(TrainStateType)获取车辆状态。TrainStateType 包含: State_TrainDir(方向)/State_Notch(级位)/State_Speed(速度)/State_Door(门状态)/Destination(目的地)等
entity.getSignal()/getSignal2()获取列车信号状态。在 pass=PICK 中用于判断列车外部高亮
entity.getFormation()获取编组数据——编组中每节车厢的类型和顺序。用于编写连挂识别逻辑
entity.getRollsignAnimation()方向幕滚动动画值。返回浮点型进度用于UV滚动
entity.getSeatRotation()座位旋转角度。用于翻转座椅朝向
entity.getCouplerYaw()车钩偏航角。前后车厢连接处的转角。用于绘制连接处管道/篷布
entity.train列车编组对象(属性)。可从中获取编组中任一节车厢
entity.wheelRotationR车轮旋转角度(属性)。等效 renderer.getWheelRotationR(entity)
entity.getDir()朝向(0-7对应8方向)。Ornament/装饰物类中广泛使用
entity.getMoveDir()移动方向(±1)。-1=后退,+1=前进。用于坦克履带方向
entity.isControlCar()是否为控制车(带驾驶室)。非控制车不显示方向幕
entity.isConnected(side)指定侧是否已连接。用于管道/连接件的自动延展
entity.syncNotch()同步手柄级位到客户端。防止多人游戏时不同步
entity.setVehicleState(type)设置车辆状态。用于自定义列车行为
entity.getResourceState().getDataMap()获取跨帧数据映射——可在其中读写自定义键值对实现复杂状态机(如坦克炮塔角度、射击冷却)
entity.getX/Y/Z()列车当前坐标(动态位置)

列车渲染实战: 完整示例(基于 Render223.js, RenderMechaCentipede.js, RenderOhmi220.js)

// === 列车渲染核心骨架 (VehiclePartsRenderer) === var renderClass = "jp.ngt.rtm.render.VehiclePartsRenderer"; importPackage(Packages.org.lwjgl.opengl); importPackage(Packages.jp.ngt.rtm.render); var body, body_light, head, head_light, tail, doorL, doorR; function init(p1, p2) { body = renderer.registerParts(new Parts("body")); body_light = renderer.registerParts(new Parts("body_light")); head = renderer.registerParts(new Parts("head")); head_light = renderer.registerParts(new Parts("head_light")); tail = renderer.registerParts(new Parts("tail")); doorL = renderer.registerParts(new Parts("doorL")); doorR = renderer.registerParts(new Parts("doorR")); } function render(entity, pass, p3) { GL11.glPushMatrix(); var dir = (entity != null) ? entity.getTrainDirection() : 0; var frontEmpty = (entity == null || entity.getConnectedTrain(dir) == null); var backEmpty = (entity == null || entity.getConnectedTrain(1-dir) == null); if (pass == RenderPass.NORMAL.id) { body.render(renderer); if (frontEmpty) head.render(renderer); // 无前车=头车,显示车头 if (backEmpty) tail.render(renderer); // 无后车=尾车,显示车尾 // 车门动画 var doorMoveL = renderer.getDoorMovementL(entity); GL11.glPushMatrix(); GL11.glTranslatef(doorMoveL, 0, 0); doorL.render(renderer); GL11.glPopMatrix(); var doorMoveR = renderer.getDoorMovementR(entity); GL11.glPushMatrix(); GL11.glTranslatef(doorMoveR, 0, 0); doorR.render(renderer); GL11.glPopMatrix(); } else if (pass == RenderPass.LIGHT.id) { GLHelper.disableLighting(); // 发光通道:关环境光 body_light.render(renderer); if (frontEmpty) head_light.render(renderer); GLHelper.enableLighting(); } GL11.glPopMatrix(); }
车头/车尾判断模式:几乎所有列车脚本都使用 entity.getConnectedTrain(dir) == null 来判断是否为编组端点。前车为空=自己是车头(显示车头模型+车头灯),后车为空=自己是车尾(显示尾灯)。

7.10 dataMap 跨帧持久化数据详解

在 JS 脚本中,每一次 render() 调用都是一个独立的帧,帧与帧之间不共享变量。如果脚本需要在多帧之间记住某个状态(比如炮塔的当前角度、射击的冷却计时器、门的开关进度),就必须使用 dataMap。

dataMap 可以理解为「脚本的记事本」——每一帧你都可以往上面写东西,下一帧再翻出来读。RTM 会帮你保管这份数据,即使 Minecraft 重载资源包也不会丢失。

获取 dataMap

var dm = entity.getResourceState().getDataMap();

这行代码必须放在 render() 函数内部调用——因为 entity 参数在 init() 中不存在。

完整 API 参考

方法说明来源脚本
dm.getDouble(key)读取浮点数值。返回 double 类型,默认值 0.0RenderTank.js(4个不同key使用)
dm.getInt(key)读取整数值。返回 int 类型,默认值 0render_crh2c_test.js(最常见,60+处调用)
dm.getBoolean(key)读取布尔值。返回 true/false,默认值 falseRenderTank.js, RenderNGT-18.js
dm.getString(key)读取字符串值。默认值空字符串 ""RenderTweetMonitor.js, RenderPictureMonitor.js
dm.getHex(key)读取十六进制颜色值(如 0xFF00AA)。返回 int,默认 0xFFFFFF(白)RenderBeamGenerator.js(4个颜色key)
dm.setDouble(key, value, syncFlag)写入浮点数值。syncFlag 0=本地/1=同步到服务端/3=广播所有玩家RenderTank.js, render_crh2c_test.js
dm.setInt(key, value, syncFlag)写入整数值。syncFlag 同上,也可传 Boolean(true=同步)render_crh2c_test.js, RenderTorii.js
dm.setBoolean(key, value, syncFlag)写入布尔值。同上RenderTank.js, RenderNGT-18.js

syncFlag 同步标志详解

syncFlag含义使用场景
0仅写入本地,不通知服务端。最快视觉动画数据(炮塔旋转、履带进度)
1同步到服务端,服务端再分发给其他玩家炮口火焰标记(其他玩家需要看到开火)、启动方向
3不经过缓冲区,立即广播所有玩家。最慢缩放比例(RenderPalm.js)、门锁密码(RenderTorii.js)
true等效于 1,布尔参数便捷写法render_crh2c_test.js(5处使用)

实战案例一:坦克炮塔跟踪(RenderTank.js)

function render(entity, pass, p3) { GL11.glPushMatrix(); if (pass == 0) { body.render(renderer); // --- 获取 dataMap --- var dm = entity.getResourceState().getDataMap(); // --- 读取上一帧保存的炮塔角度 --- var yaw = dm.getDouble("turret_yaw"); // --- 如果有玩家乘坐,用玩家视角覆盖炮塔角度 --- if (renderer.isRidden(entity)) { yaw = renderer.getPlayerYaw(entity) - renderer.getYaw(entity); dm.setDouble("turret_yaw", yaw, 1); // syncFlag=1,其他玩家也要看到炮塔转动 } // --- 应用炮塔旋转 --- GL11.glPushMatrix(); GL11.glRotatef(yaw, 0.0, 1.0, 0.0); turret.render(renderer); GL11.glPopMatrix(); // --- 读取射击冷却——冷却期间禁止开火 --- var fireCount = dm.getInt("fire_count"); if (fireCount <= 0) { // 冷却完毕,允许下次射击 } } GL11.glPopMatrix(); }

实战案例二:帧差时间计算(RenderTank.js 履带动画)

// 每帧计算上一帧到当前帧经过了多长时间 var dm = entity.getResourceState().getDataMap(); var msec = renderer.getSystemTimeMillis() % 60000; // 当前系统时间(每分钟重置) var prevTime = dm.getInt("prev_time"); // 上一帧的时间 var dif = msec - prevTime; // 帧间时间差(毫秒) dm.setInt("prev_time", msec, 0); // 保存本帧时间给下一帧 // 用帧差计算履带累积位移 var speed = entity.getSpeed(); amount += speed * (dif / 50) * 0.25; // 速度 × 时间 = 位移 dm.setDouble("mov_amount", amount, 0); // 保存累积位移
为什么不能直接用 getTick():getTick 返回的是从游戏开始累计的 tick 数,不会重置。而帧差计算需要精确到毫秒的时间差,所以用 getSystemTimeMillis() 配合 dataMap 存储上一帧的时间戳。这个模式在 RenderTank.js, RenderNGT-18.js, Render223.js 等多个脚本中出现。

实战案例三:编组级数据同步(render_crh2c_test.js)

// 自定义的 syncDataMap 函数——同步数据到编组中的所有车厢 function syncDataMap(entity, key, value, toServer) { var formation = entity.getFormation(); // 获取编组 var flag = toServer ? 1 : 0; for (var i = 0; i < formation.size(); ++i) { var entry = formation.get(i); // 遍历编组中每一节车厢,写入相同的数据 entry.train.getResourceState().getDataMap().setInt(key, value, flag); } // 同时也要写入自己的 dataMap entity.getResourceState().getDataMap().setInt(key, value, flag); }

这个模式展示了如何通过 entity.getFormation() 遍历编组中的所有车厢,并将同一个值写入每一节车厢的 dataMap。用于实现编组内状态同步——例如所有车厢的方向幕显示相同的路线、所有车厢的灯光状态一致。

key 的命名规范

dataMap 的 key 是完全自定义的字符串,RTM 不限制内容。以下是 78 个脚本中常见的命名模式:

key 示例存储内容数据类型
"turret_yaw"炮塔/武器水平角度Double
"fire_count" / "fired"射击冷却/扳机状态Int / Boolean
"prev_time" / "recentTick"上一帧时间戳(帧差计算用)Int
"mov_amount"履带累积位移Double
"start_notch" / "start_time"启动时的级位/时间(音效脚本用)Int
"input_num" / "password"用户输入数字/密码Int
"signal" / "preSignal"信号状态/前一帧信号Int
"scale" / "sizeX" / "sizeY"缩放/尺寸(显示器脚本用)Double
"url" / "keyword" / "type"URL/关键词/类型(显示器脚本用)String
"insideColor" / "outsideAlpha"内外颜色/透明度(光束效果用)Hex

7.11 交互逻辑教程——拖拽与点击检测

除了渲染视觉效果之外,RTM 的 JS 脚本也能响应玩家的交互操作——右键点击部件、拖拽操纵杆、打开 GUI 等。这些交互通过以下几套机制实现。

7.11.1 ActionParts——可交互部件的注册

普通的 Parts 只能被渲染,不能响应点击或拖拽。要让某个部件变成可交互的,必须使用 ActionParts 类。来源: render_crh2c_test.js(全功能测试列车):

importPackage(Packages.jp.ngt.rtm.render); // 注册可拖拽的操纵杆 var mascon = renderer.registerParts(new ActionParts(ActionType.DRAG_X, "mascon")); var brake = renderer.registerParts(new ActionParts(ActionType.DRAG_X, "brake")); // 注册普通可点击部件(不需要拖拽) var button = renderer.registerParts(new Parts("button_start"));
ActionType含义使用场景
DRAG_X沿 X 轴拖拽列车操纵杆(前后推拉)、制动阀
DRAG_Y沿 Y 轴拖拽升降式操纵杆、油门杆
DRAG_Z沿 Z 轴拖拽侧推杆
CLICK单击按钮、开关(用普通 Parts 即可)

7.11.2 右键交互回调函数

RTM 提供了几个特定名称的函数,只要在脚本中定义了这些函数,RTM 就会在对应事件发生时自动调用。来源: render_crh2c_test.js, RenderTorii.js:

回调函数触发时机参数说明
onRightClick(entity, parts)玩家右键点击某个部件时entity=被点击的物品实例, parts=被点击的 ActionParts 对象
onRightDrag(entity, parts, move)玩家右键拖拽某个部件时move=拖拽位移量(float),正负表示方向
onRightClick(entity)玩家右键点击物品本体(非特定部件)时entity=被点击的物品实例

7.11.3 完整实战:列车操纵杆交互

以下示例来自 render_crh2c_test.js,展示了如何用 ActionParts + onRightDrag 实现列车的牵引手柄(mascon)和制动阀(brake)的拖拽操作:

var renderClass = "jp.ngt.rtm.render.VehiclePartsRenderer"; importPackage(Packages.org.lwjgl.opengl); importPackage(Packages.jp.ngt.rtm.render); var mascon, brake; function init(par1, par2) { // 注册可拖拽部件 mascon = renderer.registerParts(new ActionParts(ActionType.DRAG_X, "mascon")); brake = renderer.registerParts(new ActionParts(ActionType.DRAG_X, "brake")); } // 右键拖拽时——根据拖拽方向改变列车级位 function onRightDrag(entity, parts, move) { var dm = entity.getResourceState().getDataMap(); if (parts == mascon) { // 操纵杆(牵引手柄):往前推=加速,往后拉=制动 if (move > 0.05) { entity.addNotch(1); // 往前推一格,牵引级位+1 } else if (move < -0.05) { entity.addNotch(-1); // 往后拉一格,制动级位+1 } } else if (parts == brake) { // 制动阀:独立制动 var currentBrake = dm.getInt("brake_level"); currentBrake += (move > 0) ? 1 : -1; dm.setInt("brake_level", currentBrake, 0); } }

move 值的解读:move 是本次拖拽的累积位移量(float),不是拖拽的方向。如果 move > 0 表示正方向拖拽,move < 0 表示反方向拖拽。通常配合阈值判断(如 Math.abs(move) > 0.05)来防止微小的鼠标移动触发操作。

7.12 常见模式

动效实验室 / MOTION LAB
DOOR / 车门滑动
WHEEL / 车轮旋转
VVVF / 变频声

车门滑动 (来源: Render223.js)

var move = renderer.getMovingCount(entity) * speed * len; GL11.glTranslatef(move, 0.0, 0.0); door.render(renderer);

车轮旋转 (来源: RenderTest4.js)

var angle = renderer.getWheelRotationR(entity); GL11.glTranslatef(0, wheelY, wheelZ); GL11.glRotatef(angle, 1.0, 0.0, 0.0); GL11.glTranslatef(0, -wheelY, -wheelZ); wheel.render(renderer);

switch-case 根据模型名切换 (来源: Renderdoor.js)

switch (renderer.getModelName()) { case "Hd1L": doorLength = -1.0; doorSpeed = 1.5; break; case "Hd1R": doorLength = 1.0; doorSpeed = 1.5; break; }

VVVF 变频声 (来源: sound_223.js)

var speed = entity.getSpeed(); var notch = entity.getNotch(); if (notch > 0) { var pitch = (speed + 30) / 90; // 音高随速度升频 var vol = (speed < 20) ? (speed-10)/12.5 : 0.8; su.playSound("sounds", "train.NJ01A", vol, pitch); }

使用 dataMap 持久化数据 (来源: RenderTank.js)

var dm = entity.getResourceState().getDataMap(); var yaw = dm.getDouble("turret_yaw"); // 读取炮塔角度 dm.setDouble("turret_yaw", newYaw, 1); // 保存(1=同步到服务器) dm.setBoolean("fired", true, 0); // 保存(0=不同步)
学习顺序: Ornament(Machine(Firearm/Vehicle(MachinePartsRenderer(NPC(Wire(Signal(Rail()。建议从最简单的 OrnamentPartsRenderer 开始——只需要 body 和 light 两个部件。然后学 MachinePartsRenderer(会滚动的关机和 rotate),再挑战 VehiclePartsRenderer(列车渲染——8 通道、编组判断、方向幕、受电弓等),最后才接触 RailPartsRenderer(可从 LibRenderRail.js 复制现成代码)。

7.13 实战合集:常见功能的完整实现

场景一:右键点击按钮切换列车状态

// 在 init 中注册按钮部件 var btn = renderer.registerParts(new Parts("button_start")); // 定义右键点击回调 function onRightClick(entity, parts) { if (parts == btn) { var dm = entity.getResourceState().getDataMap(); var isRunning = dm.getBoolean("engine_running"); dm.setBoolean("engine_running", !isRunning, 1); // 切换状态并同步 } }

场景二:渲染物品栏内物品(集装箱类)

来源: RenderC20FT_NC.js。货柜/集装箱内部可以放置物品,JS 脚本通过 renderer.renderItem() 将其中一个物品渲染出来:

function render(entity, pass, par3) { GL11.glPushMatrix(); if (pass == 0) { body.render(renderer); // 渲染集装箱内第 0 个槽位的物品 var slotIndex = column * 9 + row; // 9 格宽度的网格布局 renderer.renderItem(renderer.getInventoryItem(entity, slotIndex)); } GL11.glPopMatrix(); }

场景三:密码锁门(RenderTorii.js)

鸟居(Torii)方块通过 dataMap 实现了一个四位密码输入系统。右键点击时累计输入数字,达到 4 位后与预设密码比对:

function onRightClick(entity) { var dm = entity.getResourceState().getDataMap(); var input = dm.getInt("input_num"); var mode = dm.getInt("mode"); if (mode == 0) { // 输入模式:每次右键累加一位 input = input * 10 + 1; // 简化,每次按1 if (input >= 10000) { // 4位输入完毕,检查密码 if (input == 1234) { // 预设密码 dm.setInt("mode", 1, 3); // 切换到解锁模式 } else { input = 0; // 密码错误,清零 } } dm.setInt("input_num", input, 3); } }
8测试与发布

8.1 本地测试流程

  1. 将追加包文件夹或 JAR 放入 .minecraft/mods/ 目录
  2. 启动游戏,进入创造模式世界
  3. 在 RTM 物品栏中按 tags 找到你的物品
  4. 轨道:放置后检查外观和道岔动画
  5. 列车:右键轨道放置列车,右键列车打开 GUI 测试报站/灯光/座位
  6. 接触网:放置 Connector 后连接 Wire

8.2 常见问题排查

现象可能原因解决方法
物品不显示 / 无名称JSON 文件名不符合前缀规范确认文件名以 ModelTrain_ 或 ModelRail_ 等正确前缀开头
模型显示为紫黑方块MQO 文件路径错误或文件不存在检查 modelFile 路径与实际文件位置是否一致,注意大小写
贴图全白贴图路径错误或 MQO 材质名与 JSON 不匹配确认 textures 数组中的材质名与 MQO 模型中的材质名完全一致
车门不滑动未绑定 JS 渲染器,或 Parts 注册的对象名不匹配确认 rendererPath 正确指定;Parts('door') 中的名字与 MQO 对象名一致
音效不播放未在 sounds.json 注册或文件格式不对确认 OGG 格式正确,sounds.json 路径不包含 .ogg 扩展名
JS 报错语法错误或 renderClass 不正确查看 .minecraft/logs/latest.log 中的 JavaScript 错误栈
列车无法编组isSingleTrain 设为 true将 isSingleTrain 改为 false 允许挂多节

8.3 发布检查清单

  • ✅ JSON 文件名前缀正确(ModelTrain_ / ModelRail_ 等)
  • ✅ MQO / PNG / OGG 所有文件路径正确且存在
  • ✅ sounds.json 中所有用到的音效均已注册
  • ✅ JS 脚本可以在 Node.js 中测试基本语法
  • ✅ 在 Minecraft 1.12.2 中实测通过所有功能
  • ✅ 编写 ReadMe.txt 注明作者、适用版本和鸣谢

8.4 发布格式

将整个追加包文件夹打包为 .zip 或 .jar 文件发布。用户下载后将文件放入 mods 文件夹即可使用。注意压缩包内不要嵌套多余的目录层——解压后应直接看到 assets/、mods/ 和 ReadMe.txt。

学习路径建议:不要一上来就做完整列车。推荐从修改现有列车的贴图开始(替换 PNG 不改 JSON),然后新建一个简单的 Connector(如标志牌),再做带 JS 动画的 Machine(如闸机),有信心了再挑战完整列车。