Scilab を利用したシミュレーションで、ジョイスティック入力を行いたいことがあったのですが、ジョイスティックの入力値を取得してくれる関数が Scilab の標準モジュールにも Atoms にも見当たらなかったので、少し DIY する必要がありました。
この記事では、Scilab 環境でジョイスティック(またはゲームパッド)の入力値を読み取るために、筆者が行った方法を紹介します。対象 OS は Windows です。
大まかな流れは次の通りです。
- C 言語でジョイスティックの入力値を取得するプログラム(C 関数)を作成する
- プログラムをコンパイルし、ダイナミックライブラリ(DLL)を生成する
- ダイナミックライブラリ内の C 関数を Scilab とリンクする
- Scilab の call 関数でリンクした C 関数を呼び出して使用する
以下では、これらを順を追って解説します。
ここで紹介する方法は、ジョイスティック入力の読み取りに限らず、一般に C 言語でプログラムしたルーチンを Scilab で使用したい場合に利用できます。
動作確認環境
- Windows 10
- MSYS2 MinGW 64-bit
- gcc 8.2.0
- Scilab 6.1.1
- ELECOM JC-U3312SBU(ゲームパッド)
C 関数を作成する
以下のような C 言語のプログラムを作成します。関数 get_joystick()
は、Windows API の joyGetPos
関数を用いてジョイスティックの3つの軸(axis)の位置および4つのボタン(button)の押下状態を読み取ります。
// joystick.h void get_joystick(int *joyID, double axis[], int button[], int *status);
// joystick.c #include <windows.h> #include "joystick.h" enum { RANGE_AXIS_MIN = 0, RANGE_AXIS_MAX = 65535, RANGE_AXIS_CTR = 32767 }; void get_joystick(int *joyID, double axis[], int button[], int *status) { UINT joystickID; axis[0] = 0; axis[1] = 0; axis[2] = 0; button[0] = 0; button[1] = 0; button[2] = 0; button[3] = 0; switch (*joyID) { case 1: joystickID = JOYSTICKID1; break; case 2: joystickID = JOYSTICKID2; break; default: *status = 2; /* Error: Invalid joystick ID */ return; } JOYINFO joystickInfo; MMRESULT errorCode = joyGetPos(joystickID, &joystickInfo); switch (errorCode) { case JOYERR_NOERROR: axis[0] = ((double)joystickInfo.wXpos - RANGE_AXIS_CTR) / (RANGE_AXIS_MAX - RANGE_AXIS_MIN) * 2.0; /* range from -1.0 to +1.0, with 0.0 at center */ axis[1] = ((double)joystickInfo.wYpos - RANGE_AXIS_CTR) / (RANGE_AXIS_MAX - RANGE_AXIS_MIN) * 2.0; /* range from -1.0 to +1.0, with 0.0 at center */ axis[2] = ((double)joystickInfo.wZpos - RANGE_AXIS_CTR) / (RANGE_AXIS_MAX - RANGE_AXIS_MIN) * 2.0; /* range from -1.0 to +1.0, with 0.0 at center */ button[0] = (int) ((joystickInfo.wButtons & JOY_BUTTON1) ? 1 : 0); /* If pressed then 1, otherwise 0 */ button[1] = (int) ((joystickInfo.wButtons & JOY_BUTTON2) ? 1 : 0); /* If pressed then 1, otherwise 0 */ button[2] = (int) ((joystickInfo.wButtons & JOY_BUTTON3) ? 1 : 0); /* If pressed then 1, otherwise 0 */ button[3] = (int) ((joystickInfo.wButtons & JOY_BUTTON4) ? 1 : 0); /* If pressed then 1, otherwise 0 */ *status = 0; /* Success */ break; case MMSYSERR_NODRIVER: *status = 1; /* Error: No (active) joystick driver available */ break; case MMSYSERR_INVALPARAM: *status = 2; /* Error: Invalid parameters to joyGetPos */ break; case JOYERR_UNPLUGGED: *status = 3; /* Error: The joystick identified by joystickId isn't plugged in */ break; default: *status = 4; /* Error: Unknown error */ } }
ダイナミックライブラリを生成する
プログラムをコンパイルし、ダイナミックライブラリ libjoystick.dll
を生成します。
gcc -c joystick.c gcc -shared joystick.o -lwinmm -o libjoystick.dll
C 関数を Scilab とリンクする
Scilab で以下のコマンドを実行し、libjoystick.dll
内の get_joystick
関数を Scilab とリンクします。
link('libjoystick.dll', 'get_joystick', 'c');
上記コマンドは libjoystick.dll
が Scilab のカレントディレクトリにある場合の例ですが、そうでない場合、最初の引数には libjoystick.dll
ファイルの正しいパスを入力する必要があります。
上記コマンドを2回以上実行すると、同じリンクが重複してできてしまいます。スクリプトでリンクを行う場合には、次のように書けば重複を防げます。
linked_routines = link(); if ~or(linked_routines == 'get_joystick') then link_id = link('libjoystick.dll', 'get_joystick', 'c'); end
call 関数で C 関数を使用する
リンクした C 関数は、call 関数で呼び出すことができます。当該 get_joystick()
関数の場合には、次のように呼び出して使用します。
[axis, button, status] = call('get_joystick',.. joyID, 1, 'i',.. // joyID 'out',.. [1,3], 2, 'd',.. // axis[3] [1,4], 3, 'i',.. // button[4] [1,1], 4, 'i'.. // status );
ジョイスティック(またはゲームパッド)が PC に接続されていれば、上記コマンドにより、axis
に3つの軸の位置(-1.0 ~ 1.0)、button
に4つのボタンの押下状態(0 / 1)、status
に取得結果(0:成功 / 1~4:エラー)が返されます。
動作確認
以下は、動作確認用の Scilab スクリプトです。
// test_joystick.sce clear; // Link joystick function linked_routines = link(); if ~or(linked_routines == 'get_joystick') then link_id = link('../bin/libjoystick.dll', 'get_joystick', 'c'); end joyID = 1; realtimeinit(0.1); realtime(0) for i = 1:500 [axis, button, status] = call('get_joystick',.. joyID, 1, 'i',.. // joyID 'out',.. [1,3], 2, 'd',.. // axis[3] [1,4], 3, 'i',.. // button[4] [1,1], 4, 'i'.. // status ); if status ~= 0 then select status case 1 error('Joystick driver not available.'); case 2 error('Invalid parameters.'); case 3 error('Joystick not plugged in.'); case 4 error('Unknown error.'); end break; end disp([axis, button]); realtime(i); end // Unlink joystick function if isdef('link_id') then ulink(link_id); end
このスクリプトを実行した結果を以下の GIF アニメーションに示します。実行中、ジョイスティックの軸とボタンをランダムに操作しています。図中右側の GUI は Scilab を経由しないジョイスティックチェッカーで比較用に示しています。
まとめ
以上、Scilab 環境でジョイスティック(またはゲームパッド)の入力値を読み取る方法を紹介しました。ジョイスティックの入力値を読み取る関数は Scilab の標準モジュールに含まれていないので、C 関数を自作し、Scilab とリンクして call 関数で呼び出す方法を取りました。ここで紹介した方法は、ジョイスティックの入力値の取得に限らず、一般に C 言語で記述したルーチンを Scilab で使用したい場合に利用できます。
最後まで読んでいただきありがとうございました。この記事がご参考になれば幸いです。
コメント