AndEngineでピンチズームとスクロールを実装する

 画面タッチの制御関係は度々書かざるを得ないから、前回のロングタップに続けて書き留めておきます。
 用いるは、AndEngine GLES2-AnchorCenter です。
 
 ピンチズームとスクロールは「Polygons Lights Out」と「Polygons Mine Sweeper」で実装済みなので、コードを抜書きします。
 MainActivity での Camera の設定です。

import org.andengine.engine.Engine;
import org.andengine.engine.LimitedFPSEngine;
import org.andengine.engine.camera.ZoomCamera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.andengine.ui.activity.BaseGameActivity;

public class MainActivity extends BaseGameActivity {
	private ZoomCamera camera;
	static final int CAMERA_WIDTH = 480;
	static final int CAMERA_HEIGHT = 800;
	
	@Override
	public Engine onCreateEngine(EngineOptions pEngineOptions){
		return new LimitedFPSEngine(pEngineOptions, 60);
	}
	
	@Override
	public EngineOptions onCreateEngineOptions(){
		camera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		camera.setBounds(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		camera.setBoundsEnabled(true);
		EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.PORTRAIT_FIXED, new FillResolutionPolicy(), this.camera);
		return engineOptions;
	}

 もちろん ZoomCamera を選択します。
で、Scene を継承したサブクラスで実装します。私の場合、最大3倍止まりの設定です。

import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.input.touch.TouchEvent;
import org.andengine.input.touch.detector.PinchZoomDetector;
import org.andengine.input.touch.detector.ScrollDetector;
import org.andengine.input.touch.detector.SurfaceScrollDetector;
import org.andengine.input.touch.detector.PinchZoomDetector.IPinchZoomDetectorListener;
import org.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;

public class GameScene extends Scene implements IOnSceneTouchListener, IScrollDetectorListener, IPinchZoomDetectorListener{
	final static float MIN_ZOOM = 1.0f;
	final static float MAX_ZOOM = 3.0f;
	private SurfaceScrollDetector mScrollDetector;
	private PinchZoomDetector mPinchZoomDetector;
	private float mPinchZoomStartedCameraZoomFactor;
	private float zoomValue = 1.0f;
	private boolean isTouchOK = true;
	private boolean isMove;
	
	GameScene(){
		setOnSceneTouchListener(this);
		setOnAreaTouchTraversalFrontToBack();
		mScrollDetector = new SurfaceScrollDetector(this);
		mScrollDetector.setTriggerScrollMinimumDistance(15f);
		mPinchZoomDetector = new PinchZoomDetector(this);
		setOnSceneTouchListener(this);
		setTouchAreaBindingOnActionDownEnabled(true);
		isTouchOK = true;
	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent){
		if(isTouchOK){
			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;
						// タップ時の処理
						// 処理が終わったら 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; // 1.1倍未満になったら原寸に戻す
		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);
	}

}

 写し漏れで、何かが足りないかもしれませんが、概ねこんな感じ。
 
 onScrollStarted() 内の isMove = true; は、 onSceneTouchEvent() のif (pSceneTouchEvent.isActionMove()){} 内に入れても動く気がするのですが、このロジックで現に動いているので試してはいません。
 
 毎度、同じような言いっぷりで申し訳ない。