BveEXプラグイン開発 クイックスタート
お急ぎの方向けに、BveEXプラグインの開発に必要な情報を最小限に絞って説明しています。
現時点ではBveEXプラグインの文法のうち最も根幹となる部分のみの解説になります。より込み入った部分の仕様についてはSDKを参考にしてください。
INDEX
1. BveEXの仕組み
2. プラグインアセンブリ(DLL)の開発①:基本文法
3. プラグインアセンブリ(DLL)の開発②:各種機能の概要と呼び出し方
4. 開発したプラグインを実行させる方法
執筆中……1. BveEXの仕組み
1-1. BveEXの動作原理
「BveEXは、BVE上であらゆる演出を実現可能にする」と言いますが、その原理を見ていきます。
基本原理はリフレクション
BVE本体に定義されているクラスや関数、変数を外部から直接参照することは可能ではあるのですが、
実際にやろうとすると、BVE本体(BveTs.exe)の構造上の理由で困難を極めます(想定されていない使い方なのですから当然です)。
そこで、BveEXはBVE本体のほぼ全てのクラスやそのメンバーをリフレクションによって参照し、BveTypes.dllを介して使いやすい形で公開しています。
これをブリッジなどと呼んでいます。
BVE本体の機能を、BveEXを経由してデータ作者の皆様に参照していただく。このようにしてBveEXは成り立っているわけです。
え、それだけ?と思いませんか
実際、BveEXはそこまで特殊なことをやっているわけではありません。
ではなぜBveEXが必要なのか。それは、とにかくBVE本体に実装されている機能が多すぎるからです。
原理は至って(ソフトウェア開発に精通している人にとっては)単純なのですが、データ作者の皆様が各々で実装するにはあまりにも複雑でかつ個数が多すぎるのです。 それをBveEXは全て肩代わりしてくれるわけですから、強力なツールと言うことができるでしょう。
強力なプラグイン機能
BVE本体のブリッジをデータ作者の皆様から呼び出すための手段として提供しているのが、プラグイン機能です。詳細は1-2にて説明しています。
同等の機能を自前で実装すれば、BveEXを使うメリットはない?
いいえ、そんなことはありません。詳しい理由はQ&Aにて説明しています。
1-2. BveEXプラグインの種類とその特徴
BveEXプラグインは、その実行形態によって
- 車両プラグイン
- マッププラグイン
- 拡張機能
車両プラグイン
車両プラグインは、車両データに同梱するプラグインです。
形としては通常のBVEにおけるATSプラグインに近いと言えます。
マッププラグイン
マッププラグインは、路線データに同梱するプラグインです。
通常のBVEでは提供されていない、新しい形のプラグインになります。
拡張機能
拡張機能は、他の2種類のプラグインとは異なり読み込まれている車両、路線に関わらず常に読み込まれる仕様のプラグインです。
形としては入力デバイスプラグインに近いと言えます。
どの種類のBveEXプラグインでも、BveEXの一部機能が制限されるようなことはありません。
車両プラグインでも、マッププラグインでも、拡張機能でも、変わらずBveEXの機能をフル活用することができます。
2. プラグインアセンブリ(DLL)の開発①:基本文法
2-1. 車両・マッププラグイン:メインクラスの定義例
以下に示すのが、最小構成のBveEX車両・マッププラグインのコードです。
BveEx.PluginHost
をインストールしておく必要があります。
車両プラグイン
マッププラグイン
2-2. 車両・マッププラグイン:クラス、属性などの解説
PluginBase抽象クラス
PluginBase
抽象クラスを継承したクラスがBveEXプラグインのメインクラスとして認識される仕様ですが、
基本的には PluginBase
の派生である AssemblyPluginBase
抽象クラスを継承することになります。
PluginBase抽象クラス、AssemblyPluginBase抽象クラスの違い
BveEXプラグインとして認識されるのはどちらの抽象クラスを継承した場合も同様なのですが、
AssemblyPluginBase
では、BveEXプラグインの名前や説明、著作権表示等が AssemblyInfo.cs
で定義したアセンブリ情報を参照して自動で設定されます。
Plugin属性(PluginAttributeクラス)
BveEXプラグインのメインクラスは、PluginBase
抽象クラスを継承することに加えて、Plugin
属性を付加することによってプラグインの種類を指定する必要があります。
2-3. 拡張機能:メインクラスの定義例
以下に示すのが、最小構成のBveEX拡張機能のコードです。
車両・マッププラグインでも必要だった要素に加えて、拡張機能の場合のみ必要となる要素が存在します。
2-4. 拡張機能:クラス、属性などの解説
PluginBase抽象クラス、Plugin属性
車両・マッププラグインでの解説を参照してください。
IExtensionインターフェイス
BveEX拡張機能のメインクラスは、車両・マッププラグインの場合と同様に PluginBase
抽象クラスを継承し、Plugin
属性を付加することに加えて、
IExtension
インターフェイスを実装する必要があります。
IExtension
インターフェイスにはメンバーは何も定義されていません(ver2.0時点)。
あくまでもBveEX本体の処理上の都合になります。
ITogglableExtensionインターフェイス(任意)
IExtension
インターフェイスの派生である ITogglableExtension
インターフェイスを実装し、Togglable
属性を付加した拡張機能は、
「BveEX バージョン情報・プラグイン一覧」画面(右クリックメニューから表示できます)で有効・無効を切り替えることができます。
ExtensionMainDisplayType属性(任意)
拡張機能のメインクラスのインスタンスは他のプラグインから取得することができるのですが (詳しくは後述)、 その際に見える型を実際とは異なるクラス・インターフェイスへ置き換えることのできる機能です。
BveEX拡張機能のメインクラスに ExtensionMainDisplayType
属性を付加し、そのパラメーターとして
IExtension
インターフェイスを実装しており、かつ- メインクラスの継承(実装含む)元である
例えば以下のコードでは、本来のメインクラスが
PluginMain
クラスであるところを IHogeExtension
インターフェイスに置き換えています。
HideExtensionMain属性(任意)
BveEX拡張機能のメインクラスに HideExtensionMain
属性を付加すると、
この拡張機能のメインクラスのインスタンスについて、他のプラグインからの取得が禁止されます。
3. プラグインアセンブリ(DLL)の開発②:各種機能の概要と呼び出し方
3-1. BveHacker
1-1で説明した「BVE本体の機能を使いやすい形で公開する」機能を、
PluginBase
クラスの BveHacker
プロパティから参照することができます。
BveHacker
の中に入っている機能を組み合わせることで、BveEXプラグインを作成していくことになります。
BveHacker
の機能は、大きく以下の2種類に分けることができます。
- クラスラッパー
- リフレクション情報
クラスラッパー
BVE本体が管理するクラスのインスタンスを、BveEXを介して分かりやすい形で取得することができます。
ここで、元のインスタンスを「分かりやすい形」に加工するのがクラスラッパーです。
その機能は ClassWrapperBase
抽象クラスから派生する諸クラスに定義されています。
ClassWrapperBase
抽象クラスの概形を以下に示します。
BVE本体が管理する元のインスタンス(オリジナルオブジェクトと呼びます)を、Src
プロパティに格納します。
これを基に、Scenario
クラス、Vehicle
クラス、Map
クラス、Station
クラスなど、
様々なラッパーが派生クラスとして定義されています。
一例として ScenarioInfo
クラスの概形を示します。説明していない属性などが所々見られますが、先述した Src
プロパティを使って、
Path
プロパティからオリジナルオブジェクトの対応する値を参照できるようになっていることが分かるかと思います。
さて、このようにして実装されたクラスラッパーを BveHacker
プロパティから取得することができます。
BveHacker
プロパティから直接取得できるクラスラッパー(ver2.0.5現在)のうち、よく使うものを示しました。
プロパティ名 | クラス名 | 説明 |
---|---|---|
MainForm |
MainForm |
BVEのメインフォーム |
LoadingProgressForm |
LoadingProgressForm |
BVEの「シナリオを読み込んでいます...」フォーム |
DirectSound |
DirectSound |
BVE本体が音声再生に使用するDirectSoundデバイス※ |
Assistants |
AssistantSet |
補助表示のセット |
InputManager |
InputManager |
キー入力を管理するオブジェクト |
Preferences |
Preferences |
BVE本体の設定※ |
MapLoader |
MapLoader |
マップ読込ロジック |
ScenarioInfo |
ScenarioInfo |
シナリオファイルの情報 |
Scenario |
Scenario |
実行中のシナリオ全体を管理するオブジェクト |
これらのオブジェクトの中にも更に沢山のクラスラッパーが格納されています。
特に、実行中のシナリオ全体を管理する Scenario
クラスはとても大きく、何度も参照することになるでしょう。
例えば自列車の現在の速度は、以下のように記述すると取得・変更できます。
リフレクション情報
オリジナルオブジェクトのプロパティの値を取得・変更したり、メソッドを呼び出したりする他に、
ObjectiveHarmonyPatch
を使ってBveEXプラグインからBVE本体に定義されているメソッドを上書きすることもできます。
BveHacker.BveTypes
プロパティから上書きするメソッドのリフレクション情報(MethodInfo
など)を取得して実装します。
難易度が高く使用頻度も低い機能なので詳細は割愛しますが、
RichLoadでの実装などが参考になるでしょう。
3-2. 外部の拡張機能を参照する
理論上は、BveHackerから取得できるオブジェクトを組み合わせることで、BveEXが提供するBVE本体への介入機能を全て使うことができます。
しかしながら、新しくBveEXプラグインを開発する度にその全てを自前で実装するのは手間がかかるものです。
そこで、BveEX拡張機能として実装された外部のプラグインを、ライブラリとして参照することができるようになっています。
例えば、2-4の ExtensionMainDisplayType
属性の例として提示した拡張機能を参照する場合は、以下のように記述します。