Chapter 3. Input handling

Table of Contents
Handling Joysticks
Handling the Keyboard

Handling Joysticks

Initialization

SDL プロブラムにおいて、ジョイスティックを使う 最初のステップは、SDL のジョイスティックサブシステムを初期化することです。 これは、SDL_InitSDL_INIT_JOYSTICK フラグを渡すことで行われます。 ジョイスィックは普通何かを制御するために使われるので、 ジョイスィックフラグは通常、他のフラグ(ビデオフラグのように)と一緒に使われます。

Example 3-1. ジョイスティックサポートを伴う SDL の初期化


    if (SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0)
    {
        fprintf(stderr, "SDL が初期化できませんでした: %s\n", SDL_GetError());
        exit(1);
    }

これは、ビデオとジョイスティックサブシステムを同時に有効にして SDL を開始しようとしています。

Querying

ここまでたどり着いたなら、 SDL ライブラリは初期化されてジョイスティックサブシステムも有効であると 安心して仮定することができます。 ジョイスティックが必要になる前に物事を進めるため、 いくつかのビデオ/サウンド関数を呼び出します。 万一の場合に、使っているジョイスティックが 実際にあることを確認する必要があります。 ジョイスティックが抜かれたことを検出する助けにもなるため、 ジョイティックがあると知っていても 常にチェックすることは賢明です。 ジョイスティックがあるかどうかをチェックするために使われる関数は、 SDL_NumJoysticks です。

この関数は単にシステム上で利用可能なジョイスティックの数を返します。 それが少なくとも 1 つならば、よい状態にあると言えます。 次のステップは、ユーザーはどのジョイスティックを使いたいのかを 決定することです。 利用可能なジョイスティックの数が 1 つだけならば、 そのジョイスティックがユーザーが使いたいものだと安全に仮定することができます SDL はオペレーティングシステムによって割り当てられたジョイスティックの名前を 取得する関数があり、それは SDL_JoystickName です。 ジョイスティックは 0 が最初のジョイスティック、最後のジョイスティックは SDL_NumJoysticks - 1 によって返される数 となるインデックスによって特定されます。 デモンスレーションの中では、すべての利用可能なジョイスティックのリストが 標準出力に表示されます。

Example 3-2. 利用可能なジョイスティック数の問い合わせ


    printf("%i 個のジョイスティックが見つかりました。\n\n", SDL_NumJoysticks() );
    printf("ジョイスティックの名前は:\n");
		
    for( i=0; i < SDL_NumJoysticks(); i++ ) 
    {
        printf("    %s\n", SDL_JoystickName(i));
    }

Opening a Joystick and Receiving Joystick Events

SDL のイベントドリブンのアーキテクチャによって、 ジョイスティックの使用が楽になります。 ジョイスティックは 4 種類のイベントを発生することができます。

SDL_JoyAxisEvent 軸が変化したときに発生
SDL_JoyBallEvent ジョイスティックのトラックボールの位置が変化したときに発生
SDL_JoyHatEvent ハットスイッチの位置が変換したときに発生
SDL_JoyButtonEvent ボタンが押された、または離された時に発生

イベントはオープンされた全てのジョイスティックから受け取ります。 ジョイスティックイベントを受け取るためにまずしなければいけないことは、 SDL_ENABLE フラグをつけて SDL_JoystickEventState を呼ぶことです。 次に、イベントを受け取りたいジョイスティックをオープンする必要があります。 これは SDL_JoystickOpen 関数によって行われます。 例えば、どんな状況にも関わらずシステム上の最初の ジョイスティックからのイベントにしか興味がないことがあります。 イベントを受け取るためには、このようにします。

Example 3-3. ジョイスティックのオープン

    SDL_Joystick *joystick;

    SDL_JoystickEventState(SDL_ENABLE);
    joystick = SDL_JoystickOpen(0);

他のジョイスティックのイベントを受け取りたいなら、 返ってくる SDL_Joystick 構造体を 異なるポインタに格納することを除き、 ちょうどジョイスティック 0 をオープンしたように、 SDL_JoystickOpen を呼んでオープンします。 ジョイスティック構造体へのポインタは、ジョイスティックを問い合わせる時か、 ジョイスティックをクローズする時にだけ必要となります。

ここまでに我々が持っている全てのコードは、 実行時に値を読み込むためにジョイスティックを初期化することだけに 使われています。 そして今我々が知るべきことは 全ての SDL プログラムがシステムの終了イベントを何とかして 受け取る必要のある、イベントループです。 上で触れたイベントのうち、少なくともいくつかのための イベントループをチェックするコードを追加しなければなりません。 我々のイベントループがこのようであると仮定しましょう。


    SDL_Event event;
    /* 他の初期化がここに来る */

    /* メインのゲームループがここから開始 */

    while(SDL_PollEvent(&event))
    {  
        switch(event.type)
        {  
            case SDL_KEYDOWN:
            /* ここでキーボード関係の処理 */
            break;

            case SDL_QUIT:
            /* 必要なフラグをセット */
            /* メインのゲームループはここで終了 */
            break;
        }
    }

    /* ループはここで終了 */
ジョイスティックイベントを処理するためには、単に case 文を追加するだけです。 まず、軸の処理コードを追加します。 受け取ったジョイスティックイベントの多くはごみなので、 軸のチェックは一種のトリックになります。 これを埋め合わせるため、変化に対する閾値を設定し、 閾値を越えなかったイベントを無視します。 通常 10% が良い閾値です。 これは複雑なように聞こえますが、実際はそうではありません。 軸イベント処理コードをここに載せます。

Example 3-4. ジョイスティックの軸イベント


    case SDL_JOYAXISMOTION:  /* ジョイスティックの動きを処理 */
    if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) ) 
    {
      /* ここにコードが来る */
    }
    break;

軸イベントに関するもう一つのトリックは、上下と左右の動きが 2 つの異なる軸が合わさったものであるということです。 最も重要な軸は軸 0 (左右) と軸 1 (上下) です。 コード内でそれらを別々に処理するため、次のようにします。

Example 3-5. さらなるジョイスティック軸イベント


    case SDL_JOYAXISMOTION:  /* ジョイスティックの動きを処理 */
    if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) ) 
    {
        if( event.jaxis.axis == 0) 
        {
            /* ここに左右の動きのコードが来る */
        }

        if( event.jaxis.axis == 1) 
        {
            /* ここに上下の動きのコードが来る */
        }
    }
    break;

理想的には、このコードはスケーリングするために event.jaxis.value を使うべきです。 例えば、宇宙船の動きを制御するためにジョイスティックを使っていると仮定しましょう。 ユーザーがアナログジョイスティックを使っていて、 スティックを少し倒すなら、 大きく倒す場合よりも小さく動くことを期待しています。 このような状況のためにコードを設計することが好ましいです。 なぜなら、ユーザーのアナログ制御の体験を改善し、 デジタル制御の体験はそのままにするからです。

あなたのジョイスティックに追加の軸があるなら、 他のスティックやスロットル制御に使うことができるかも知れません。 それらの軸もまた、 event.jaxis.axis の値とは 異なる値を返します。

ボタンの処理は軸のチェックに比べ単純です。

Example 3-6. ジョイスティックのボタンイベント


    case SDL_JOYBUTTONDOWN:  /* ジョイスティックボタンの押下を処理 */
    if ( event.jbutton.button == 0 ) 
    {
        /* ここにコードが来る */
    }
    break;

ボタンは押される・押されないのいずれかしかないため、 ボタンノチェックは軸のチェックより単純です。 SDL_JOYBUTTONDOWN イベントは ボタンが押された時に発生し、 SDL_JOYBUTTONUP イベントは ボタンが離された時に発生します。 どのボタンが押されたのかを知る必要があり、 それは event.jbutton.button フィールドを読むことで できます。

最後に、ジョイスティックに使用を終えたときは、 SDL_JoystickClose を呼んでクローズする必要があります。 オープンされたジョイスティック 0 をクローズするためには、 プログラムの最後でこのようにします。

    SDL_JoystickClose(joystick);

Advanced Joystick Functions

それらは、 この世のすべてのジョイスティックを使うにあたって あなたが頼ることができる制御の面倒を見ます。 しかし、SDL がサポートできるさらにもう幾つかのことがあります。 ジョイボールは我々のリスト上では次にあり、 いくつかの小さな違いはあるものの軸に大変良く似ています。 軸イベントで絶対位置を格納するのとは違い、 ジョイボールは相対的な違いを格納します。 また、あるトラックボールイベントは s の変化と y の変化の両方が含まれています。 それらに対応した我々の場合は次のようになります。

Example 3-7. ジョイスティックのボールイベント


    case SDL_JOYBALLMOTION:  /* ジョイボールの動きを処理 */
    if( event.jball.ball == 0 )
    {
      /* ボールの処理 */
    }
    break;

上ではジョイスティックにおいて最初のジョイボールをチェックしています。 位置の変化は event.jball.xrelevent.jball.yrel に格納されます。

最後に、ハットスイッチのイベントがあります。 ハットスイッチは押された方向だけを報告します。 ビットマスクを使ってハットスイッチの意志をチェックします。

SDL_HAT_CENTERED
SDL_HAT_UP
SDL_HAT_RIGHT
SDL_HAT_DOWN
SDL_HAT_LEFT

また上記のあらかじめ定義された組み合わせもあります。

SDL_HAT_RIGHTUP
SDL_HAT_RIGHTDOWN
SDL_HAT_LEFTUP
SDL_HAT_LEFTDOWN

ハットスイッチに対する我々の場合は次のようになります。

Example 3-8. ジョイスティックのハットスイッチイベント


    case SDL_JOYHATMOTION:  /* ハットスイッチの動きを処理 */
    if ( event.jhat.hat & SDL_HAT_UP )
    {
        /* ここで上の場合を処理 */
    }

    if ( event.jhat.hat & SDL_HAT_LEFT )
    {
		/* ここで左の場合を処理 */
    }

    if ( event.jhat.hat & SDL_HAT_RIGHTDOWN )
    {
		/* ここで右と下が一緒に押された場合を処理 */
    }
    break;

システム上のジョイスティックの数とその名前の問い合わせに加えて、 接続されたジョイスティックの能力を問い合わせる追加の関数があります。

SDL_JoystickNumAxes ジョイスティックの軸の数を返します。
SDL_JoystickNumButtons ジョイスティックのボタンの数を返します。
SDL_JoystickNumBalls ジョイスティックのボールの数を返します。
SDL_JoystickNumHats ジョイスティックのハットスイッチの数を返します。

これらの関数を使うためには、 ジョイスティックをオープンしたときに得た ジョイスティック構造体を渡す必要があります。 例えば、

Example 3-9. ジョイスティックの特徴の問い合わせ

    int number_of_buttons;
    SDL_Joystick *joystick;

    joystick = SDL_JoystickOpen(0);
    number_of_buttons = SDL_JoystickNumButtons(joystick);

コードのこのブロックはシステムにおける最初のジョイスティックにある ボタンの数を取得します。