AndEngine講座‎ > ‎

第6回:AndEngineでタッチ入力

作成日:2015/01/18


動画版

Youtube版

AndEngineでAndroid2Dゲーム開発 part6 AndEngineでタッチ入力


ニコニコ版

AndEngineでAndroid2Dゲーム開発 part6 AndEngineでタッチ入力 - ニコニコ動画:

前回のあらすじと概要

前回:第5回:ゲーム作成開始!

前回はゲーム画面にテキストやパネルを配置しました。

今回は、タッチしたパネルの番号と、次にタッチする番号とが同じだったらパネルが消えるような処理を実装しました。

タッチ処理の詳しい説明

タッチを処理する方法は大きく分けて2つ。さらにもう少し細かく分けると計3つの種類があります。

  • シーンの特定のパーツのタッチを処理する
    • パーツ自身に処理させる (1)
    • シーンに処理させる (2)
  • シーン全体のタッチを処理する
    • シーンに処理させる (3)

特定のパーツのタッチはAreaTouchと言い、シーン全体のタッチはSceneTouchといいます。 うまく使い分けてください。

上の(1), (2), (3), の処理は、どれも決まった形のメソッドが呼び出されることで行います。
つまり、タッチされたときに呼び出されるメソッドを実装する必要がある訳です。

タッチ入力が行われると次のような順番で処理が行われます。

  1. シーンに登録されている領域に対して以下の処理
    1. タッチ座標が領域に含まれているかチェック
    2. 含まれていればパーツ自身にタッチの処理をさせる(1)
    3. パーツ自身のタッチ処理が行われなければ、シーンにパーツのタッチ処理をさせる(2)
    4. (1)(2)どちらかでタッチ処理が行われたらその時点でタッチ処理は終了
  2. 登録されている全ての領域に関してタッチ処理が行われなければシーン全体のタッチ処理を行う (3)

「タッチ処理が行われたかどうか」は、その処理を行うメソッドの戻り値で判断されます。
trueが返されると処理が行われた。falseが返されると行われなかったと判断されます。
順番や、処理の戻り値を意識しないと、意図しない動作をするかもしれないので注意してください。

以下では、3種類のタッチ処理を順に見ていきます。

パーツのタッチをパーツ自身に処理させる

方法

  • シーンにパーツの領域を登録
  • パーツにデフォルトで定義されているタッチ時の処理をオーバーライドする

コードは以下のような感じです。

パーツ自身にタッチ処理をさせる例

Sprite sprite = new Sprite(x, y,  textureRegion, vertexBufferObjectManager){
  @Override
  public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY)  {
    //処理を行う
    return true;
  };
};
scene.registerTouchArea(sprite);
scene.attouchChild(sprite);

ちなみに、もともと定義されているonAreaTouchメソッドは、何もせずにfalseを返すだけです。

パーツのタッチをシーンに処理させる

方法

  • シーンにパーツの領域を登録
  • シーンにsetOnAreaTouchListenerを使って、タッチ処理を定義したオブジェクトをセット
    • セットするリスナーはIOnAreaTouchListenerを実装している必要がある
  • リスナーのOnAreaTouchedの中身を実装

コードは以下のような感じです。

パーツのタッチをシーンに処理させる例

Sprite sprite = new Sprite(x, y,  textureRegion, vertexBufferObjectManager);
scene.registerTouchArea(sprite);
scene.attouchChild(sprite);


scene.setOnAreaTouchListener(new IOnAreaTouchListener() {
  @Override
  public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
    ITouchArea pTouchArea, float pTouchAreaLocalX,
    float pTouchAreaLocalY) {
      //処理を行う
      return true;
  }
});

ITouchArea pTouchAreaは、タッチされたEntity(正確にはもう何段階か継承したサブクラス)のオブジェクトです。
タッチされた領域のクラス(SpriteやText)がわかっているなら、キャストして使うことが出来ます。

シーン全体のタッチをシーンに処理させる

方法

  • シーンにsetOnSceneTouchListenerを使って、タッチ処理を定義したオブジェクトをセット
    • IOnSceneTouchListenerを実装している必要がある
  • リスナーのOnSceneTouchedの中身を実装

コードは以下のような感じです。

シーンのタッチをシーンに処理させる例

scene.setOnSceneTouchListener(new IOnSceneTouchListener() {
  @Override
  public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
      //処理を行う
      return true;
    }
});

エリアタッチの処理の順番

registerTouchAreaで登録されている領域が、どの順序で処理されるかは、登録された順番によります。
基本は登録された順番です。 登録が新しい順にも出来ます。

  • setOnAreaTouchTraversalBackToFront()で古い登録から(デフォルト)
  • setOnAreaTouchTraversalFrontToBack()で新しい登録から

つまり、見た目の重なりが変化するゲームの場合は直感的でない挙動をするかもしれません。

TouchEventについて

以上で紹介したタッチ時のメソッドの引数であるTouchEvent pSceneTouchEventには、
検出したタッチに関するあらゆる情報が含まれています。それを用いて以下のようなことが可能です。

アクションの区別について

タッチ時のメソッドは、タッチの瞬間だけでなく、タッチしたまま指をドラッグしたとき、 タッチした指を離したときも呼ばれます。
これらの種類を区別するには、TouchEvent pSceneTouchEventを使います。 このオブジェクトに対して、どのタッチの種類かを得ます。

コードは以下のような感じ

タッチの種類の区別の例

scene.setOnAreaTouchListener(new IOnAreaTouchListener() {
  @Override
  public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
    ITouchArea pTouchArea, float pTouchAreaLocalX,
    float pTouchAreaLocalY) {
      if(pSceneTouchEvent.isActionDown()){
        //押したときの処理
      }else if(pSceneTouchEvent.isActionMove()){
        //指を動かしたときの処理
      }else if(pSceneTouchEvent.isActionUp()){
        //指を離したときの処理
      }
    }
});

シーンにおけるタッチ位置座標の取得

AreaTouchのメソッドのx座標、y座標は、タッチ位置の、その領域における座標です。
タッチ位置のScene上での座標はTouchEvent pSceneTouchEventを使って得ることが出来ます。

コードは以下のような感じ

タッチ位置のシーンにおける座標の取得の例

scene.setOnSceneTouchListener(new IOnSceneTouchListener() {
  @Override
  public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
    //シーン上のタッチ座標の取得
    float x = pSceneTouchEvent.getX();
    float y = pSceneTouchEvent.getY();

    //処理を行う

    return true;
  }
  });

ソースコード

今回は、変更のあったGameSceneのソースコードのみ掲載します。

GameScene.java

package com.example.yukkuritouchnumber;

import org.andengine.entity.scene.IOnAreaTouchListener;
import org.andengine.entity.scene.ITouchArea;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.text.Text;
import org.andengine.input.touch.TouchEvent;
import org.andengine.util.color.Color;

public class GameScene extends Scene {

	ResourceManager resourceManager;

	// 次にタッチする番号
	int nextNumber;

	//次にタッチする番号のテキスト
	private Text nextText;

	public GameScene(ResourceManager resourceManager) {
		super();
		this.resourceManager = resourceManager;

		// 次にタッチする番号の初期化
		nextNumber = 1;

		createScene();
	}

	private void createScene() {
		setBackground(new Background(Color.WHITE));

		nextText = new Text(40, 40, resourceManager.gameFont, String.format("Next %3d", nextNumber),
				"Next XXX".length(), resourceManager.vbom);
		attachChild(nextText);

		// 現在時間を表示
		Text timeText = new Text(0, 40, resourceManager.gameFont,
				"Time XX:XX.XX", "Time XX:XX.XX".length(), resourceManager.vbom);
		// テキストの右端が画面の右端から40px離れているようにする
		timeText.setX(resourceManager.camera.getWidth() - 40
				- timeText.getWidth());
		attachChild(timeText);

		// パネルを25個配置する

		for (int i = 0; i < 25; i++) {
			int xPosIndex = i % 5;
			int yPosIndex = i / 5;

			float x = 40 + 80 * xPosIndex;
			float y = 280 + 80 * yPosIndex;

			// パネルの作成
			Sprite panel = new Sprite(x, y, resourceManager.panelRegion,
					resourceManager.vbom);
			// パネルに番号の追加
			int panelNumber = i + 1;
			Text text = new Text(0, 0, resourceManager.panelFont,
					String.valueOf(panelNumber), resourceManager.vbom);
			// 文字の座標をパネルの中心に合わせる
			float textX = (panel.getWidth() - text.getWidth()) / 2;
			float textY = (panel.getHeight() - text.getHeight()) / 2;
			text.setPosition(textX, textY);
			// パネルにテキストを追加する
			panel.attachChild(text);
			attachChild(panel);

			// パネルにタグを設定
			panel.setTag(panelNumber);

			// パネルをタッチ領域に登録
			registerTouchArea(panel);
		}

		// タッチ時の処理をシーンに登録
		setOnAreaTouchListener(new IOnAreaTouchListener() {

			@Override
			public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
					ITouchArea pTouchArea, float pTouchAreaLocalX,
					float pTouchAreaLocalY) {
				// タッチ時の処理

				// 押したときだけ処理を行う
				if (pSceneTouchEvent.isActionDown()) {

					// パネルのタグと、次にタッチする番号が等しいときだけ処理を行う
					if (nextNumber == ((Sprite) pTouchArea).getTag()) {
						// タッチされたパネルを非表示にする。
						((Sprite) pTouchArea).setVisible(false);


						//最後のパネルがタッチされたかどうかを判別する
							if(nextNumber==25){
								//最後のパネルだった場合
								nextText.setText("Clear!!");

							}else{
								//次にタッチする番号を増やす
								nextNumber ++;

								//次にタッチする番号を表示する。
								nextText.setText(String.format("Next %3d", nextNumber));

							}
					}
				}
				// タッチ処理が行われたことをあらわすtrueを返す。
				return true;
			}
		});

	}
}

Comments