2016年6月9日木曜日

SimConnectで操作

SimConnectの基本的な使い方は例のチュートリアルを見てもらうとして。

とりあえずギアハンドルを例に説明。

MSDNによるとギアハンドルはBoolだが、SIMCONNECT_DATATYPEにはBoolっぽいのは見当たらないのでINT32を使用する。たぶん0がfalseで!0(実質的に1)がtrueだとおもう。C++なら01で分岐できるので問題なかったんだろう。C#だと01をboolとして判定できないので面倒だが。

まずはint32を含む構造体を作る。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct sInt32
{
    public Int32 value;
}

次にFSXにこの構造体をギアハンドルに関連付ける。
simconnect.AddToDataDefinition(DATADEFINEID.GEAR_HANDLE_POSITION, "gear handle position", null,
    SIMCONNECT_DATATYPE.INT32, 0, SimConnect.SIMCONNECT_UNUSED);
simconnect.RegisterDataDefineStruct<sInt32>(DATADEFINEID.GEAR_HANDLE_POSITION);
simconnect.RequestDataOnSimObject(REQUESTS.GEAR_HANDLE_POSITION, DATADEFINEID.GEAR_HANDLE_POSITION,
    SimConnect.SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD.SECOND, SIMCONNECT_DATA_REQUEST_FLAG.DEFAULT, 0, 0, 0);

NameはGEAR HANDLE POSITIONを、Unitsにはnullを指定する。nullでも特に問題なく取れてるので、FSX側でうまく処理してくれるんだと思う。これは他のfeetとかを指定する部分でもnullで動作する。単位を明示してないのでちょっと不安になるけど。
変数の型(DATATYPE)にはINT32を指定する。
PERIODにはSECONDを指定した。これで1秒毎にギアハンドルの位置を取得することができる。ただMax1secの遅れが発生するので、リアルタイムに取りたい場合はFRAMEにする。
DATADEFINEIDとかREQUESTSは、こっち側でどういうデータかを判断するのに使うだけなので、オレオレで勝手に列挙型を作ってしまって問題ないと思う。DATADEFINEIDとREQUESTSは一応分けてるけど、たぶん同じ列挙型を与えて問題ないと思う。

FSXからは
private 
void Simconnect_OnRecvSimobjectData(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA data)
にイベントが来るので、
(REQUESTS)data.dwRequestID
のようにキャストしてswitchとかに渡す。caseでGEAR_HANDLE_POSITIONだった場合は
sInt32 s = (sInt32)data.dwData[0];
のように構造体にキャストする。あとはsのvalueを読めば、ハンドルの位置を獲得できる。0の場合は格納、1の場合は展開になるらしい。


次にC#から操作する場合。とても簡単で、
sInt32 s = new sInt32();
s.value = 1;
simconnect.SetDataOnSimObject(DATADEFINEID.GEAR_HANDLE_POSITION, SimConnect.SIMCONNECT_OBJECT_ID_USER, 
    SIMCONNECT_DATA_SET_FLAG.DEFAULT, s);
のように渡すだけでギアハンドルを展開に移すことができる。s.valueを0にして渡せば収納できる。

注意点として、例のチュートリアルPart4ではギアのイベントを得るが、上記の方法でギアの位置を動かした場合はイベントが発生しない。同じプログラムを2個起動し、片方で操作した場合も他方にはイベントが来ないので、ギアの位置を常に得たい場合はイベントではなくRequestで常に受け取っておいたほうが良いと思う。


それと、C# Formのボタンを操作するとFormがアクティブになり、FSXの音声出力などが止まってしまう。それを回避するにはFormをアクティブにしないようにする。そのためにはFormの適当な場所に以下の処理を追加する。
private const int WS_EX_NOACTIVATE = 0x8000000;
protected override CreateParams CreateParams
{
    get
    {
        CreateParams p = base.CreateParams;
        if (!DesignMode) { p.ExStyle |= (WS_EX_NOACTIVATE); }
        return p;
    }
}
この処理と、常に手前に表示するような属性を合わせれば、FSXの手前に常に表示したりできる。


ギアの上げ下げは今回はFormのButtonイベントに実装したが、必ずしもFormを使う必要はなく、他のタイミングで発生させても問題ないはず。例えばCOMポートから特定の文字列を受信したらギアを展開する という処理をつければ、マイコンのスイッチを押したらギアを展開することができる。
同じようにスロットルハンドルをマイコンでPCに渡せば細かい推力調整ができるし、FSXからスロットル位置を受け取ってモーターでスティックに反映させればオートスロットルにあわせてスティックを動かすこともできる。

0 件のコメント:

コメントを投稿