以前作ったサンプルライブラリの内、MainActivity クラスと SceneManager クラスに変更はありません。
 
 新たな素材を読み込むため、ResourcesManager クラスに追記します。
 それぞれの BitmapTextureAtlas と ITextureRegion を用意して

	private BitmapTextureAtlas colaTextureAtlas;
	public ITextureRegion cola_region;
	private BitmapTextureAtlas maskTextureAtlas;
	public ITextureRegion mask_region;
	private BitmapTextureAtlas ninePatchTextureAtlas;
	public ITextureRegion ninePatch_region;

 画像を読み込みます。

	public void loadResources(){
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
        spriteTextureAtlas = new BitmapTextureAtlas(activity.getTextureManager(), 256, 256, TextureOptions.BILINEAR);
        sprite_region = BitmapTextureAtlasTextureRegionFactory.createFromAsset(spriteTextureAtlas, activity, "sprite.png", 0, 0);
        spriteTextureAtlas.load();
        colaTextureAtlas = new BitmapTextureAtlas(activity.getTextureManager(), 256, 256, TextureOptions.BILINEAR);
        cola_region = BitmapTextureAtlasTextureRegionFactory.createFromAsset(colaTextureAtlas, activity, "cola.png", 0, 0);
        colaTextureAtlas.load();
        maskTextureAtlas = new BitmapTextureAtlas(activity.getTextureManager(), 4, 4, TextureOptions.BILINEAR);
        mask_region = BitmapTextureAtlasTextureRegionFactory.createFromAsset(maskTextureAtlas, activity, "mask.png", 0, 0);
        maskTextureAtlas.load();
        ninePatchTextureAtlas = new BitmapTextureAtlas(activity.getTextureManager(), 16, 16, TextureOptions.BILINEAR);
        ninePatch_region = BitmapTextureAtlasTextureRegionFactory.createFromAsset(ninePatchTextureAtlas, activity, "ninePatch.png", 0, 0);
        ninePatchTextureAtlas.load();
	}

 
 シーン周りは大改造となります。
 先ずは、BaseScene クラス。
 ZoomCamera につられず、常に等倍・位置固定の Scene のサブクラス HUD クラスにダイアログを表示します。

	protected HUD hud;
	protected NineSliceSprite dialogSprite;
	private float scaleTime = 0.5f;

 今後 BaseScene のサブクラスが増えたときでもコードを加えることなくダイアログが表示できるよう、ここにメソッドを作ってしまいましょう。

	abstract void drawDialog(final float sec);

	protected void openDialog(final float x, final float y, final float width, final float height){
		attachMask();
		final float sec = scaleTime * (height / MainActivity.CAMERA_HEIGHT);
		dialogSprite = new NineSliceSprite(x, y, width, height, resourcesManager.ninePatch_region, 8, 8, 9, 9, vbom);
		ScaleModifier scaleModifier = new ScaleModifier(sec, 1.0f, 1.0f, 0.0f, 1.0f, new IEntityModifierListener(){
			public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem){}
			public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem){
				drawDialog(sec);
			}
		});
        scaleModifier.setAutoUnregisterWhenFinished(true);
		dialogSprite.registerEntityModifier(scaleModifier);
		hud.attachChild(dialogSprite);
	}

	protected void closeDialog(final float sec){
		ScaleModifier scaleModifier = new ScaleModifier(sec, 1.0f, 1.0f, 1.0f, 0.0f, new IEntityModifierListener(){
			public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem){
				detachMask();
			}
			public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem){}
		});
        scaleModifier.setAutoUnregisterWhenFinished(true);
		dialogSprite.registerEntityModifier(scaleModifier);
	}

	protected void attachMask(){
		final Sprite mask = new Sprite(MainActivity.CAMERA_WIDTH / 2, MainActivity.CAMERA_HEIGHT / 2, MainActivity.CAMERA_WIDTH, MainActivity.CAMERA_HEIGHT, resourcesManager.mask_region, vbom){
			@Override
			public boolean onAreaTouched(final TouchEvent pAreaTouchEvent, final float x, final float y){ 
				return true;
			}
		};
    	mask.setAlpha(0.5f);
    	hud.attachChild(mask);
		hud.registerTouchArea(mask);
		hud.setOnAreaTouchTraversalFrontToBack();
	}

	protected void detachMask(){
		hud.detachChildren();
		hud.clearTouchAreas();
	}

 openDialog() の最初に attachMask() を呼び出し、前面の全面に黒50%のマスクをかけ、onAreaTouched() の戻り値を true とすることで下のシーンにイベント通知しないようにしています。
 new NineSliceSprite(x, y, width, height, resourcesManager.ninePatch_region, 8, 8, 9, 9, vbom) でダイアログを作り、new ScaleModifier(sec, 1.0f, 1.0f, 0.0f, 1.0f, new IEntityModifierListener(){}) でY軸のスケールを0~1倍へと増やしていきます。終了した時点でダイアログの中身を描画する drawDialog() を呼び出すのですが、具体的には各シーンごとに実装します。
 closeDialog() は、その反対のことをしているだけですね。
 
 つづく。

 9パッチを利用してダイアログを実装します。
 ただのダイアログでは寂しいので、開くアニメーション、閉じるアニメーションを付け加えます。
 部分的なコードではわかり難いので、以前作ったサンプルプロジェクトに追記して動くプロジェクトとして掲載しましょう。
 題して「AndEngineのピンチズームとスクロールとロングタップを実装したサンプルプロジェクトにナインパッチを利用したアニメーションするオリジナルダイアログを実装する」略して「AndEngineでアニメーションダイアログを実装する」です。
 
 ピンチズームとスクロールは前回のままスプライトの Sprite への視点の拡大縮小移動を行いますが、ロングタップはスプライトの Sprite を回転させることを止め、スプライトの画面にタッチイベントを渡さないように全面にマスクをかけた上で、9パッチを利用した Sprite をアニメーションさせて開いたダイアログにコーラの Sprite を貼り付けます。コーラの Sprite をタッチすることによってダイアログをアニメーションで閉じ、マスクを外すこととします。
device-2014-06-21-115804 → device-2014-06-21-115813
 
 用意する画像は、コーラの画像、4×4ピクセルを黒く塗りつぶしただけのマスク画像、9パッチ画像です。
cola mask ninePatch
 
 9パッチ画像は16×16ピクセルでこのように作ってあります。
 ninePatch-zoom

 つづく。

 9パッチは自前で実装しても大したこたぁないやと、録に調べずに自前コードを書いていたのですが、AndEngine では2行で書けることを発見。ショック。

	NineSliceSprite dialogSprite = new NineSliceSprite(pX, pY, pWidth, pHeight, pTextureRegion, pInsetLeft, pInsetTop, pInsetRight, pInsetBottom, pVertexBufferObjectManager);
	this.attachChild(dialogSprite);

 Android 純正と違って、用いる画像の周囲に透過部分を入れなくても、拡張子も.9.png にしなくてもよいらしい。
 早く気付くべきだった。
 
 
 今作っている「撮影した画像をスライドパズル化するアプリ」で、カメラの設定がどうやっても横向きにしかならない。縦にしようとするとプレビュー画面が細長くなってしまう。
 でも、このアプリはどうしても縦向きにしたいのです(いや「どうしても」という程ではないのですが・・)。
 
 そこで、カメラ画面以外はダミーの Entity に貼り付けて270度回転させて表示するといった回りくどいことやっています。
 もう、座標系がぐるぐる回って訳わからん・・。上記の9パッチも縦画面の中、横に開いたりしてびっくりものです。

 お詫びと訂正。
 AndEngineのサンプルプロジェクト(その4)の GameScene.java の中で

    private Sprite sprite;
     
    @Override
    public void createScene(){
        createBackground();
        Sprite sprite = new Sprite(MainActivity.CAMERA_WIDTH / 2, MainActivity.CAMERA_HEIGHT / 2, resourcesManager.sprite_region, vbom);
        attachChild(sprite);
    }

と、しておりました。メンバ変数の意味がありませんね。次の記事につなげるためにも

    private Sprite sprite;
     
    @Override
    public void createScene(){
        createBackground();
        sprite = new Sprite(MainActivity.CAMERA_WIDTH / 2, MainActivity.CAMERA_HEIGHT / 2, resourcesManager.sprite_region, vbom);
        attachChild(sprite);
    }

このように、直させていただきます。
 
 さて。
 
 予告どおり、というか思い付きでロングタップも含め実装してみました。
 GameScene.java に追記。

package com.chabaori.sample;

import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.sprite.Sprite;
import org.andengine.input.touch.TouchEvent;
import org.andengine.input.touch.detector.ContinuousHoldDetector;
import org.andengine.input.touch.detector.HoldDetector;
import org.andengine.input.touch.detector.HoldDetector.IHoldDetectorListener;
import org.andengine.input.touch.detector.PinchZoomDetector;
import org.andengine.input.touch.detector.PinchZoomDetector.IPinchZoomDetectorListener;
import org.andengine.input.touch.detector.ScrollDetector;
import org.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
import org.andengine.input.touch.detector.SurfaceScrollDetector;

import com.chabaori.sample.SceneManager.SceneType;

public class GameScene extends BaseScene  implements IOnSceneTouchListener, IScrollDetectorListener, IPinchZoomDetectorListener, IHoldDetectorListener{
	private Sprite sprite;
    final static float MIN_ZOOM = 1.0f;
    final static float MAX_ZOOM = 3.0f;
    private SurfaceScrollDetector mScrollDetector;
    private PinchZoomDetector mPinchZoomDetector;
    private float mPinchZoomStartedCameraZoomFactor;
    private boolean isTouchOK = true;
    private boolean isMove;
    private boolean isHold = false;
    private ContinuousHoldDetector continuousHoldDetector;
    private long holdTime = 200;
    private float rotation = 0;
	
	@Override
	public void createScene(){
        setOnSceneTouchListener(this);
        setOnAreaTouchTraversalFrontToBack();
        mScrollDetector = new SurfaceScrollDetector(this);
        mScrollDetector.setTriggerScrollMinimumDistance(15f);
        mPinchZoomDetector = new PinchZoomDetector(this);

        continuousHoldDetector = new ContinuousHoldDetector(this);
        registerUpdateHandler(continuousHoldDetector);
        
        setOnSceneTouchListener(this);
        setTouchAreaBindingOnActionDownEnabled(true);
        isTouchOK = true;

		createBackground();
    	sprite = new Sprite(MainActivity.CAMERA_WIDTH / 2, MainActivity.CAMERA_HEIGHT / 2, resourcesManager.sprite_region, vbom);
    	attachChild(sprite);
	}

	@Override
	public void onBackKeyPressed(){
		System.exit(0);
	}

	@Override
	public SceneType getSceneType(){
		return SceneType.SCENE_GAME;
	}
	
	@Override
	public void disposeScene(){
		background.detachSelf();
		background.dispose();
		sprite.detachSelf();
		sprite.dispose();
		this.detachSelf();
		this.dispose();
	}

    @Override
    public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent){
        if(isTouchOK){
            if(isHold){
                isHold = false;
                continuousHoldDetector.reset();
            }
            continuousHoldDetector.onTouchEvent(pSceneTouchEvent);
            if(!isHold){
                mPinchZoomDetector.onTouchEvent(pSceneTouchEvent);
                
                if(mPinchZoomDetector.isZooming()){
                    mScrollDetector.setEnabled(false);
                    isMove = true;
                }
                else{
                    if (pSceneTouchEvent.isActionMove()){
                        mScrollDetector.setEnabled(true);
                    }
                    else if (pSceneTouchEvent.isActionUp()){
                        if(!isMove){
                            isTouchOK = false;
                            final float zoomValue = 1.0f;
                            camera.setZoomFactor(zoomValue);
                            isTouchOK = true;
                        }
                        isMove = false;
                    }
                    else if (pSceneTouchEvent.isActionOutside()){
                        isMove = false;
                    }
                    mScrollDetector.onTouchEvent(pSceneTouchEvent);
                }
            }
        }
        return true;
    }
 
    @Override
    public void onScrollStarted(final ScrollDetector pScollDetector, final int pPointerID, final float pDistanceX, final float pDistanceY) {
        final float zoomFactor = camera.getZoomFactor();
        camera.offsetCenter(-pDistanceX / zoomFactor, pDistanceY / zoomFactor);
        isMove = true;
    }
 
    @Override
    public void onScroll(final ScrollDetector pScollDetector, final int pPointerID, final float pDistanceX, final float pDistanceY) {
        final float zoomFactor = camera.getZoomFactor();
        camera.offsetCenter(-pDistanceX / zoomFactor, pDistanceY / zoomFactor);
    }
     
    @Override
    public void onScrollFinished(final ScrollDetector pScollDetector, final int pPointerID, final float pDistanceX, final float pDistanceY) {
        final float zoomFactor = camera.getZoomFactor();
        camera.offsetCenter(-pDistanceX / zoomFactor, pDistanceY / zoomFactor);
    }
 
    @Override
    public void onPinchZoomStarted(final PinchZoomDetector pPinchZoomDetector, final TouchEvent pTouchEvent) {
        mPinchZoomStartedCameraZoomFactor = camera.getZoomFactor();
    }
 
    @Override
    public void onPinchZoom(final PinchZoomDetector pPinchZoomDetector, final TouchEvent pTouchEvent, final float pZoomFactor) {
        float zoomValue = mPinchZoomStartedCameraZoomFactor * pZoomFactor;
        if(zoomValue < 1.1) zoomValue = MIN_ZOOM;
        else if(zoomValue > MAX_ZOOM) zoomValue = MAX_ZOOM;
        camera.setZoomFactor(zoomValue);
    }
 
    @Override
    public void onPinchZoomFinished(final PinchZoomDetector pPinchZoomDetector, final TouchEvent pTouchEvent, final float pZoomFactor) {
        float zoomValue = mPinchZoomStartedCameraZoomFactor * pZoomFactor;
        if(zoomValue < 1.1) zoomValue = MIN_ZOOM;
        else if(zoomValue > MAX_ZOOM) zoomValue = MAX_ZOOM;
        camera.setZoomFactor(zoomValue);
    }
    
    @Override
    public void onHoldStarted(HoldDetector pHoldDetector, int pPointerID, float pHoldX, float pHoldY){
        isTouchOK = false;
    }
 
    @Override
    public void onHold(HoldDetector pHoldDetector, long pHoldTimeMilliseconds, int pPointerID, float pHoldX, float pHoldY){
        if(!isHold && pHoldTimeMilliseconds > holdTime){
            isHold = true;
            rotation += 90;
            if(rotation == 360) rotation = 0; 
            sprite.setRotation(rotation);
            isTouchOK = true;
        }
    }
         
    @Override
    public void onHoldFinished(HoldDetector pHoldDetector, long pHoldTimeMilliseconds, int pPointerID, float pHoldX, float pHoldY){
    }
}

 ロングタップ制御外にピンチズームを入れただけなのに複雑になりましたね。
 この他にも、各クラスの BoundCamera を ZoomCamera に直す必要があります。
 MainActivity クラスの onCreateEngineOptions() に camera.setBounds(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT); を加えるのもお忘れなく。
 
 Sprite をピンチで3倍までズームアップし、タップで等倍に戻します。ロングタップで90度右回転。
device-2014-06-10-223552 device-2014-06-10-223727
 タップとロングタップの制御が甘いのはご愛嬌。
 
 
 そうそう、認証もらった。

アンドロイダー公認デベロッパー認証

 お約束のプロジェクトアーカイブは、こちら からどうぞ。zip ファイルであります。
 AndEngine のライブラリ参照は切ってあるので、適宜設定してください。
 
 これらのクラスは、イギリス在住の Matim さんのページ(19歳でエライねぇ)を参考にさせて(否、丸写しか・・)もらったものです。
 
 サンプルプログラムは、スプライト表示のみの機能しかないので、あっさりしたコードです。じっくり読んで追いかければ処理の流れはわかるかと思います。
 その上で、上記サイト等を参考にコードを追加していけば、簡単なゲーム位はすぐ出来るはずです。
 
 ぜひ、参考にしてみてください。
 
 
 
 
 
 これで、サンプルの紹介は終わりにしようと思ったのですが、(その1)で『タッチ制御のプログラムを抜書きしても「全体がわからないから使いどころがわからない」というご意見もあるかもしれないと思ったので、サンプルプロジェクトを作ってみました。』と書いておいて、タッチ制御を入れないのもなんだかな・・、上でも『簡単なゲーム位はすぐ出来るはずです。』とも書いているし・・、簡単にタッチ制御のコードを追加してみようかと思った次第。
 
 次回「AndEngineのサンプルプロジェクトにピンチズームとスクロールを実装する」です。タイトル長いな。