サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

入力を制御する -- マウスとキーボード

このページでは、LibGDXにおいてマウスとキーボードをどのように制御するかを見て行きます。 入力を制御する方法については、入力をポーリングする( このように… “やあ、何か入力は発生したかい? ないのか、分かったよ…  今度は何か発生したかい? ないのか、分かったよ… 今は?  発生した!  その入力を制御してくれ” )もしくはイベントを制御する ( “やあ、君の代わりに僕がこのイベントを取得したよ!” )、といった二つの方法があります。 一般的にどちらの方法を使うかは、あなたのコードがどのような構造をしているかによって変わります。 ポーリングの方がリソースを多く消費する傾向にありますが、色々考慮して見ても、それがどちらを使うかの要因にはほぼならないでしょう。



キーボードの入力をポーリングする

それではさっそく、キーボードの入力をポーリングする方法を見てみましょう。 以下がそのコードになります:

package input.gamefromscratch.com;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class InputDemo implements ApplicationListener {
    private SpriteBatch batch;
    private Texture texture;
    private Sprite sprite;
    
    @Override
    public void create() {        
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();
        batch = new SpriteBatch();
        
        texture = new Texture(Gdx.files.internal("data/0001.png"));
        sprite = new Sprite(texture);
        sprite.setPosition(w/2 -sprite.getWidth()/2, h/2 - sprite.getHeight()/2);
    }

    @Override
    public void dispose() {
        batch.dispose();
        texture.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
            if(Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT))
                sprite.translateX(-1f);
            else
                sprite.translateX(-10.0f);
        }
        if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
            if(Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT))
                sprite.translateX(1f);
            else
                sprite.translateX(10.0f);
        }
        batch.begin();
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

強調表示された部分と translateX メソッド部分以外は、全て以前と同じ内容です。 基本的には、画面の中央に簡単なsprite を表示して、毎フレームごとにユーザーが左矢印キーや右矢印キーを押しているかを確認します。 矢印キーを押していた場合は、ユーザーがコントロールキーも押しているかを確認します。コントロールキーを押していた場合は、押しているキーに応じた方向へゆっくりと移動させます。 コントロールキーを押していない場合は、代わりに10ピクセル移動させます。

以下がそのアプリです。まず最初にこのフレームをクリックしてキーボードのフォーカスをする必要があります:

フレーム内の画像が動作しない場合は、ここをクリックしてください

左矢印キーや右矢印キーを押すと飛行機が移動します。コントロールキーを押したままにするとゆっくり移動します。クリッピング処理は行っていないので、飛行機が画面外に飛んでいってしまうかもしれません。

新たに追加してコードが何をしているのかという点について、Sprite.translateXメソッドは一目瞭然です。 このメソッドはspriteをX軸方向に特定のピクセル分移動させます。 同様にtranslateY メソッドというものもあり、さらに一般的なtranslate メソッドというものもあります。 この例で重要なメソッドは、グローバルGdx オブジェクトのinputインスタンスにある、isKeyPressed()メンバ関数です。 Gdx.filesにアクセスした時にも、似たようなインスタンメンバを使用しました。 これらはGDX が依存している様々なサブシステムへの、 public で static な参照です。詳細についてはここで読めます。 isKeyPressed にはKeys オブジェクト で定義されているキー値を渡し、そのキーが現在押されている場合は true が戻り値として返ります。 コントロールキーも一緒に押した場合のテストをすると分かりますが、 複数のキーを同時に押すこともできます。



マウスの入力をポーリングする

それでは、マウスの入力をポーリングする方法を見てみましょう。 ページを節約するために、このコードは前のサンプルと同じもので、render() メソッドのみを書き換えています。

public void render() {        
    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    
    if(Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
        sprite.setPosition(Gdx.input.getX() - sprite.getWidth()/2,
                Gdx.graphics.getHeight() - Gdx.input.getY() - sprite.getHeight()/2);
    }
    if(Gdx.input.isButtonPressed(Input.Buttons.RIGHT)){
        sprite.setPosition(Gdx.graphics.getWidth()/2 -sprite.getWidth()/2, 
                Gdx.graphics.getHeight()/2 - sprite.getHeight()/2);
    }
    batch.begin();
    sprite.draw(batch);
    batch.end();
}

上記コードでは前のコードとは違って、Buttonsオブジェクトに定義されているボタン値をisButtonPressed メソッドに渡して、マウスボタンが押されているかどうかを確認しています。 マウスの左ボタンを押されている場合、 Gdx.input.getX()とGdx.input.getY()を使ってマウスの位置を取得し、取得した位置にsprites の位置を設定しています。 この時の計算では少し複雑なことをしているように見えます。なぜspriteの位置にgetX/Yメソッドで取得した値をそのまま設定していないのでしょうか? これには二つの理由があります。一つ目は、spriteの座標は画像の左下を基点として配置されるためです。ですから、spriteを画面の中央に配置したい場合は、spriteの横幅と高さの半分の長さを考慮する必要があります。 二つ目は、LibGDX では左下を開始点(0,0)としていますが、マウスの位置については画面の左上を開始点(0,0)としているためです。 位置から画面の高さを引くだけで、画面座標におけるマウスの位置が取得できます。 また、ユーザーが右のマウスボタンを押しているかも確認し、押していた場合は飛行機のspriteを画面の中央に配置します。

フレーム内の画像が動作しない場合は、ここをクリックしてください

あらためて言いますが、マウスイベントの受信をする前に上記の枠内をクリックする必要があります ( ブラウザによって変わります )。 クリックして、spreiteをクリックした位置に移動させてみましょう。 右クリックをすると既定の位置に戻りますが ( 理論的には… )、右クリックの動作はウェブブラウザによってやや違ってきます。



イベント駆動でキーボードとマウスを制御する

ここまでは上記二つの制御機能の例を( 一つの例のように )を見てきましたが、今回はイベント駆動型を使ったやり方です。

package input.gamefromscratch.com;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class InputDemo implements ApplicationListener, InputProcessor {
    private SpriteBatch batch;
    private Texture texture;
    private Sprite sprite;
    private float posX, posY;
    
    @Override
    public void create() {        
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();
        batch = new SpriteBatch();
        
        texture = new Texture(Gdx.files.internal("data/0001.png"));
        sprite = new Sprite(texture);
        posX = w/2 - sprite.getWidth()/2;
        posY = h/2 - sprite.getHeight()/2;
        sprite.setPosition(posX,posY);
        
        Gdx.input.setInputProcessor(this);
    }

    @Override
    public void dispose() {
        batch.dispose();
        texture.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        sprite.setPosition(posX,posY);
        batch.begin();
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }

    @Override
    public boolean keyDown(int keycode) {
        float moveAmount = 1.0f;
        if(Gdx.input.isKeyPressed(Keys.CONTROL_LEFT))
            moveAmount = 10.0f;
        
        if(keycode == Keys.LEFT)
            posX-=moveAmount;
        if(keycode == Keys.RIGHT)
            posX+=moveAmount;
        return true;
    }

    @Override
    public boolean keyUp(int keycode) {
        return false;
    }

    @Override
    public boolean keyTyped(char character) {
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        if(button == Buttons.LEFT){
            posX = screenX - sprite.getWidth()/2;
            posY = Gdx.graphics.getHeight() - screenY - sprite.getHeight()/2;
        }
        if(button == Buttons.RIGHT){
            posX = Gdx.graphics.getWidth()/2 - sprite.getWidth()/2;
            posY = Gdx.graphics.getHeight()/2 - sprite.getHeight()/2;
        }
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }
}

そして以下が実行結果です:

フレーム内の画像が動作しない場合は、 ここをクリックしてください

このコードは、入力をポーリングするサイトはかなり異なる構造をしています。注意すべき最も重要なことは、クラス宣言です:

public class InputDemo implements ApplicationListener, InputProcessor { 

さらにもう一つのインタフェースInputProcessorを実装しています。コードを見ると分かりますが、InputProcessorを実装するといくつかのメソッドが追加されます。 このコードで扱っている中で最も重要なものは、keyDown とtouchDownです。 Touch と聞いて疑問に思いましたか?実は、LibGDX ではマウス入力とタッチ入力を同じものとして扱うのです。 これについては後で詳しく見ていきます。 インタフェースのいくつかのメソッドを実装するのに加えて、以下のようにして、グローバル inputインスタンスに InputProcessorを登録する必要もあります:

Gdx.input.setInputProcessor(this);

これで、イベントが発生する度に様々なイベントハンドラーが呼び出されるようになります。 keyDown はキーが押された時に発生します。 ( 一方、 keyUp はキーが離された時に発生し、 keyTyped is fired after it has been fired and released )。 引数には押されたキーの値が入ります。 改めて言いますが、これらの値はKeyオブジェクト内で使用できます。 コンロトールキーが押されているかどうかを確認するために、未だにポーリングを使用していることに気づいたかもしれません。 他にも、コントロールキーが押された時にフラグをセットして、キーが離された時にフラグをクリアするやり方があります。 keyDown イベントは、押された個々のキーごとに発生するということを理解してください。 そのため、複数のキーが同時に押された状況を制御したい場合は、今回のやり方がベストな方法でしょう。 また、移動させるには複数回キーを押す必要があることにも気づいたかもしれません。 これは、キーが押された時には一回しかイベントが発生しないためです(キーが離された時も同様です)。 前回と同じ様にキーを押したままにするとキャラクターが移動し続ける動作にしたい場合は、あなた自身でロジックを実装する必要があります。 これについても、keyDown のコード内でフラグを設定して、keyUp が呼び出された時にフラグを切り替えるだけでできます。

一方、 touchDown イベントははるかに簡単です。 “マウス” イベントの制御なのに “タッチ”と呼ぶのでややこしいかもしれませんが、これには意味があります。 一般的にはマウス操作とタッチ操作を制御するロジックは全く同じなので、それらを別々に扱うのは無意味なのです。 touchDown へ渡される引数は、タッチ/クリックされた位置のx座標とy座標、ポインター、クリックされたボタンです。 携帯端末では、Button の値は常にButtons.LEFTになります。 ここでも、スクリーン座標と画像の座標は同じではないので、位置を修正して対応する必要があります。 Notice how I glossed over just what exactly pointer is?  実は、私としてはpointer というのは少し変な名前だと思います。 pointerという言葉は既に別の意味でよく使われているので、おそらくはTouchIndex と呼ぶのが分かりやすいでしょう、 pointer の値は0からnの間になり ( LibGDXでは20として定義されていますが、実際に使われるのはそれよりかなり少ない値になります。 )、複数のタッチイベントが同時に発生した場合のタッチされた順番を表します。 したがって、あなたが複数の指でタッチをした場合、pointer の値が0だとそのタッチイベントは最初に画面にタッチした指を表します。 一方、値が3だと4番目に画面にタッチした指をとなります。 心配しないでください。これについては、後で具体的にタッチ操作を扱う際に説明します。