-
Notifications
You must be signed in to change notification settings - Fork 2
クイックスタート
本項では継続的な開発は考えず、ひとまずローカルで本テンプレートを用いて簡単にプラグインの開発を始める手順について述べる
初めから継続的な開発を視野に入れている人はHomeから「テンプレートの使い方」に沿って順に進めていくことをお勧めする
ここでは、簡単なAtsEXの車両プラグインを作成する
作成したプラグインはAtsEXのサンプルシナリオ付属する車両に読み込ませ、動作を確認する
ここでは、まずプラグイン開発に必須となるソフトウエアのインストールを行い、次に本テンプレートを用いて開発が始められるようになることを目指す
初めに、以下の全てをダウンロードしてインストールを行う
- Microsoft Visual Studio
- BVE Trainsim
- AtsEX
そのあと、テンプレートをダウンロードしてコーディングに備える
公式ページから無料の「Visual Studio Community」をダウンロードする
ダウンロードしたファイルを実行し、しばらくすると下のような画面が表示されるので、必要なものを選択してインストールする
よく分からない場合は画像と同じもの(.NET デスクトップ開発)を選択しておけば問題ない
その後もいくつか設定項目が表示されるので、好みに応じて選択する
こちらもよく分からない場合は基本的にデフォルトで問題ない
下の画像のような画面になれば Visual Studio の導入は終了である
公式ページから5.8.7554.391以上の任意のバージョンをダウンロードする
インストール方法については公式の案内を参照されたい
このとき、下の二点についても同様にダウンロードとインストールを行っておく
公式ページから最新版をダウンロードする
こちらについてもインストール方法については公式の案内を参照されたい
緑色の < > Code ボタンから Download ZIP を選択し、テンプレートのソースコードをダウンロードする
ダウンロードフォルダに AtsExCsTemplate-main.zip が生成されてることを確認し、その中身をドキュメント以下など任意のわかりやすい場所に解凍する
解凍してできた AtsExCsTemplate-main フォルダ内の AtsExCsTemplate.sln を開く
インストールが問題なくできている場合は規定で Visual Studio が開かれるはずである
このステップで行ったことは下のとおりである
- ソフトウエアのインストール
- Visual Studio
- Microsoft .NET Framework 4.8
- DirectX End-User Runtime
- BVE Trainsim
- AtsEX
- Visual Studio
- ソースコード
- ダウンロード
- 解凍
これらの全てが問題がなくできていれば、次の2項目が確認できるはずである
- BVE Trainsim を用いた AtsEX サンプルシナリオの実行
- ソースコードを Visual Studio で開いて見る
これらの2点が確認出来たらこのステップは完了である
ここでは構築した環境で実際にコードを書いていく
簡単な目標として、AtsEXのサンプルシナリオの中の「普通 一橋→六合【運転台パネルに値を渡すサンプル】」で読み込ませるプラグインを作成する
最終的な目標は、次の4点の実現である
- 知らせ灯
- 編成全てのドアが閉まっているときのみ点灯する
- 速度計
- 列車の速度の絶対値を示す
- 電流計
- 電流の値を示す
- ノッチカット
- 一定の速度を超過した際にノッチを切って加速できないようにする
知らせ灯はおおまかに次の2つの機能の集合といえる
- ドアの開閉情報の収集
- panelの出力制御
つまり、ドアの開閉状況に応じてpanelの出力を変化させることができればよいわけである
ドアの開閉状況はNative.DoorOpened
イベントとNative.DoorClosed
イベントを購読することで確認できる
下のような関数を用意し、Native.DoorOpened += DoorOpened;
とすることでイベントを購読することができる
イベントの購読中はイベントが起こるごとに登録した関数が呼ばれるため、その関数内で変数を変更することでドアの開閉状況をクラス内から変数を介して確認できる
// ドアの開閉状況を表す変数
bool door
/// <summary>
/// ドアが開いた時に呼ばれる関数
/// </summary>
/// <param name="e"></param>
private void DoorOpened(AtsEx.PluginHost.Native.DoorEventArgs e)
{
// ドアが開いたら消す
door = false;
}
/// <summary>
/// ドアが閉じた時に呼ばれる関数
/// </summary>
/// <param name="e"></param>
private void DoorClosed(AtsEx.PluginHost.Native.DoorEventArgs e)
{
// ドアが閉じたら点ける
door = true;
}
panelの出力制御は次の手順で行うことができる
- パネルの制御を仲介する変数を制御するパネルの数だけ用意する
IAtsPanelValue<int> panelValue;
- パネルを使うための手続きを行う
panelValue = Native.AtsPanelValues.RegisterInt32(number);
- 変数に対して値の操作を行う
panelValue.Value = value;
ここで、ドアの状態は真偽値として保持しているため、bool
型を用いる
ここまでで、先に示した関数に加えて次の通りの実装を行っているはずである
IAtsPanelValue<bool> doorPanel;
doorPanel = Native.AtsPanelValues.RegisterBoolean(0);
doorPanel.Value = true;
doorPanel.Value = false;
速度計はおおまかに次の3つの機能の集合といえる
- 速度情報の収集
- 速度情報の処理
- panelの出力制御
つまり、速度に応じてpanelの出力を変化させることができればよいわけである
速度はNative.VehicleState.Speed
で取得できるが、この型はfloat
であることに注意しておきたい
パネルの最終的な出力はint
であるため、出力時にはint
にキャストしておくほうが良い
速度の表示は数値が正か負かにかかわらず、常に正の値を出力する
なお、これはats1の表示領域が負の場合はパネルの表示領域外になるためである
任意の数値の絶対値を得るには、Math.Abs()
を用いる
panelの出力制御は知らせ灯と同じく次の手順で実装すればよい
ただし、速度は真偽値ではないためint
を用いる点に留意する
- パネルの制御を仲介する変数を制御するパネルの数だけ用意する
IAtsPanelValue<int> panelValue;
- パネルを使うための手続きを行う
panelValue = Native.AtsPanelValues.RegisterInt32(number);
- 変数に対して値の操作を行う
panelValue.Value = value;
ここまでで、次の通りの実装を行っているはずである
IAtsPanelValue<int> speedPanel;
speedPanel = Native.AtsPanelValues.RegisterInt32(1);
speedPanel.Value = Math.Abs((int)Native.VehicleState.Speed);
電流計はおおまかに次の2つの機能の集合といえる
- 電流情報の収集
- panelの出力制御
つまり、電流に応じてpanelの出力を変化させることができればよいわけである
電流はNative.VehicleState.Current
で取得できるが、この型はfloat
であることに注意しておきたい
パネルの最終的な出力はint
であるため、出力時にはint
にキャストしておくほうが良い
panelの出力制御は速度計と同じ手順で実装すればよい
ただし、電流も真偽値ではないためint
を用いる点に留意する
ここまでで、次の通りの実装を行っているはずである
- パネルの制御を仲介する変数
IAtsPanelValue<int> currentPanel;
- パネルを使うための手続き
currentPanel = Native.AtsPanelValues.RegisterInt32(2);
- 電流を
int
にキャストしてパネルに出力currentPanel.Value = (int)Native.VehicleState.Current;
ノッチカットはおおまかに次の2つの機能の集合といえる
- 速度情報の収集
- handleの出力制御
つまり、速度に応じてhandleの出力を変化させることができればよいわけである
速度は速度計の節で扱った通り、Native.VehicleState.Speed
で取得できる
速度による条件分けは、if
ステートメントを用いて列車の速度と定数を比較し実装する
handleの出力制御は次の手順で行うことができる
- ハンドルの制御を仲介する変数を用意する
VehiclePluginTickResult ret = new VehiclePluginTickResult();
- ハンドルの出力値を書き込む
- ハンドルの操作を一切しない場合
ret.HandleCommandSet = HandleCommandSet.DoNothing;
- ハンドルの操作をする場合
- ハンドル情報を設定
- ノッチを0に設定
NotchCommandBase power = Native.Handles.Power.GetCommandToSetNotchTo(0);
- ブレーキは変更しない
NotchCommandBase brake = Native.Handles.Brake.GetCommandToSetNotchTo(Native.Handles.Brake.Notch);
- ノッチを0に設定
- 変数に対してハンドル情報を登録する
ret.HandleCommandSet = new HandleCommandSet(power, brake, ReverserPositionCommandBase.Continue, ConstantSpeedCommand.Disable);
- ハンドル情報を設定
- ハンドルの操作を一切しない場合
- ハンドルの制御を仲介する変数をAtsEXに渡す(Bveに送る)
return ret;
ここまでで、次の通りの実装を行っているはずである
VehiclePluginTickResult ret = new VehiclePluginTickResult();
-
if (Native.VehicleState.Speed > "任意の速度") { ... }
ret.HandleCommandSet = HandleCommandSet.DoNothing;
-
ret.HandleCommandSet = new HandleCommandSet(power, brake, ReverserPositionCommandBase.Continue, ConstantSpeedCommand.Disable);
NotchCommandBase power = Native.Handles.Power.GetCommandToSetNotchTo(0);
NotchCommandBase brake = Native.Handles.Brake.GetCommandToSetNotchTo(Native.Handles.Brake.Notch);
return ret;
このステップでは次の4種の機能を実装した
これらそれぞれの機能について、bveからは次の通りに動作を確認できる
- 知らせ灯
- Panel制御
- ats0
- 駅停車時にドアの開閉に応じて点灯・滅灯するか
- Panel制御
- 速度計
- Panel制御
- ats1
- 列車の速度の絶対値に応じて時計回りに振れるか
- Panel制御
- 電流計
- Panel制御
- ats2
- 加速すると時計回りに、減速すると反時計回りに振れるか
- Panel制御
- ノッチカット
- ハンドル制御
- 一定の速度を超過した際にノッチが切れるか
最終的なコードは次のとおりである
必ずしもこのとおりである必要はないため、各々リファレンスを確認しつつ試行錯誤してほしい
コード全体
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AtsEx.PluginHost.Handles;
using AtsEx.PluginHost.Panels.Native;
using AtsEx.PluginHost.Plugins;
namespace AtsExCsTemplate.VehiclePlugin
{
/// <summary>
/// プラグインの本体
/// </summary>
[PluginType(PluginType.VehiclePlugin)]
internal class VehiclePluginMain : AssemblyPluginBase
{
// パネル(ats0)の情報を格納する変数
private readonly IAtsPanelValue<bool> doorPanel;
// パネル(ats1)の情報を格納する変数
private readonly IAtsPanelValue<int> speedPanel;
// パネル(ats2)の情報を格納する変数
private readonly IAtsPanelValue<int> currentPanel;
/// <summary>
/// プラグインが読み込まれた時に呼ばれる
/// 初期化を実装する
/// </summary>
/// <param name="builder"></param>
public VehiclePluginMain(PluginBuilder builder) : base(builder)
{
// パネルを使う手続き
doorPanel = Native.AtsPanelValues.RegisterBoolean(0);
speedPanel = Native.AtsPanelValues.RegisterInt32(1);
currentPanel = Native.AtsPanelValues.RegisterInt32(2);
// ドアが開いた時に呼ばれる関数を登録
Native.DoorOpened += DoorOpened;
// ドアが閉じた時に呼ばれる関数を登録
Native.DoorClosed += DoorClosed;
}
/// <summary>
/// プラグインが解放されたときに呼ばれる
/// 後処理を実装する
/// </summary>
public override void Dispose()
{
Native.DoorOpened -= DoorOpened;
Native.DoorClosed -= DoorClosed;
doorPanel.Dispose();
speedPanel.Dispose();
currentPanel.Dispose();
}
/// <summary>
/// シナリオ読み込み中に毎フレーム呼び出される
/// </summary>
/// <param name="elapsed">前回フレームからの経過時間</param>
public override TickResult Tick(TimeSpan elapsed)
{
// 列車の速度
int speed = (int)Native.VehicleState.Speed;
// panelに速度を出力
speedPanel.Value = Math.Abs(speed);
// panelに電流を出力
currentPanel.Value = (int)Native.VehicleState.Current;
// ハンドルの制御を仲介する変数
VehiclePluginTickResult ret = new VehiclePluginTickResult();
// デフォルトではハンドルは操作しない
ret.HandleCommandSet = HandleCommandSet.DoNothing;
// 速度が80[km/h]以上だったら
if (speed > 80)
{
// ノッチを0に設定
NotchCommandBase power = Native.Handles.Power.GetCommandToSetNotchTo(0);
// ブレーキは変更しない
NotchCommandBase brake = Native.Handles.Brake.GetCommandToSetNotchTo(Native.Handles.Brake.Notch);
// ハンドル情報を登録
ret.HandleCommandSet = new HandleCommandSet(power, brake, ReverserPositionCommandBase.Continue, ConstantSpeedCommand.Disable);
}
// ハンドルの制御を仲介する変数をAtsEXに渡す(Bveに送る)
return ret;
}
/// <summary>
/// ドアが開いた時に呼ばれる関数
/// </summary>
/// <param name="e"></param>
private void DoorOpened(AtsEx.PluginHost.Native.DoorEventArgs e)
{
// ドアが開いたら消す
doorPanel.Value = false;
}
/// <summary>
/// ドアが閉じた時に呼ばれる関数
/// </summary>
/// <param name="e"></param>
private void DoorClosed(AtsEx.PluginHost.Native.DoorEventArgs e)
{
// ドアが閉じたら点ける
doorPanel.Value = true;
}
}
}
前項ではソースコードを改変して新しいプラグインを実装した
ここではそのソースコードからdllを生成し、bveに読み込ませるための準備を行う
ビルドは Ctrl + B または メニューバー > ビルド で行うことができる
試しに、設定はそのままで一度ビルドを行ってみる
ビルドは Ctrl + B または メニューバー > ビルド で行うことができる
画面下部の出力に次のように表示されればビルドは成功である
========== ビルド: 成功 1、失敗 0、最新の状態 0、スキップ 0 ==========
ここで失敗してしまった場合は、エラーに応じて次の項目を参考にしてもらいたい
問題なくビルドができた場合には、このステップは終了したのでこのステップのまとめに進んで問題ない
ビルドを行う前に、テンプレートが参照するパッケージを導入する必要がある
パッケージの導入といっても、基本的にはパッケージマネージャーが依存関係を読んで自動で解決するので心配する必要はない
パッケージの導入などに問題がある場合には、自動で解決されないパッケージを確認する必要がある
問題のある参照パッケージの確認は次の手順で行う
- ソリューションエクスプローラーから プロジェクト > 参照 を展開する
- 一覧表示される参照ペッケージのうち、三角形表示のあるものは問題がある
下の画像はすべてのパッケージに問題がなく正常にビルドできる状態である
一部の環境などでは自動でパッケージが入らない場合もあるが、その場合には手動でのパッケージ導入が必要となる
手動でのパッケージ導入手順を次に示す
- メニューバーから ツール > NuGet パッケージマネージャー > ソリューションのNuGet パッケージの管理 からパッケージの管理画面を開く
- 参照画面からパッケージを検索し、すべてのプロジェクトにインストールする
このステップではソースコードからdllを生成した
この手続きによりソースコードは他のプログラムから呼び出せるアセンブリの形になったといえる
エラーが出てビルドできない場合はエラーコードを読み、ソースコードを修正して再度ビルドを行って欲しい
前項ではソースコードからdllを生成した
ここではそのdllをbveに読み込ませ、実際に動作していることを確認する
これ以降、特記なき場合各ファイルのパスはScenarios以下のものである
なお、使用するシナリオとマップ・車両アドオンは次のとおりである
- 普通 一橋→六合【運転台パネルに値を渡すサンプル】
_AtsExSamples.Basic.Panel.txt
- ★AtsEX サンプル
AtsEx.Samples\Vehicles\Basic\Vehicle_WithPanel.txt
- AtsEX 搭載サンプル車両
AtsEx.Samples\Vehicles\Basic\Vehicle_WithPanel.txt
- ★AtsEX サンプル
dllをbveに読み込ませるためには、車両アドオンの中にプラグインを配置する必要がある
ここで、ビルドにより生成されたdllを車両アドオンの中に移動する
ビルドにより生成されたdllは AtsExCsTemplate-main フォルダ以下、ソースコードのある VehiclePlugin フォルダの中のbinフォルダの中にある
ビルド時の設定により出力先は変わるが、デフォルトの場合はプロジェクトフォルダ内のbin以下を探せばよい
プラグイン本体が見つかったら、コピーまたは切り取りをしておく
プラグインはScenarios/AtsEx.Samples/Vehicles/Basic/Ats_WithPanel/AtsEXPlugins/
に張り付ける
dllの配置は終わったがこのままではbveはプラグインを読み込んでくれないため、プラグインを読み込ませるための設定を行う
下に示すファイル群を編集することで、デフォルトのAtsPanelValueTest.dll
の代わりに今回作成したVehiclePlugin.dll
を読み込ませる
Scenarios/AtsEx.Samples/Vehicles/Basic/Ats_WithPanel/AtsEx.Caller.x86.VehiclePluginUsing.xml
Scenarios/AtsEx.Samples/Vehicles/Basic/Ats_WithPanel/AtsEx.Caller.x64.VehiclePluginUsing.xml
<?xml version="1.0" encoding="utf-8" ?>
<AtsExPluginUsing xmlns="http://automatic9045.github.io/ns/xmlschemas/AtsExPluginUsingXmlSchema.xsd">
<Assembly Path="AtsEXPlugins\SimpleAts.dll" />
<Assembly Path="AtsEXPlugins\StateViewer.dll" />
<Assembly Path="AtsEXPlugins\VehiclePlugin.dll" />
</AtsExPluginUsing>
ここまでのすべてが終わったら実際にbveにdllを読み込んでもらい、動作の確認を行う
動作の確認を行うには、bveを実行して「普通 一橋→六合【運転台パネルに値を渡すサンプル】」を開く
シナリオが読み込まれたら、まず右クリックメニューから VehiclePlugin.dll が読み込まれていることを確認する
問題なくプラグインが読み込まれていることが確認出来たら、次に実装した各機能の動作を確認する
各機能とその確認方法を次の通り再掲する
これらそれぞれの機能について、bveからは次の通りに動作を確認できる
- 知らせ灯
- Panel制御
- ats0
- 駅停車時にドアの開閉に応じて点灯・滅灯するか
- 速度計
- Panel制御
- ats1
- 列車の速度の絶対値に応じて時計回りに振れるか
- 電流計
- Panel制御
- ats2
- 加速すると時計回りに、減速すると反時計回りに振れるか
- ノッチカット
- ハンドル制御
- 一定の速度を超過した際にノッチが切れるか
試運転を行い、これらの機能について問題がないことを確認できればこのステップは完了である
このステップではdllをbveに読み込ませるための作業を行い、プラグインが正常に読み込まれ動作することを確認した
bveでの読み込み時にエラーが発生する場合や、プラグインは正常に読み込まれるが動作がおかしい場合には、ソースコードや環境に問題があると考えられる
そういった問題は後述のデバッグを行うことで解決できることが多い
このステップでの作業後のディレクトリ構成を次に示す
ディレクトリ構成
星印はファイルの変更や更新を意味する
Scenarios
├ _AtsExSamples.Basic.Panel.txt
└ AtsEx.Samples
├ Maps
│ └ ...
├ Thumbnails
│ └ ...
└ Vehicles
├ E217r
│ └ ...
└ Basic
├ Ats_CooperatingWithMapPlugin
│ └ ...
├ Ats_Empty
│ └ ...
├ Ats_Simple
│ └ ...
├ Ats_WithPanel
│ ├ AtsEXPlugins
│ │ ├ AtsPanelValueTest.dll
│ │ ├ SimpleAts.dll
│ │ ├ StateViewer.dll
☆ │ │ └ VehiclePlugin.dll <= 追加
│ ├ AtsEx.Caller.txt
│ ├ AtsEx.Caller.x64.dll
│ ├ AtsEx.Caller.x64.VehicleConfig.xml
☆ │ ├ AtsEx.Caller.x64.VehiclePluginUsing.xml <= 編集
│ ├ AtsEx.Caller.x86.dll
│ ├ AtsEx.Caller.x86.VehicleConfig.xml
☆ │ └ AtsEx.Caller.x86.VehiclePluginUsing.xml <= 編集
├ DummyAts
│ └ ...
├ Notch
│ └ ...
├ Panel
│ └ ...
├ _Readme.txt
├ Ats.wav
├ Null.wav
├ Parameters.txt
├ Sound.txt
├ Vehicle_AsInputDevice.txt
├ Vehicle_AsInputDevice.VehiclePluginUsing.xml
├ Vehicle_CooperatingWithMapPlugin.txt
├ Vehicle_Empty.txt
├ Vehicle_Simple.txt
└ Vehicle_WithPanel.txt
前項ではdllをbveに読み込ませ実際に動作していることを確認した
bveは正常にdllを読み込んでいるが、意図した動作をしない場合はコードにバグが潜んでいると考えられる
こういった状況はbveを通してデバッグを行うことで解決できることが多い
本項では生成したdllを随時更新できる構成とし、読み込ませるdllを更新しながらデバッグを行う
- メニューバー > プロジェクト > VehiclePluginのプロパティ を選択しプロジェクトのプロパティ画面を開く
- サイドバー > ビルド を選択しビルドの設定画面を開く
- 出力セクションの出力パスをプラグインの出力先に設定する
Scenarios/AtsEx.Samples/Vehicles/Basic/Ats_WithPanel/
- 試しにビルドしてみて出力されるか確認する
- メニューバー > デバッグ > VehiclePluginのデバッグプロパティ を選択しプロジェクトのデバッグプロパティ画面を開く
- 開始動作を"外部プログラムの開始"を選択しBveのパスを設定する
- デフォルト:
C:\Program Files (x86)\mackoy\BveTs5\BveTs.exe
- デフォルト:
C:\Program Files\mackoy\BveTs6\BveTs.exe
- デフォルト:
- コマンドライン引数にシナリオファイルのパスを設定する
Scenarios/_AtsExSamples.Basic.Panel.txt
デバッグでは実行中のプログラム内での変数などの変化を確認することができる
試しにTick
関数の最初の部分にブレークポイントを張ってデバッグを行うと、前回フレームからの変数の変化を観察することができる
例えば、bveでの現在時間を表すthis.Native.VehicleState.Time
の変化を観察すると、1フレーム進むごとに前回フレームからの経過時間であるelapsed
と等しい数値が足されていることがわかる
このように、ブレークを張り変数の内容を確認することで、意図した通りの動作であるかどうかを確認できる
このステップでは読み込ませるdllを更新しながらデバッグを行った
デバッグでは内部の変数の値や関数の呼び出しを直接確認できる
これはバグ取りに必須といえるほど重要な機能であるため有効に活用するとよい
本章では簡単なAtsEXの車両プラグインを作成した
なお、本章で作成したプラグインはAtsEX独自の機能は一切使っておらず、従来のプラグインとしても実装可能である
しかしながら、このプラグインでも次のようにAtsEXの機能を利用することは可能である
- ハンドル制御
- 車両プラグイン側で操作するのではなく入力デバイスからの操作とする
- ドアの開閉情報
- ドアの開閉中は点滅させる 本章の内容だけでは物足りないと感じた各位においては、ぜひともこれらをはじめとするAtsEXの独自機能を使用した実装を行いAtsEXの恩恵を享受してほしい
AtsEXでは車両プラグイン以外にもマッププラグインと拡張機能が実装できる
これらの3種は目的に応じて使い分けるものであるが、基本の車両プラグインが実装できればそれ以外はドキュメントを読めば簡単に実装できるだろう
本章での体験が一人でも多くの方のAtsEXへの導入ハードルの低減につながれば幸いである