快速上手

先決條件

工具版本說明
C++ 編譯器C++17MSVC(Windows)/ GCC / Clang 任一
CMake≥ 3.16推薦,但非硬性——SDK 是純標頭檔
JDK可選僅執行 SDK 自帶的 JVM 整合測試時需要
提示

SDK 全部以標頭檔提供(native/include/mtr/),沒有任何預編譯程式庫依賴;用你熟悉的建置系統直接把 include/ 加進包含路徑即可。

取得 SDK

SDK 隨每個 Release 發佈為 jcm-native-sdk-<版本>.zip(見 GitHub Releases 附件),內容即儲存庫 native/ 目錄原始碼:

jcm-native-sdk-2.3.0-1.0.2/
├── include/mtr/          # 全部 SDK 標頭檔(script.hpp 是總入口)
│   ├── mtr_native.h      # C ABI:匯出函式、快照、繪製記錄、宿主服務
│   ├── script.hpp        # 腳本基類 + 註冊巨集(#include 這一個就夠)
│   ├── vehicle.hpp  pids.hpp  lift.hpp  eyecandy.hpp
│   ├── gfx.hpp  gfx2d.hpp  font5x7.hpp  text.hpp  matrices.hpp
│   ├── api.hpp           # mtr::Api(完整 Java API 存取)
│   └── crash_guard.hpp ...
├── examples/             # 三個最小範例模組
├── bench/                # 測試與工具
└── CMakeLists.txt

建立模組專案

最省事的方式:把 SDK 解壓進你的資源包專案,直接在它的 CMakeLists.txt 末尾追加自己的目標——SDK 已經把各平台的連結細節(Windows 無前綴 .dll、macOS .dylib 與 -undefined dynamic_lookup、Android 16 KB 頁對齊)封裝成 mtr_add_script() 函式:

# 追加到 SDK 的 native/CMakeLists.txt 末尾
mtr_add_script(my_pids src/my_pids.cpp)

若想獨立建專案,下面是與該函式等價的完整模板:

cmake_minimum_required(VERSION 3.16)
project(my_native_scripts LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# SDK 標頭檔路徑(把解壓的 SDK include 目錄指進來)
add_library(mtr_script_sdk INTERFACE)
target_include_directories(mtr_script_sdk INTERFACE path/to/sdk/include)

function(mtr_add_script name)          # 與 SDK 同款的模組目標函式
  add_library(${name} MODULE ${ARGN})
  target_link_libraries(${name} PRIVATE mtr_script_sdk)
  set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
  if(WIN32)
    set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX ".dll")
  elseif(APPLE)
    set_target_properties(${name} PROPERTIES PREFIX "lib" SUFFIX ".dylib")
    target_link_options(${name} PRIVATE "-Wl,-undefined,dynamic_lookup")
  elseif(ANDROID)
    target_link_options(${name} PRIVATE "-Wl,-z,max-page-size=16384")
  endif()
endfunction()

mtr_add_script(my_pids src/my_pids.cpp)
說明

腳本是 MODULE 程式庫(不連結任何 SDK 二進位),預設符號隱藏、只匯出 ABI 要求的 7 個 mtr* 函式(由 script.hpp 的註冊巨集完成)。C++17 是最低標準。

第一個模組

一個最小的 PIDS 到站顯示模組——完整、可編譯、可直接進遊戲:

#include <mtr/script.hpp>

struct ArrivalState {};                    // 逐實例持久狀態(本例為空)

struct ArrivalDisplay : mtr::PidsScript<ArrivalState> {
    static constexpr auto ID = "example:arrivals";   // 必須與資源包宣告一致

    void render(mtr::PidsContext& ctx, ArrivalState&,
                const mtr::Pids& pids) override {
        for (int row = 0; row < pids.rows(); row++) {
            if (pids.is_row_hidden(row)) continue;

            std::string text = pids.get_custom_message(row).str();
            if (text.empty() && row < pids.arrivals().size())
                text = pids.arrivals().at(row).destination().str();

            if (!text.empty())
                mtr::Text::create()
                    .text(text.c_str())
                    .pos(2, row * 10.0 + 2)
                    .size(pids.width() - 4, 9)
                    .draw(ctx);
        }
    }
};

MTR_REGISTER_PIDS_SCRIPT(ArrivalDisplay)

要點:

  • 基類決定腳本類型:PidsScript / VehicleScript / LiftScript / EyecandyScript,模板參數是逐實例狀態類型。
  • 生命週期與 JS 相同:create / render / dispose 三個虛擬函式按需 override(全部可選)。
  • 註冊巨集展開全部 ABI 匯出,一個翻譯單元只註冊一個腳本。

更多範例見 SDK 的 examples/:車輛 / 電梯 / 裝飾三類各有最小樣例。

編譯

cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
cmake --build native/build --config Release --parallel 4
ctest --test-dir native/build -C Release --output-on-failure   # 可選:跑 SDK 自帶測試
目標平台產物說明
Windows x64my_pids.dllMSVC,/utf-8 由 SDK 專案預設
Linux x64 / ARM64libmy_pids.soGCC / Clang
macOS x64 / arm64libmy_pids.dylib用 -DCMAKE_OSX_ARCHITECTURES=arm64(或 x86_64)選擇架構,-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0
Android arm64libmy_pids.soNDK 工具鏈交叉編譯,API 21+;16 KB 頁對齊已由專案模板處理
iOS / iPadOS arm64libmy_pids.dylibMach-O 交叉編譯,無需簽署(側載時重新簽署)
提示

資源包內的程式庫按副檔名綁定平台(.dll → Windows、.so → Linux/Android、.dylib → macOS/iOS),具體目錄規則見下節。

打包進資源包

目錄佈局

把各平台編譯產物放進資源包的 natives/<platform>/ 目錄:

my_pack/
├── natives/
│   ├── windows-x64/my_pids.dll
│   ├── windows-arm64/my_pids.dll
│   ├── linux-x64/libmy_pids.so
│   ├── linux-arm64/libmy_pids.so
│   ├── macos-x64/libmy_pids.dylib
│   ├── macos-arm64/libmy_pids.dylib
│   ├── android-arm64/libmy_pids.so
│   └── ios-arm64/libmy_pids.dylib
└── ...(貼圖、模型等其餘資源)

在 joban_custom_resources.json 中宣告(PIDS 預設)

不需要 language 欄位——模組按 scriptLocations 裏每個項目的特徵自動判定語言,JS 與 C++ 可以在同一項目混用任何數量:

{
    "pids_images": [{
        "id": "example:arrivals",
        "scriptLocations": [
            "mtr:my_pack/natives/my_pids"
        ]
    }]
}

項目解析規則:

scriptLocations 項目判定結果
…/main.js(大小寫不敏感)JavaScript
mtr:my_pack/natives/my_pids(無副檔名)C++ 平台無關 stem → 各平台自動解析為 natives/linux-x64/libmy_pids.so、natives/windows-x64/my_pids.dll、natives/macos-arm64/libmy_pids.dylib 等;ARM64 Linux 環境自動追加 ios-arm64 / android-arm64 候選
…/libfoo.so / ….dll / ….dylibC++ 完整路徑(綁定該副檔名對應的平台,其他平台跳過)
{"windows": …, "linux": …, "macos": …}C++ 按平台對應表;支援 macos-arm64 等精確鍵(精確鍵優先於通用鍵)
  • 四類腳本同規則:mtr_custom_resources.json 的 vehicleScripts / objectScripts / liftScripts 與 joban_custom_resources.json 的 pids_images 預設(scriptLocations)全部支援上述混合宣告。
  • 舊格式相容:"language": "cpp" + nativeLibrary / nativeLibraries 的舊宣告繼續有效,也可與 .js 混用;"language": "javascript" 可強制整條按 JS 解析。
  • JS 欄位照常:prependExpressions / input / dataFetchMode 等欄位含義不變,C++ 側經 api.input() 讀取 input。
  • 視界外繼續渲染:PIDS 預設欄位 outOfBound: true(上游 v2.3.0-prerelease.3)令螢幕移出相機視界時仍繼續渲染——須臾不可停嘅投影機預設所必需。
注意

腳本模組(你編譯的 .dll/.so/.dylib)屬於資源包,放進 natives/<platform>/;模組 jar 自帶的 assets/jcm/natives/ 只有原生橋,兩者不要混淆。遊戲只會載入現時作業系統對應平台的程式庫。

遊戲內驗證

  1. 安裝好 JCM Yanyang 與 MTR,把資源包放進 resourcepacks/ 並啟用(或作為內置資源隨存檔一併發佈)。
  2. 放置對應方塊(如 PIDS),在設定裏選取你的腳本 / 讓宣告項目生效。
  3. 開啟 logs/latest.log,搜尋 native script——模組載入時會列印模組 census 摘要(模組數目、腳本類型、傳輸通道等)。看到你的 example:arrivals 即為載入成功。
  4. 改了程式庫檔案後,按 F3+T 重新載入資源包即可重新載入 C++ 腳本(或使用模組提供的「重新載入全部 MTR 腳本」快捷鍵),無需重新啟動遊戲。
重要

必須使用 Release 版 jar 或 Complete Build 產物。push/PR 的快速 CI 產物不含原生橋,日誌會提示 native bridge unavailable、所有 C++ 腳本被跳過——這不是你的模組出錯。


下一步:腳本模型與生命週期 —— 四類腳本的完整語義;或直接跳到資料快照查閱可讀取的資料。