AndEngine入門3

[AndEngine入門2](http://qiita.com/PianoScoreJP/items/46d4b2a6a56555590a1b)の続きです
この記事は[こちら](http://www.raywenderlich.com/12065/how-to-create-a-simple-android-game)の記事を参考にしています。

前回は背景画像とタワーを表示するところまでを説明しました。

今回はリングを表示して動かす部分を説明します。

基本的な原理はここまでと同じです。
画像ファイルからテクスチャを作成し、スプライトを作成して表示させます。

ここではリング用のクラスを作成します。

#リング用のクラスを作成する
プロジェクトのソースフォルダを右クリックし、[新規]-[クラス]をクリックします。
次の画面で以下のように入力します。
パッケージ：com.towerofhanoi
名前：Ring
スーパークラス：org.andengine.entity.sprite.Sprite

![image](https://qiita-image-store.s3.amazonaws.com/0/75209/8ba8a5f0-68d9-0a21-7a2b-9a05a2dbb694.png)

中身は以下のように記述します

````Ring.java
package com.towerofhanoi;

import java.util.Stack;

import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

public class Ring extends Sprite {
    private int mWeight;
    private Stack mStack; //this represents the stack that this ring belongs to
    private Sprite mTower;
 
    public Ring(int weight, float pX, float pY, ITextureRegion pTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager) {
        super(pX, pY, pTextureRegion, pVertexBufferObjectManager);
        this.mWeight = weight;
    }
    //このリングの大きさを取得します。
    public int getmWeight() {
        return mWeight;
    }
 
    public Stack getmStack() {
        return mStack;
    }
    //このリングが属するスタックをセットします。
    public void setmStack(Stack mStack) {
        this.mStack = mStack;
    }
    //このリングが属するタワーを取得する
    public Sprite getmTower() {
        return mTower;
    }
 　　//このリングが属するタワーをセットする
    public void setmTower(Sprite mTower) {
        this.mTower = mTower;
    }
}

````

このクラスはSpriteクラスを継承しています。
このクラスのインスタンスがリングの一つを表現します。
独自のメソッドをいろいろ定義しています。
次にメインファイルである*TowerOfHanoiActivity.java*を編集します。

次のクラスをインポートします

````TowerOfHanoiActivity.java
import java.util.Stack;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
````

次にonCreateSceneメソッドの最後(returnの前)に下記を追記します。

````TowerOfHanoiActivity.java
Ring ring1 = new Ring(1, 139, 174, this.mRing1, getVertexBufferObjectManager());
Ring ring2 = new Ring(2, 118, 212, this.mRing2, getVertexBufferObjectManager());
Ring ring3 = new Ring(3, 97, 255, this.mRing3, getVertexBufferObjectManager());
scene.attachChild(ring1);
scene.attachChild(ring2);
scene.attachChild(ring3);
````

ポールを作ったときは、`new Sprite`としていましたが、
今回はSpriteを継承したRingクラスを使用しているので、`new Ring`となります。
第一引数では、「リングの重さ」を定義しています。
この状態で実行すると下記のようになります。
![image](https://qiita-image-store.s3.amazonaws.com/0/75209/82490ddb-a96e-9f21-e84e-005430e041a6.png)

#タッチイベントを実装する

次にリングを動かすためのコードを記述します。
イベントはRingのインスタンスに対して実装します。
また、Ringの重なり状態をStackを使って管理します。

まずJava.util.Stackクラスをインポートします。

````TowerOfHanoiActivity.java
import import java.util.Stack
`````

次にクラス変数を追加します。

````TowerOfHanoiActivity.java
//それぞれのタワーのリングの重なりを表現するスタック
private Stack mStack1, mStack2, mStack3;
````

**onCreateResouce**メソッドの中の最後で、上記変数を初期化します。

````TowerOfHanoiActivity.java
this.mStack1 = new Stack();
this.mStack2 = new Stack();
this.mStack3 = new Stack();
````

次にに**onCreateScene**メソッドの最後、returnの前に下記を追記します

````TowerOfHanoiActivity.java
// 4 - Add all rings to stack one
this.mStack1.add(ring3);
this.mStack1.add(ring2);
this.mStack1.add(ring1);
// 5 - Initialize starting position for each ring
ring1.setmStack(mStack1);
ring2.setmStack(mStack1);
ring3.setmStack(mStack1);
ring1.setmTower(mTower1);
ring2.setmTower(mTower1);
ring3.setmTower(mTower1);
// 6 - Add touch handlers
scene.registerTouchArea(ring1);
scene.registerTouchArea(ring2);
scene.registerTouchArea(ring3);
scene.setTouchAreaBindingOnActionDownEnabled(true);
````

上記コードは
1.すべてのリングをスタック1にセット
2.スタック1をすべてのリングにセット
3.すべてのリングにタワー1をセット
4.タッチイベントをすべてのリングにセット

ということをやっています

次にSpriteクラスのonAreaTouchメソッドをオーバーライドします。
その前に一つクラスをimportしておきます

````TowerOfHanoiActivity.java
import org.andengine.input.touch.TouchEvent;
````
**onCreateScene**メソッドの下記部分を書き換えます。

この部分を

````TowerOfHanoiActivity.java
Ring ring1 = new Ring(1, 139, 174, this.mRing1, getVertexBufferObjectManager());

````
このように書き換えます

````TowerOfHanoiActivity.java
Ring ring1 = new Ring(1, 139, 174, this.mRing1, getVertexBufferObjectManager()) {
    @Override
    public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
        if (((Ring) this.getmStack().peek()).getmWeight() != this.getmWeight())
            return false;
        this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2, 
            pSceneTouchEvent.getY() - this.getHeight() / 2);
        if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_UP) {
            checkForCollisionsWithTowers(this);
        }
        return true;
    }
};
````

上記コードのオーバーライドした部分 *onAreatouched*メソッドの解説です。

1.まずはこの部分

````java
if (((Ring) this.getmStack().peek()).getmWeight() != this.getmWeight())
            return false;
````
`this.getmStack().peek()`
`this.getmStack()`は*Ring*クラスに定義した、Ringに現在登録されているスタックを取得するメソッドです。
`this.getmStack().peek()`でスタックに追加された最後のオブジェクトを取得します。

スタックに追加されたオブジェクトは*Ring*クラスのインスタンスのはずですから、Castして、Ringクラスの*getWeight*メソッドを呼び出しています。

「操作されたリングの重さと、スタックの最後に追加されたリング（タワーの一番上のリング）の重さが一致しない場合、何もしない(return false)」
ということをやっています。

2.上記をクリアしたらリングを動かせるようになりますが、まず下記部分が実行されます。

````java
 this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2, 
            pSceneTouchEvent.getY() - this.getHeight() / 2);
````

リングの位置をタッチした位置に動かしています。
`-this.getWIdth`や`-this.getHeight`の部分は、リングの中央がタッチしている部分にくるようにしているわけですね。タッチしている部分がリングの端になると、フォーカスを外れてしまう可能性があるため、このようにしていると思います。

3.最後にドラッグが終了した場合ですね。

````java
        if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_UP) {
            checkForCollisionsWithTowers(this);
        }
        return true;
````
ドラッグが終わるとcheckForCollinsionWithTowersメソッドが実行されます。
`checkForCollinsionWithTowers`
これはまだ解説してませんでした。上記メソッドを*TowerOfHanoiActivity.java*に定義します。


````TowerOfHanoiActivity.java
private void checkForCollisionsWithTowers(Ring ring) {
    Stack stack = null;
    Sprite tower = null;
    if (ring.collidesWith(mTower1) && (mStack1.size() == 0 ||             
            ring.getmWeight() < ((Ring) mStack1.peek()).getmWeight())) {
        stack = mStack1;
        tower = mTower1;
    } else if (ring.collidesWith(mTower2) && (mStack2.size() == 0 || 
            ring.getmWeight() < ((Ring) mStack2.peek()).getmWeight())) {
        stack = mStack2;
        tower = mTower2;
    } else if (ring.collidesWith(mTower3) && (mStack3.size() == 0 || 
            ring.getmWeight() < ((Ring) mStack3.peek()).getmWeight())) {
        stack = mStack3;
        tower = mTower3;
    } else {
        stack = ring.getmStack();
        tower = ring.getmTower();
    }
    ring.getmStack().remove(ring);
    if (stack != null && tower !=null && stack.size() == 0) {
        ring.setPosition(tower.getX() + tower.getWidth()/2 - 
            ring.getWidth()/2, tower.getY() + tower.getHeight() - 
            ring.getHeight());
    } else if (stack != null && tower !=null && stack.size() > 0) {
        ring.setPosition(tower.getX() + tower.getWidth()/2 - 
            ring.getWidth()/2, ((Ring) stack.peek()).getY() - 
            ring.getHeight());
    }
    stack.add(ring);
    ring.setmStack(stack);
    ring.setmTower(tower);
}
````

上記コードは、
「動かしていたリングを手放したときタワーにぶつかっていたら、そのタワーにリングをセットする」
ということをやっています。

上から順に説明します。

1.空のstackと空のspriteを準備しておきます。
````
    Stack stack = null;
    Sprite tower = null;
````

2.動かしたリングをどのタワーに積み上げるかを判定します。
````
    if (ring.collidesWith(mTower1) && (mStack1.size() == 0 ||             
            ring.getmWeight() < ((Ring) mStack1.peek()).getmWeight())) {
        stack = mStack1;
        tower = mTower1;
````

`collidesWith`はSpriteのメソッドで、他のスプライトとの重なり状態を判定します。
mStack1はタワー1のリングの積み重なり状態を表します。
`mStack1.size()==0`は、タワーにリングが一つもない状態です。
`mStack1.size()>0`ならタワーにリングが1つ以上ある状態です。
` ring.getmWeight() < ((Ring) mStack1.peek()).getmWeight())`は、
積み上げようとしているリングの大きさが、すでに積み重なっているリングの大きさより小さい場合、stackにmStack1を、towerにタワー1をセットします。


タワー2,タワー3についても同様の判定、処理を行います。
いずれにも該当しなかった場合、下記の処理が行われます。

````
 else {
        stack = ring.getmStack();
        tower = ring.getmTower();
    }
`````

動かしたリングが保持していたスタックとタワーをセットします。
要は元の場所に戻すってことです。

3.最後の部分です。

````
    ring.getmStack().remove(ring);
    if (stack != null && tower !=null && stack.size() == 0) {
        ring.setPosition(tower.getX() + tower.getWidth()/2 - 
            ring.getWidth()/2, tower.getY() + tower.getHeight() - 
            ring.getHeight());
    } else if (stack != null && tower !=null && stack.size() > 0) {
        ring.setPosition(tower.getX() + tower.getWidth()/2 - 
            ring.getWidth()/2, ((Ring) stack.peek()).getY() - 
            ring.getHeight());
    }
````

`ring.getmStack().remove(ring);`
まず、リングに設定されているスタックから、現在登録されているリングを削除します。

````
    if (stack != null && tower !=null && stack.size() == 0) {
        ring.setPosition(tower.getX() + tower.getWidth()/2 - 
            ring.getWidth()/2, tower.getY() + tower.getHeight() - 
            ring.getHeight());
    }
````
積み上げようとしているタワーのスッタクが0(タワーにはリングが1つも存在しない状態)
の場合、タワーの一番下にリングを動かします。

````
 else if (stack != null && tower !=null && stack.size() > 0) {
        ring.setPosition(tower.getX() + tower.getWidth()/2 - 
            ring.getWidth()/2, ((Ring) stack.peek()).getY() - 
            ring.getHeight());
    }
````

それ以外の場合(積み上げようとしているタワーに、他のリングが積み重なっている場合)、
スタックの一番上にあるリングの位置を取得し、その上にリングを動かします。
この状態で実行すると、リングを他のタワーに動かすことができます。

コードの解説は以上です
#おわり
以上で[こちら](http://www.raywenderlich.com/12065/how-to-create-a-simple-android-game)を参考にしたAndEngine入門を終了します。

このサンプルのソースコードは[こちら](http://cdn3.raywenderlich.com/downloads/TowerOfHanoi.zip)で公開されています。
Eclipseプロジェクトになっていますので、そのままインポートして使えると思います。

なお、記事中に不備、間違い等ございましたらご連絡ください。