Scilab でジョイスティック入力を行う方法 ~ ダイナミックリンクと call 関数の利用

アイキャッチ画像 未分類

Scilab を利用したシミュレーションで、ジョイスティック入力を行いたいことがあったのですが、ジョイスティックの入力値を取得してくれる関数が Scilab の標準モジュールにも Atoms にも見当たらなかったので、少し DIY する必要がありました。

この記事では、Scilab 環境でジョイスティック(またはゲームパッド)の入力値を読み取るために、筆者が行った方法を紹介します。対象 OS は Windows です。

大まかな流れは次の通りです。

  1. C 言語でジョイスティックの入力値を取得するプログラム(C 関数)を作成する
  2. プログラムをコンパイルし、ダイナミックライブラリ(DLL)を生成する
  3. ダイナミックライブラリ内の C 関数を Scilab とリンクする
  4. 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 で使用したい場合に利用できます。

最後まで読んでいただきありがとうございました。この記事がご参考になれば幸いです。

技術コンサルティング、技術サービスを提供しています!
スカイ技術研究所は機械システムの解析/制御に関する技術コンサルティング、技術サービスを提供しています。仕事のご依頼などございましたら、お気軽にお問い合わせください。
未分類
skyenginlabをフォローする
スカイ技術研究所ブログ

コメント