はじめに
2025年7月、Stormworks のアップデートでコンポーネント Mod が導入されました。それまでの Mod と何が変わり、新たに何ができるようになったのか、Mod の仕組みから紐解いていきます (なお、Stormworks をリバースエンジニアリングしたわけではなく挙動から仕組みを推測しているに過ぎませんので、情報が不正確な場合があります)。本記事は Mod 作成に興味があるがよく知らないという方をメインターゲットにしていますが、Mod 作成はせず利用だけするという方にも読んでいただきたい内容になっています。
なお、コンポーネント Mod の作り方そのものには本記事ではそこまで深く切り込みません。コンポーネント Mod 導入直後に私が書いたチュートリアルもありますので、あわせてご覧ください。
また、本記事ではコンポーネント以外のマップ Mod 等に関してはほとんど触れませんので、ご了承ください。
パーツ定義
前提知識として、パーツ定義ファイルの仕組みをおさらいします。Mod 作者の方はすでにご存知の内容、Mod を作る予定がない方にはあまり関係のない内容となりますので、次章 まで読み飛ばしていただいても構いません。
コンポーネント、つまりワークベンチで設置するビークルパーツは XML ファイルによって定義されています。バニラのパーツ定義ファイルは Stormworks 本体 (stormworks.exe) と同じ階層から rom/data/definitions と辿ったところにあります。stormworks.exe のフォルダは Steam から「ローカルファイルを閲覧」を押すと開きます。

例えば、rom/data/definitions/01_block.xml は通常の立方体パーツです。あまり関係のない内容を削って要約すると次のようになっています。2行目の name 属性はゲーム内での表示名、type 属性はパーツの種類 (整数値) 、mass は質量、value は価格といったように、パーツの情報が記述されています。
<?xml version="1.0" encoding="UTF-8"?> <definition name="Block" category="0" type="0" mass="1" value="2" flags="56" tags="basic"> <surfaces> <surface orientation="0" shape="1"> <position x="0" y="0" z="0"/> </surface> ... </surfaces> <buoyancy_surfaces> <surface orientation="0" shape="1"> <position x="0" y="0" z="0"/> </surface> ... </buoyancy_surfaces> <logic_nodes/> <voxels> <voxel flags="1"> <position x="0" y="0" z="0"/> </voxel> </voxels> <voxel_min x="0" y="0" z="0"/> <voxel_max x="0" y="0" z="0"/> <voxel_physics_min x="0" y="0" z="0"/> <voxel_physics_max x="0" y="0" z="0"/> <tooltip_properties description="The block is a quarter-metre in size. Components can be attached to all 6 faces." short_description="Basic cube-shaped building block." /> <reward_properties tier="0" number_rewarded="2000"/> </definition>
<surfaces> は設置判定や表示に使われる面、<bupyancy_surfaces> は水密判定・浮力判定のための面です。ロジックノードを持つようなパーツは <logic_nodes> にロジックノードの名前、説明文、種類、位置などが記述されます。その他にも様々な属性・要素があります。
01_block.xml にはありませんが、3Dメッシュを持つようなパーツは .mesh とつくファイルへのパスがあります。.mesh ファイルは通常 rom/meshes 以下にあり、Stormworks 独自フォーマットのバイナリ3Dメッシュファイルです。Stormworks 独自フォーマットなのでこのファイルを直接開けるソフトウェアはありませんが、拙作の sw_block_definition_tools で間接的に見ることができます。(直接開く機能も用意したほうがいいでしょうか……)
また、音声の鳴るパーツには .ogg とつくファイルへのパスがあります。.ogg ファイルは通常 rom/audio 以下にあります。これは一般的な音声ファイル形式です。
パーツ定義 XML によって定義される内容は以上のようなものです。ここに記述されていない内容、例えばパーツの動作などは、<definition> の type 属性の値に基づいて stormworks.exe の中にあるコードを呼び出すような形になっていると思われます。従来型の Mod では XML、mesh、ogg の編集で実現できない、例えばまったく新しい動作や複数の既存パーツの組み合わせのような動作は実現できないことになります。要するに見た目や音声を変更できるだけですが、それでも創意工夫次第で様々なことができます*1。ところが、2025年7月の Lua コンポーネントアップデートの追加機能でできることが広がりました。
時系列
従来型の Mod についても少々ややこしいので、一度時系列で Mod の種類を分類しておきます。なお、分類名は本記事に限ったものです。
- ~2024年11月: 非公式 Mod 期
- 2024年11月: v1.13.0 - The Asset Modding Major Update
- 2024年11月~2025年7月: アセット Mod 期
- 2025年7月: v1.15.0 - The Components, Physics, Gameplay Modding Update
- 2025年7月~: コンポーネント Mod 期

非公式 Mod 期
公式に Mod がサポートされる以前から、非公式で Mod は存在していました。この時期には私は Mod にあまり関わっていませんでしたので詳しいことは言えませんが、主な内容としては次のようなことができていました。
この頃の Mod は rom フォルダの一部を直接書き換え・追加するというものでした。rom フォルダを書き換えても Steam で整合性チェックを行うとバニラに戻すことができましたが、まれに完全には戻らず副作用が残ることもありました。
この頃には Mod 利用時には非公式ツールの StormLoader などがよく使われ、Mod を作成したければやはり非公式ツールの StormworksData などでデータ形式を変換していました。
このようなタイプの Mod は以下 rom 書き換え型 Mod と呼ぶことにします。
アセット Mod 期
2024年11月のアップデートの主要な変更として、それまで非公式 Mod として行われていた内容が公式になり、rom フォルダに触らずに Mod を使うことができるようになりました。ビークルやアドオンと同じように %appdata%/Stormworks/data/mods に Mod を置くと、ワールド生成時の設定で選択でき、Steam Workshop で共有もできるようになりました。さらに、Mod を導入したマルチプレイに参加するときにはホストから Mod を配られるようになりました。Mod を持っているか否かに関わらずダウンロードするため、同一 Mod のバージョン違いなども起こらず強制的に同期されます。
まとめると、Mod 利用者の視点では次のような変化がありました。
- ゲーム内のワールド生成時の設定で Mod をオンオフできるように
- マルチプレイ時の Mod 同期機能
- Steam Workshop での Mod 共有
非公式ツールを使用せずともよくなり、副作用もなくなり、オンオフも簡単になったことで Mod 利用のハードルが大きく下がったと言えます。一方で、Mod で追加されたパーツを使用したビークルは Steam Workshop にアップロードできない仕様となりました。これは Geometa によると、サブスクライブしたビークルが必要とする Mod をワールド作成時に正確に把握しなければならなくなるなど、プレイヤーの管理負担が増大するからという判断だそうです*2。この問題は後にコンポーネント Mod という形で解決が図られることになりました*3。
Mod 作成者から見たアセット Mod
このアップデートにより、次のような点で Mod 作成に参入しやすくなりました。
- Terms of use 更新、Mod 関連が明記
- 公式アセット Mod ドキュメントが公開
- 公式 SDK が Stormworks 本体に同梱
Mod 作成に興味がある方は、是非公式ドキュメントを一度読んでみてください。
コンポーネント Mod 期
2025年7月のアップデートでついにコンポーネント Mod が追加されました。Mod の中でもコンポーネント、つまりビークルパーツに焦点を当てたものです。ビークルデータと Mod パーツのデータを抱き合わせにすることで、その Mod が有効になっていない環境でビークルを読み込んだときにも問題なく読み込めるようになりました。同様の考え方で、先述したワークショップビークルの Mod パーツ問題を解消し*4、さらにマルチプレイにおいてはホストが持っていない Mod パーツを使用したビークルを参加者が出すことができるようになるなど、Mod パーツの扱いやすさに重点を置いた仕様となっています。*5
さらに、Lua コンポーネントが追加されました。これまでのパーツ Mod は既存パーツの見た目や属性を変更することしかできませんでしたが、このアップデートから Lua を使って既存パーツにない動作をするパーツを作れるようになりました。(ただし、Component Lua API に用意されていないものは実装できません。)
また、コンポーネント Mod と直接関係はありませんが、Game Constants Mod も可能となりました。あまりにもユーザーがねちょねちょエアー*6とかに文句を言うので、空力をはじめとしたゲーム内定数を変更する Mod を作れる道を用意することで決着にしたいようです。
まとめると、Mod 利用者の視点では次のような変化がありました。
- コンポーネント Mod を使用したビークルは、その Mod が有効でない環境でも使えるように
- Mod パーツを使用したビークルの取り回しが便利に
- これまで不可能だった新機能を持つ Mod パーツが可能に
- ゲーム内定数の一部を変更することが技術的に可能に
Mod 作成者から見たコンポーネント Mod
コンポーネント Mod の実態は .bin という拡張子のバイナリファイルです。コンポーネントを構成する要素は、定義 XML、メッシュ、音声です (Lua コンポーネントの場合には Lua スクリプトも)。このうちメッシュと音声は1つのパーツに複数含まれることもあり、1つのコンポーネントは多数のファイルで構成されうることになります。そこで、1つのコンポーネントの構成ファイルをすべてまとめて1つのファイルにしてしまおうということになっています。このアップデートで .bin ファイルにコンパイルするための component_mod_compiler がSDKに追加されました。
また、<definition> の type 属性を 66 にし、lua_script 属性に Lua ファイルへのパスを指定することで Lua コンポーネントを作成できるようになりました。Component Lua API を使用し、コードを記述して任意の動作をするパーツを作成できるようになっています。
追加されたのは同時でしたが、コンポーネント Mod と Lua コンポーネントは別の概念として捉えたほうがよいでしょう。コンポーネント Mod は .bin 形式の Mod のこと、Lua コンポーネントは type="66" の Mod のことで、必ずしもこれらはセットというわけでもありません。
詳しくは公式コンポーネント Mod ドキュメントを読んでみてください。
Mod の仕組み
時系列を整理したところで、本題である Mod の仕組みに入ります。
アセット Mod の仕組み
アセット Mod の仕組みはシンプルなもので、%appdata%/Stormworks/data/mods またはワークショップサブスクリプション内のフォルダのうち、中に mod.xml を含んでいるものを Mod とみなして Mod 一覧に表示し、有効にした環境内では rom フォルダが仮想的に書き換わったような動作をします。
コンポーネント Mod の仕組み
先述したように、コンポーネント Mod は .bin ファイルのこと、.bin ファイルは XML と .mesh と .ogg と .lua をまとめたもののことです。より具体的には各ファイルのバイナリを単に結合して多少メタデータを含めたもののようです。component_mod_compiler に XML ファイルと付属する各ファイルを渡してコンパイルした .bin ファイルを rom/data/components に置くと、ゲーム内ワークベンチで設置できるようになります。従来型 rom 書き換え Mod では rom/data/definitions に .xml を置いていたのが、rom/data/components に .bin 置くようになったものです。
.bin ファイルの構造
.bin ファイルを解析し、コンポーネント Mod の逆コンパイラ、つまり .bin ファイルから元のファイルを復元するプログラムを作成することができたので、それで判明したことをまとめます。ファイルの構造は先頭から、次のようになっています。なお、整数はリトルエンディアンで、文字列は末尾ヌル文字の可変長となっていました。
- ファイルサイズ (ファイル全体のバイト数から4(自身の長さ)を引いた数): 4バイト整数
- 定義XMLのテキスト: 可変長文字列
- 付属ファイルの数 (定義XML以外): 2バイト整数
- 付属ファイル①のファイル名: 可変長文字列
- 付属ファイル①のサイズ (1バイト=1): 4バイト整数
- 付属ファイル①のバイナリ: 上で指定された長さのバイト列
- 付属ファイル②のファイル名: 可変長文字列
- 付属ファイル②のサイズ (1バイト=1): 4バイト整数
- 付属ファイル②のバイナリ: 上で指定された長さのバイト列
- (付属ファイルの数だけ繰り返し)
- 末尾 詳細不明 (大抵 0 だが、たまに0でない場合がある): 1バイト
アセット Mod とコンポーネント Mod の併用
上記のように rom/data/components に置いていると、rom 書き換え型 Mod と同じでアセット Mod アップデートの恩恵 (オンオフしたり、Steam Workshop にアップロードしたり) が受けられません。そのため多くの場合はアセット Mod の形を作って .bin を組み込むことになるでしょう。アセット Mod のフォルダ (mod.xml の階層) の data/components に .bin を置くと、アセット Mod にコンポーネント Mod を追加できます。このようにアセット Mod にコンポーネント Mod を含める形にすると、Steam Workshop で共有することもできます。
コンポーネント Mod を含むアセット Mod はオンオフできますが、オフのときは設置できないだけでコンポーネント Mod を使用したビークルのロードは可能です。これはどのように実現しているのでしょうか。
ビークルロードの仕組み
通常のビークルを保存すると %appdata%/Stormworks/data/vehicles に .xml ファイルが保存されます(同時にサムネイルの .png も保存されますが、今回は関係ないので割愛します)。このビークル XML の中を見ると、<c> というタグが各パーツ (コンポーネント) のことを表していて、d 属性が definitions 内のパーツ定義 XML のファイル名に対応しています (d 属性が省略されている場合は通常ブロックの 01_block とみなされていると思います)。
コンポーネント Mod のパーツを設置しでビークルを保存してみると、d 属性には「7f996f0d84e451a61694cc63fc174c52」のようなランダム文字列 (のように見えるもの) が書き込まれています。同時に、vehicles に .xml と同名のフォルダが作成されます。その中には「7f996f0d84e451a61694cc63fc174c52.bin」のような、d 属性と同じ名前の .bin ファイルがあります。このファイルは設置したコンポーネント Mod の .bin ファイルをそのまま、名前だけ変更したものです。つまり、コンポーネント Mod を使用するとビークル保存と同時にコンポーネント Mod もセットで保存されて、ビークルロード時にはセットになっているコンポーネント Mod も読み込むようになっています。
コンポーネント Mod の問題点
ビークル XML ファイルをコピー等するときに同名のフォルダがあればそれもセットにして扱うということを徹底できれば、環境を気にすることなくコンポーネント Mod を含むビークルを扱うことができます。しかし、残念なことに Steam Workshop にビークルを投稿するときにはそのフォルダがアップロード対象に含まれないというバグがあります。Mod を使用したビークルを簡単に共有できるという、コンポーネント Mod の主要な利点を潰してしまっており、しかもそのバグが半年間も放置されているのは残念な限りです。(アドオンにビークルを含める場合にも同様のバグがありましたが、まだ確認できていないものの、この記事を出す2日前のアップデートで修正されたようです。Steam Workshop 投稿のバグも近いうちに修正されることに期待しています。)
コンポーネント Mod は、マルチプレイでホストが Mod を有効にしていなくても参加者が Mod を使用したビークルを出すことができてしまうため、競技性のあるマルチプレイでは対策が問題視されることもありました。しかし、比較的最近のアップデート*7でホストがコンポーネント Mod を許可するかどうかの設定項目が追加されたため、この問題はほぼ解消したと言えるでしょう。
ランダムっぽい文字列の正体
さて、ビークルのコンポーネント Mod フォルダを削除するとどうなるでしょうか。ロードができなくなるか、パーツが欠損しそうに思えますが、実際にはロード時の環境によって結果が変わり、同 Mod が有効になっていればロード可能、そうでなければ欠損という結果になります。ということは、d 属性のランダム文字列っぽいものを手がかりに対応するコンポーネント Mod を導き出していることになります。

同じコンポーネント Mod は異なるビークルに設置して保存したときにも同じ文字列になります。これはたとえ元の .bin を component_mod_compiler で再コンパイルしても変わりません。このことから、ランダムではなさそうと推測できます。ただし、コンポーネント Mod を構成するファイルのどれか1つでも、ほんの1文字でも変更した場合は別の文字列になります。
こうした挙動から、件の文字列はビークル保存時にランダム生成したわけでもコンパイル時にランダム生成したわけでもないということがわかり、おそらく .bin ファイルのハッシュ値 *8 のようなものと推測できます。*9
コンポーネント Mod のまとめ
コンポーネント Mod は、1個のパーツを構成する複数のファイルを1個のファイルに固めた .bin のことで、おそらくハッシュ値を識別用の ID にしています。たとえ Mod が有効になっていない環境でも、ビークル XML とセットで保存されていれば読み込みが可能です。また、もしそれがなくても、同じ Mod が有効になっている環境なら読み込むことができます。ビークルと Mod をセットにして扱うことでマルチプレイや Steam Workshop 等でも Mod を使用したビークルを簡単に扱えるとされていますが、ビークルとしての Steam Workshop へのアップロードは現時点ではできません。バグとみられますが半年間放置されています。とはいえ、コンポーネント Mod を使用したビークルは、Steam Workshop へのアップロード自体ができないわけではなく、.bin が含まれずにアップロードされるだけなので、Mod は アセット Mod として別でアップロードして正しく Mod を有効にすれば Mod を使用したビークルの Steam Workshop での共有自体は可能です。
Lua コンポーネントの仕組み
コンポーネント Mod と同時に追加されましたが、Lua コンポーネントは独立した別の機能と考えたほうがよいでしょう。パーツ定義 XML で <definition type="66" lua_script="{Luaファイル名}"> と指定すると、既存パーツにない挙動をするパーツを追加することができます。Stormworks にはビークルのマイコンで使う Lua と、アドオンで使う Lua が存在しますが、新たにコンポーネントで使う Lua が登場した形です。
コンポーネント Lua API
ビークル Lua とアドオン Lua で API が異なるように、コンポーネント Lua も別の API が提供されています。APIは公式コンポーネント Mod ドキュメントで確認できますが、本記事でもざっくり紹介します。なお、私が触ったことのないAPIはドキュメントの文章を読んだだけの理解で書いていますので、情報が不正確な場合があります。
コンポーネント Lua では次のコールバック関数を用意しておくとそれぞれのタイミングで呼ばれます。
function onAddToSimulation() -- ビークルスポーン時・ロード時などの処理 end function onRemoveFromSimulation() -- ビークル消去時・アンロード時などの処理 end function onParse() -- ビークルアンロード・ロードをまたいで保持する値の処理(?) end function onTick(tick_time) -- 毎 tick 実行される処理 -- tick_time は通常 1 だが、ベッドで寝て早送りするときは 400 end function onRender() -- メッシュレンダリングなどの処理 end
ロジックノード
コンポーネント Lua API の重要な機能の1つとして、ロジックノードの入出力を制御することが可能です。例えば、定義 XML を次のように書いて On/Off の入力ノードを持った Lua コンポーネントがあるとします。
<defintion {略}> {略} <logic_nodes> <logic_node label="Input" mode="1" type="0" description="Example logic input"> <position x="0" y="0" z="0"/> </logic_node> </logic_nodes> {略} </definition>
onTick の中で次のように書くとロジックノードへの入力値を取得できます。
value, success = component.getInputLogicSlotBool(0)
定義 XML の <logic_node> で mode="1" と書けば入力ノード、mode="0" (または省略) で出力ノードになります。また、type 属性の値は次の表の通りです。
| type | 種類 |
|---|---|
| 0 (または省略) | On/off |
| 1 | 数値 |
| 2 | 動力 (orientation 属性でポートの面を指定) |
| 3 | 流体 (orientation 属性でポートの面を指定) |
| 4 | 電気 |
| 5 | コンポジット |
| 6 | 映像 |
| 7 | 音声 |
| 8 | ロープ等 |
On/Off、数値、コンポジットノードについては、component.(getInput|setOutput)LogicSlot(Bool|Number|Float) で読み取り、書き込みができます。
ロジックノードのインデックス
component.getInputLogicSlotBool(0) の 0 の部分は、Input かつ Bool のノード、つまり <logic_node type="0" mode="1" /> のうち最初のものを示すインデックスです。type もしくは mode が異なれば別のカウントになりますし、0始まりです。公式ドキュメントでは1始まりと読めてしまう表現になっていますが、おそらく誤りかと思います。
動力
<logic_node type="2"/> の動力ポートの操作は次のAPIがあります。
component.slotTorqueIsConnected: 動力ポートに他のパーツが繋がっているか取得します。component.slotTorqueApplyMomentum: 動力ポートを回したり止めたり、RPSを取得したりします。
動力ポートが複数あれば、それらを繋げたり切り離したりするすることができるようです。
component.slotTorqueCreateBridge: 2個の動力ポートを繋げます。component.slotTorqueDestroyBridge: 繋げた動力ポートを切り離します。component.slotTorqueSetBridgeFactor: 伝達率を設定します。component.slotTorqueSetBridgeRatio: 動力ポートのギア比を設定します。
流体
<logic_node type="3"/> の流体ポートの操作は次のAPIがあります。4つまで内部タンクを持つことができます。
component.slotFluidResolveFlow: 流体ポートと内部タンク間で流体を流します。ポンプやバルブのように加圧したり制限したり一方通行にしたり、流体の種類をフィルターにかけることもできるようです。component.slotFluidResolveFlowToSlot: 2つの流体ポート間で上と同様に流体を流します。component.fluidContentsTransferVolume: 2つの内部タンク間で指定した量を移動させます。component.fluidContentsTransferVolumeExceptType: 流体を1種類除いて上と同様に流体を移動させます。component.fluidContentsSetFluidTypeVolume: 内部タンクの指定した種類の流体の量を指定した量にセットします。component.fluidContentsGetFluidTypeVolume: 内部タンクの指定した種類の流体の量を取得します。component.fluidContentsGetVolume: 内部タンクの全種類の流体の量の合計を取得します。component.fluidContentsSetCapacity: 内部タンクの容量を設定します。component.fluidContentsGetCapacity: 内部タンクの容量を取得します。component.fluidContentsGetPressure: 内部タンクの圧力を取得します。
電力
<logic_node type="4"> の電気ノードの操作は次のAPIがあります。
component.slotElectricIsConnected: 電気ノードに他のパーツが繋がっているか取得します。component.slotElectricAddCharge: 電気ノードに電力を与えます。component.slotElectricRemoveCharge: 電気ノードから電力を奪います。component.slotElectricGetChargeFactor: 電気ノードの電圧を取得します。
ヒーター
ヒーターのように周囲を温めるAPIがあります。
component.heaterSetSphere: 球形の範囲にヒーター効果を適用します。component.heaterSetOrientedBox: 直方体の範囲にヒーター効果を適用します。component.heaterSetTemperature: ヒーターの温度を設定します。component.heaterSetFactor: ヒーターの効率係数(?)を設定します。
音声
音声を再生するAPIがあります。component_mod_compiler で指定した .ogg ファイルを Lua から呼び出して再生できます。同時再生枠は4つまでですが、.ogg ファイル自体の数には制限はありません(.bin 自体の2MB制限がありますが)。再生枠 (channel_index) は0~3で指定、.ogg ファイル (effect_index) はコンパイル時の .ogg の記述順 (0始まり) で指定します。ゲーム全体での再生枠も別にあるようで、それを溢れた場合優先度の低いものが無効化されるようです。
component.sfxPlayOnce: 音声を1回再生します。位置オフセット、可聴範囲、音量、ピッチ、優先度を指定できます。component.sfxPlayLoop: 音声をループで上と同様に再生します。component.sfxUpdate: すでに再生中の音声の位置オフセット、可聴範囲、音量、ピッチを変更します。component.sfxStop: すでに再生中の音声を止めます。
メッシュレンダリング
定義XMLで、<definition> の mesh_0_name、mesh_1_name、mesh_2_name で指定したメッシュを動的に表示させるAPIがあります。function onRender() の中で呼ぶ必要があります。mesh_data_name は Lua スクリプトに関係なく常時表示されます。動かせるメッシュは3種類が上限となります。
component.renderMesh0:mesh_0_nameを表示します。4x4行列を1次元で長さ16のテーブルとして指定して、位置・回転・スケール・剪断変形などを自由に操作できます。component.renderMesh1:mesh_1_nameを上と同様に表示します。component.renderMesh2:mesh_2_nameを上と同様に表示します。
ライト・レーザー
通常のライトやレーザーを出すAPIがあります。しかし、Indicator のような照明を伴わない発光や、スポットライトのような照明は出せません。function onRender() の中で呼ぶ必要があります。
component.renderLight: RGBライトと同様のライトを位置、色、大きさ等を指定して出します。赤外線にすることも可能です。component.renderLaser: 位置と距離を指定してレーザーを出します。赤外線にすることも可能です。
物理演算
物理演算に干渉したり、情報を取得したりするAPIがあります。ただし、座標オフセットの中心がコンポーネントの中心ではなくマージ (Body) の重心になっているとみられます。また、なんと位置や回転を取得する方法がありません(Physics Sensor と同等の情報すらも得られません)。
component.physicsImpulse: 位置と力のベクトルを指定して、パーツに力 (Force) を加えます。しばらく力が加わっていない物体は負荷軽減のために物理演算が休止しますが、これを再開させるかどうかも指定します。component.physicsRaycast: レイキャストで当たり判定を検査できます。好きな位置から好きな方向に出せる不可視なレーザー距離計のようなものです。component.physicsGetLinearVelocity: 速度ベクトルを取得します。component.physicsGetLinearVelocityAtPoint: 指定位置での速度ベクトルを取得します。component.physicsGetAngularVelocity: 角速度を取得します。
情報取得
周囲の情報を取得するAPIがあります。
component.getSubmergenceFactor: 位置と半径を指定して、球が水面にどれだけ浸かっているかを取得できます。component.getWindVelocity: 位置を指定して風速をベクトルで取得します。
発射物
砲弾などを発射するAPIがあります。
component.spawnProjectile: 種類、位置、速度等を指定して砲弾を出現させます。
その他
matrix.multiply など、簡易的な行列演算ライブラリが備わっています。また、onParse では parser.parseBool、parser.parseNumber、parser.parseString などでビークルのアンロード、ロード時にデータを保存できる(?)ようですが、公式ドキュメントを読んでもよくわからず、触ってみてもよくわかりませんでした。
Lua コンポーネントのまとめ
コンポーネントの動作を Lua で記述できるようになり、Mod でできることの幅が大きく広がりました。一方で、Mod を作ってみるとすぐに仕様の限界に阻まれることになります。Indicator やスポットライトのようなものが作れないこと、マイコンでは作れるカスタムプロパティが Mod パーツで作れないことなど枚挙に暇がありません。試しに既存パーツの機能を Lua で模倣しようとしても、それができるのはごく一部のパーツのみでしょう。自信満々な割に中途半端な状態でリリースしたという印象を受けました。とはいえこのアップデートのおかげでできるようになったことも多くありますので、中途半端だとしてもプレイヤーにさらなる自由を与えてくれたことは評価しています。これ以降の半年間はパーツ追加アップデートが中心で、コンポーネント Lua に関するアップデートはほとんどありませんが、今後 API が拡充されることを期待しています。
おわりに
コンポーネント Mod と Lua コンポーネントについて、これまで調べてきたことを長々と述べてきました。本当は開発中の テツダン Mod 2 のために開発した Python 製の支援ツールについて書くつもりだったのですが、それ以前にコンポーネント Mod とは何なのかという記事を書くべきと思い、この内容に変更しました。結果として想定していたよりもずっと長い記事になってしまいましたが、読んでくださった方に新たな知見があれば嬉しく思います。
コンポーネント Mod の開発は、定義 XML や Lua などのテキストファイルの編集、Blender でのモデリングと .dae 形式でのエクスポート、mesh_compiler を使用した .dae 形式から .mesh への変換、Audacity や ISSE 等を使用した音声ファイルの編集、component_mod_compiler を使用した .bin 形式へのコンパイル等、実に様々な作業があります。Mod を少しずつ更新して確認していく開発スタイルをとるには再コンパイルの手間があまりに多く、手動でやっていられませんでした。そこで、コマンド一発ですべての作業を完了させて、あとは Stormworks をリロードするだけという環境を整えることにしたのです。生の XML で書いているパーツもあれば細かいバリエーションを持つパーツ郡もあるので、パーツ郡ごとにバリエーションのXMLを自動生成する Python スクリプトを用意して、Python 製の支援ツールからさらにその Python を呼び出して XML を生成するようにしました。生の XML と生成された XML のどちらに対しても、各パーツが依存するメッシュ・音声・Lua を抽出し、メッシュを生成するために Blender をヘッドレスで起動して .blend ファイルから .dae を自動エクスポート、mesh_compiler を呼び出して .mesh に変換、component_mod_compiler を呼び出して .bin に変換、そして開発用フォルダと Stormworks 用フォルダを同期という手順をすべてコマンド一発でできるようになり、コンポーネント Mod の開発がかなり快適になりました。しかし、最近の Blender のアップデートで .dae のエクスポートができなくなってしまったので、今のところ Blender のバージョンを下げて対応ということにしています。.mesh ファイルの構造は判明しているので、いずれ Blender から直接 .mesh をエクスポートするスクリプトを用意したいという野望もありますが、今のところ着手時期は未定です。
*1:例えば、拙作の鉄道車両向けの Mod では Dial の針のモデルを変更して揺れるつり革を作ったり、モジュラーエンジンのスターター音を変更して VVVF サウンドを作ったりしていました
*2:https://store.steampowered.com/news/app/573090/view/4482864232593883181
*3:https://store.steampowered.com/news/app/573090/view/534354183830111760
*4:解消されていない。半年放置されているバグ。
*5:ホストが持っていない Mod パーツをマルチプレイで使えるようになる点はゲームバランスの観点から懸念する意見もありましたが、後のアップデートでほぼ解決となりました。
*6:やたらと大きい空気抵抗
*7:https://store.steampowered.com/news/app/573090/view/565891434097410262
*8:異なるデータを入力したら異なる値が、同じデータを入力したら同じ値が毎回出力されて、出力値から入力値の逆算がほぼ不可能など、いくつかの条件を満たすハッシュ関数を通した値
*9:ハッシュ値とみられる文字列をよく見ると g 以降のアルファベットがないことから16進数で、長さが32であることから128ビット、ということでおそらく MD5 なのではないかと推測できます。ただ、.bin のバイナリをそのまま MD5 に突っ込んでも一致しなかったためソルトみたいなものが掛かっているか、そもそも MD5 でないかもしれません。













