# 动态显示屏 ```json { "custom_trains": { "display_test": { "display_slots": "mtr:display_test/slots_small.json", "display_content": "mtr:display_test/sink_test.json", ... } } } ``` 对任意列车设定一个槽位文件 (`display_slots`) 和一个显示内容控制文件 (`display_content`) 即可为其增加动态显示屏。 ## 槽位 NTE 通过在单独的文件中给出坐标的形式来指定动态显示屏的设置位置。这是为了使得设置更为灵活,以便便利地增加到已有模型的车辆上,同时这也和 RTM 的方向幕设定较为类似。 把它称为槽位。这是一个槽位文件内容的示例: ```json { "version": 1, "slots": [ { "name": "led_side", "pos": [ [[1.5, 2.6, -3.1], [1.5, 2.4, -3.1], [1.5, 2.4, -2.5], [1.5, 2.6, -2.5]], [[-1.5, 2.6, 3.1], [-1.5, 2.4, 3.1], [-1.5, 2.4, 2.5], [-1.5, 2.6, 2.5]] ], "offsets": [[0, 0, 0], [0, 0, 2]] }, { "name": "led_head", "pos": [[[0.3, 2.6, 9.2], [0.3, 2.3, 9.2], [-0.3, 2.3, 9.2], [-0.3, 2.6, 9.2]]] } ] } ``` 这个文件里设定了两个槽位。每个槽位可以算作一种显示内容,可以在下面的 `pos` 中让它在多个屏幕上显示,但是这些屏幕都只能显示相同的内容。 - `name` 是这个槽位的名称,以稍后在显示内容控制文件中使用。 - `pos` 是一个三层的数组(注意看括号的数量和分布,避免写错),来写明各个屏幕的位置。每个屏幕只能是矩形,对于每个屏幕,分别按对于其正面来说的右上、右下、左下、左上的顺序给出四个 XYZ 坐标点。坐标原点是列车中心、地板高度,X 正方向向左、Y 正方向向上、Z 正方向向后。 - `offsets` 是一个两层的数组,用于把 `pos` 指定的显示屏复制多份,以在如门上方闪灯图的场景中省字。分别给出要复制出的每份的 XYZ 偏移量。如果没有写 `offsets`,就不会去复制。 ## 基本显示内容 现实中动态显示屏的内容复杂多变,因此 NTE 提供了一个显示内容控制文件格式来让您详细地表示显示屏的显示逻辑。 ```json { "version": 1, "texture": "display_test.png", "texture_size": [ 512, 512 ], "templates": { "line_map_sse": { "class": "include", "source": "line_map_sse.json" } }, "logic": { "class": "draw", "slot": "led_side", "src_area": [ 0, 0, 100, 100 ], "dst_area": [ 0, 0, 1, 1 ] }, } ``` - `texture` 是显示屏内容所来自的贴图文件,只允许指定一个。在此处设置为它与显示内容控制文件的相对路径。 - `texture_size` 是这个贴图文件的尺寸。 - `logic` 描述屏幕上要显示什么样的内容,以下称为 “操作”。在这个例子里,它用图片中 (0,0) 到 (100,100) 的区域贴满屏幕。 - `template` 用来辅助说明一些复杂的绘图,以下称为 “模板”。 以下详细介绍操作和模板的使用。 ### Draw 操作 ```json { "class": "draw", "slot": "led_side", "src_area": [ 0, 0, 100, 100 ], "dst_area": [ 0, 0, 1, 1 ] } ``` 将原图中的一部分或全部贴到槽位的一部分或全部上。这里说贴到槽位上,就是指同样地贴到这个槽位里设置的所有显示屏上。 - `class` 是说明这是要进行哪一类操作,如 `"draw"` 就代表要进行这个贴图操作。以下不再赘述。 - `slot` 是要把图贴到哪个槽位上。 - `src_area` 是原图区域,依次指定左上角坐标、宽度和高度,单位是像素。 - `dst_area` 是目标区域,同样依次指定左上角坐标、宽度和高度。对于水平坐标,0 是最左侧,1 是最右侧;对于宽度,0 是没有,1 是显示屏宽度;垂直坐标正方向向下,以此类推。 ### Sequence 操作 ```json { "class": "sequence", "nodes": [ { "class": "draw", "slot": "lcd_door", "src_area": [ 0, 0, 100, 100 ], "dst_area": [ 0, 0, 0.25, 1 ] }, { "class": "draw", "slot": "lcd_door", "src_area": [ 0, 200, 100, 100 ], "dst_area": [ 0.5, 0, 0.5, 1 ] }, ... ] } ``` 显然只能贴一张图是不太够用的。`sequence` 操作可以套住多个操作,让它们从上到下依次执行。用这个例子作为 `"logic"` 的话,就会分别贴两次了。在对同一槽位多次贴图时,后处理(写得靠下)的会盖在先处理的上面。 ### Include 操作 ```json { "class": "include", "source": "hogehoge.json" } ``` 引入相对于当前文件目录的另一文件,以便将显示内容分入多个文件书写,条理更清晰。效果相当于以该文件内容整体替换掉这个块。在 `logic` 和 `templates` 里面都可以这样做。 ## 闪灯线路图 ### Draw_Line_Map 操作与 Line_Map 模板 ```json { "class": "draw_line_map", "template": "line_map_sse", "slot": "lcd_door", "target": "Icon Pier", "direction": "left", "dst_area": [ 0.2, 0, 0.6, 1 ] } ``` ```json { "class": "line_map", "src_area": [0, 0, 1536, 308], "variants_y": { "highlight": 308, "passed": 616 }, "animations": { "highlight": { "duration_on": 0.5, "duration_off": 0.5 }, "progress": { "type": "slide", "duration_on": 0.2, "duration_off": 1.2 } }, "capsule_width": 14, "capsules_x": { "Al-Tighnari": 224, "Inage Kaigan": 339, "Layla": 454, ... } } ``` ## 显示文字 与 MTR 原版的文字显示系统相比,NTE 允许您在显示文字时进行高级的排版,或动态地显示任意内容。 ### Draw_Free_Text 操作 ```json { "class": "draw_free_text", "slot": "lcd_door", "text": { "align_v_in": 1, "overflow_h": "fit", "parts": [ { "text": "$sta[0].cjk", "size_v": 0.5, "margin": [ 0, 0.01 ] }, { "text": "$sta[0].eng", "size_v": 0.2 } ] }, "dst_area": [ 0.01, 0, 0.18, 1 ] } ``` 此操作可显示任意文本。不过,由于动态文本渲染的技术局限,此操作比起其他绘制操作性能更低,且只能绘制在最上层(其他 Draw 操作即使在其之后进行也不能叠加在它上方)。 以上示例会以大字显示下一站的中文名,在其右侧以小字紧接显示英文名,文字会在 `dst_area` 中居中对齐,且字体会在无法容纳时自动缩小。接下来的“排版”和“内容”部分解释 `text` 里面的格式,以及如 `$sta[0].cjk` 表示的含义。 ### 排版 ```json { "align_h": 0, "align_v_out": 0, "align_v_in": 1, "overflow_h": "scroll", "scroll_speed": 0.1, "parts": [ { "text": "你好世界", "size_v": 0.5, "margin": [ 0, 0.05 ] }, { "text": "Hello World", "size_v": 0.2 } ] } ``` 可设定多部份左右相接,每部分可分别设定字体大小和颜色等,最后一起参与居中或其他对齐处理。`text` 中可以使用任意固定文字,也可以使用下述的 “可变内容”。 | 总体设定 | | | | -------------- | -------------------------- | ------------------------------------------------------------ | | align_h | 整体在显示区域的水平对齐 | -1: 左、0: 居中、1: 右,不填为居中 | | align_v_out | 整体在显示区域的垂直对齐 | -1: 上、0: 居中、1: 下,不填为居中 | | align_v_in | 各部分之间的垂直对齐 | -1: 上、0: 居中、1: 下,不填为居中 | | overflow_h | 整体宽度超出显示区域时处理 | none: 超出部分砍掉、stretch: 横向压缩
fit: 等比例缩小、scroll: 滚动、always_scroll: 不超出也滚动 | | scroll_speed | 如果滚动的话,滚动速度 | 倍槽位宽度每秒 | | **每部分设定** | | | | text | 这部分要显示的文字 | 详见下方“内容” | | size_v | 字体高度 | 倍槽位高度 | | color | 颜色 | 十六进制 RGB/RGBA 颜色值,如 `"FFFF00"`,不填为黑 | | margin | 与相邻元素左右边距 | [左, 右],单位是倍槽位宽度,不填为 0 | ### 可变内容 | 原样显示 | | | ----------------- | ------------------------------------------------------------ | | (不以 $ 开头) | 原样显示 | | **变量** | | | `$route[n]` | 取某站所属线路名;n 的含义在下方解释(没有这站时为空) | | `$sta[n]` | 取某站的名称(没有这站时为空) | | `$dest[n]` | 取在某站时的终点站显示(没有这站时为空) | | `$dist[n]` | 到某站的沿轨道距离(总是正数;没有这站时 99999999) | | `$door[side]` | 某侧车门是否打开(相对于模型的 Z 轴负方向)
side 处填 0 为任一侧,-1 为左,1 为右。打开时为 1,否则为空。 | | `$door_run[side]` | 同上,但相对于列车运行方向。 | | `$door_closing` | 如果车门正在关闭为 1,否则为空。 | | **后缀** | | | `$xxx.visible` | 只取某名称的中文和英文部分 | | `$xxx.cjk` | 只取某名称的中文部分 | | `$xxx.eng` | 只取某名称的英文部分 | | `$xxx.extra` | 只取某名称的隐藏部分 (\|\| 之后) | 上述提到的几个与“某站”有关的变量的内容是在出站时变为下一个,进站和停车期间均不改变。因此,`$sta[0]` 在区间里是下一站的站名,在站台上停靠期间是“这一站”的站名。`$sta[1]` 则是下一站的下一站,`$sta[-1]` 是上一站,其他变量和数值以此类推。 ## 逻辑 ### Cycle 操作 ```json { "class": "cycle", "nodes": [ { "duration": 1, "then": { "class": "draw", "slot": "lcd_door", ... } }, { "duration": 2, "then": { "class": "draw", "slot": "lcd_door", ... } }, ... ] } ``` 一些液晶显示屏会循环切换多个画面。`cycle` 会将多个显示操作按照 `duration` 中设定的秒数依次轮换进行。在以上的例子中,会有 1 秒只显示第一个 draw 的内容,不显示第二个 draw 的内容;接下来会有 2 秒只显示第二个 draw 的内容,不显示第一个 draw 的内容。 `then` 中不仅可使用 `draw`,也可以使用如 `sequence` 等其他操作。您可以这样通过多层嵌套来实现更复杂的效果。 如果需要一段时间不进行显示操作,用 `"then": null`。 ### Switch 操作 ```json { "class": "switch", "target": "$route[0].eng", "nodes": [ { "when": "Sightseeing Express Line", "then": { "class": "draw", ... } }, ... ] } ``` 这一操作从多个操作中按照一个 “可变内容” 的内容选出一个来显示。 将 `target` 设为要判断的可变内容,然后会选出 `when` 和这一内容相同(区分大小写)的一项。如果都不同,就不显示。也可以用 `"when": [ "1", "2" ]` 来匹配多个内容。 可以用于给不同线路或走向设定不同内容,或在不同站显示开门方向等。 ### If 操作 ```json { "class": "if", "nodes": [ { "when": "$dist[-1] < 200", "then": { 刚刚离站... } }, { "when": "", "then": { 行车中... } }, { "when": "$dist[0] < 200", "then": { 即将到站... } }, { "when": "$door[0]", "then": { 到站开门... } }, { "when": "$door_closing", "then": { 关门中... } } ] } ``` 这一操作从多个操作中按照一定条件选出一个来显示。 在 `when` 中设置条件。可使用以上介绍的 “可变内容” 写法。 - 当使用如 `$sta[0].eng == Spawn` 的写法时,将进行文字比较,这个条件将在下站英文名为 "Spawn" (区分大小写) 时成立。 - 当使用如 `$dist[0] < 200` 的写法时,将进行数值比较,这个条件将在到下一站的距离小于 200 米时成立(可使用 `<`、`<=`、`>=`、`>` 四种比较)。 - 当使用如 `$sta[0]` 的写法(不写任何运算)时,这个条件只要这个可变内容不是 “为空” 即成立。 - `"when": ""` 会被特殊处理。 当多个 `when` 的条件成立时,将显示最下面的一个。这使得各个条件可以以一种类似时间顺序的方式来写,如到站开门时 `$dist[0] < 200` 也符合,但 `$door[0]` 写在下面更优先;不过,这本质上也只是一个下方优先,而不是真的时间顺序,所以还请综合考虑各个条件成立的情况。 如果没有(除了 `"when": ""` 之外的)条件成立,那么将显示 `"when": ""` 所匹配的操作,不论它排在第几个;如果没有一项 `"when": ""` ,那这时就不显示。 ## 声音 TODO