package org.andengine.entity.scene;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.handler.runnable.RunnableHandler;
import org.andengine.entity.Entity;
import org.andengine.entity.IEntity;
import org.andengine.entity.scene.ITouchArea.ITouchAreaMatcher;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.scene.background.IBackground;
import org.andengine.entity.shape.IShape;
import org.andengine.entity.shape.Shape;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.util.GLState;
import org.andengine.util.Constants;
import org.andengine.util.adt.list.SmartList;
import org.andengine.util.color.Color;
import android.util.SparseArray;
/**
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @since 12:47:39 - 08.03.2010
*/
public class Scene extends Entity {
// ===========================================================
// Constants
// ===========================================================
private static final int TOUCHAREAS_CAPACITY_DEFAULT = 4;
// ===========================================================
// Fields
// ===========================================================
private float mSecondsElapsedTotal;
protected Scene mParentScene;
protected Scene mChildScene;
private boolean mChildSceneModalDraw;
private boolean mChildSceneModalUpdate;
private boolean mChildSceneModalTouch;
protected SmartList<ITouchArea> mTouchAreas = new SmartList<ITouchArea>(Scene.TOUCHAREAS_CAPACITY_DEFAULT);
private final RunnableHandler mRunnableHandler = new RunnableHandler();
private IOnSceneTouchListener mOnSceneTouchListener;
private IOnAreaTouchListener mOnAreaTouchListener;
private IBackground mBackground = new Background(Color.BLACK);
private boolean mBackgroundEnabled = true;
private boolean mOnAreaTouchTraversalBackToFront = true;
private boolean mTouchAreaBindingOnActionDownEnabled = false;
private boolean mTouchAreaBindingOnActionMoveEnabled = false;
private final SparseArray<ITouchArea> mTouchAreaBindings = new SparseArray<ITouchArea>();
private boolean mOnSceneTouchListenerBindingOnActionDownEnabled = false;
private final SparseArray<IOnSceneTouchListener> mOnSceneTouchListenerBindings = new SparseArray<IOnSceneTouchListener>();
// ===========================================================
// Constructors
// ===========================================================
public Scene() {
}
@Deprecated
public Scene(final int pChildCount) {
for(int i = 0; i < pChildCount; i++) {
this.attachChild(new Entity());
}
}
// ===========================================================
// Getter & Setter
// ===========================================================
public float getSecondsElapsedTotal() {
return this.mSecondsElapsedTotal;
}
public IBackground getBackground() {
return this.mBackground;
}
public void setBackground(final IBackground pBackground) {
this.mBackground = pBackground;
}
public boolean isBackgroundEnabled() {
return this.mBackgroundEnabled;
}
public void setBackgroundEnabled(final boolean pEnabled) {
this.mBackgroundEnabled = pEnabled;
}
public void setOnSceneTouchListener(final IOnSceneTouchListener pOnSceneTouchListener) {
this.mOnSceneTouchListener = pOnSceneTouchListener;
}
public IOnSceneTouchListener getOnSceneTouchListener() {
return this.mOnSceneTouchListener;
}
public boolean hasOnSceneTouchListener() {
return this.mOnSceneTouchListener != null;
}
public void setOnAreaTouchListener(final IOnAreaTouchListener pOnAreaTouchListener) {
this.mOnAreaTouchListener = pOnAreaTouchListener;
}
public IOnAreaTouchListener getOnAreaTouchListener() {
return this.mOnAreaTouchListener;
}
public boolean hasOnAreaTouchListener() {
return this.mOnAreaTouchListener != null;
}
private void setParentScene(final Scene pParentScene) {
this.mParentScene = pParentScene;
}
public boolean hasChildScene() {
return this.mChildScene != null;
}
public Scene getChildScene() {
return this.mChildScene;
}
public void setChildSceneModal(final Scene pChildScene) {
this.setChildScene(pChildScene, true, true, true);
}
public void setChildScene(final Scene pChildScene) {
this.setChildScene(pChildScene, false, false, false);
}
public void setChildScene(final Scene pChildScene, final boolean pModalDraw, final boolean pModalUpdate, final boolean pModalTouch) {
pChildScene.setParentScene(this);
this.mChildScene = pChildScene;
this.mChildSceneModalDraw = pModalDraw;
this.mChildSceneModalUpdate = pModalUpdate;
this.mChildSceneModalTouch = pModalTouch;
}
public void clearChildScene() {
this.mChildScene = null;
}
public void setOnAreaTouchTraversalBackToFront() {
this.mOnAreaTouchTraversalBackToFront = true;
}
public void setOnAreaTouchTraversalFrontToBack() {
this.mOnAreaTouchTraversalBackToFront = false;
}
public boolean isTouchAreaBindingOnActionDownEnabled() {
return this.mTouchAreaBindingOnActionDownEnabled;
}
public boolean isTouchAreaBindingOnActionMoveEnabled() {
return this.mTouchAreaBindingOnActionMoveEnabled;
}
/**
* Enable or disable the binding of TouchAreas to PointerIDs (fingers).
* When enabled: TouchAreas get bound to a PointerID (finger) when returning true in
* {@link IShape#onAreaTouched(TouchEvent, float, float)} or
* {@link IOnAreaTouchListener#onAreaTouched(TouchEvent, ITouchArea, float, float)}
* with {@link TouchEvent#ACTION_DOWN}, they will receive all subsequent {@link TouchEvent}s
* that are made with the same PointerID (finger)
* <b>even if the {@link TouchEvent} is outside of the actual {@link ITouchArea}</b>!
*
* @param pTouchAreaBindingOnActionDownEnabled
*/
public void setTouchAreaBindingOnActionDownEnabled(final boolean pTouchAreaBindingOnActionDownEnabled) {
if(this.mTouchAreaBindingOnActionDownEnabled && !pTouchAreaBindingOnActionDownEnabled) {
this.mTouchAreaBindings.clear();
}
this.mTouchAreaBindingOnActionDownEnabled = pTouchAreaBindingOnActionDownEnabled;
}
/**
* Enable or disable the binding of TouchAreas to PointerIDs (fingers).
* When enabled: TouchAreas get bound to a PointerID (finger) when returning true in
* {@link IShape#onAreaTouched(TouchEvent, float, float)} or
* {@link IOnAreaTouchListener#onAreaTouched(TouchEvent, ITouchArea, float, float)}
* with {@link TouchEvent#ACTION_MOVE}, they will receive all subsequent {@link TouchEvent}s
* that are made with the same PointerID (finger)
* <b>even if the {@link TouchEvent} is outside of the actual {@link ITouchArea}</b>!
*
* @param pTouchAreaBindingOnActionMoveEnabled
*/
public void setTouchAreaBindingOnActionMoveEnabled(final boolean pTouchAreaBindingOnActionMoveEnabled) {
if(this.mTouchAreaBindingOnActionMoveEnabled && !pTouchAreaBindingOnActionMoveEnabled) {
this.mTouchAreaBindings.clear();
}
this.mTouchAreaBindingOnActionMoveEnabled = pTouchAreaBindingOnActionMoveEnabled;
}
public boolean isOnSceneTouchListenerBindingOnActionDownEnabled() {
return this.mOnSceneTouchListenerBindingOnActionDownEnabled;
}
/**
* Enable or disable the binding of TouchAreas to PointerIDs (fingers).
* When enabled: The OnSceneTouchListener gets bound to a PointerID (finger) when returning true in
* {@link Shape#onAreaTouched(TouchEvent, float, float)} or
* {@link IOnAreaTouchListener#onAreaTouched(TouchEvent, ITouchArea, float, float)}
* with {@link TouchEvent#ACTION_DOWN}, it will receive all subsequent {@link TouchEvent}s
* that are made with the same PointerID (finger)
* <b>even if the {@link TouchEvent} is would belong to an overlaying {@link ITouchArea}</b>!
*
* @param pOnSceneTouchListenerBindingOnActionDownEnabled
*/
public void setOnSceneTouchListenerBindingOnActionDownEnabled(final boolean pOnSceneTouchListenerBindingOnActionDownEnabled) {
if(this.mOnSceneTouchListenerBindingOnActionDownEnabled && !pOnSceneTouchListenerBindingOnActionDownEnabled) {
this.mOnSceneTouchListenerBindings.clear();
}
this.mOnSceneTouchListenerBindingOnActionDownEnabled = pOnSceneTouchListenerBindingOnActionDownEnabled;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
protected void onManagedDraw(final GLState pGLState, final Camera pCamera) {
final Scene childScene = this.mChildScene;
if(childScene == null || !this.mChildSceneModalDraw) {
if(this.mBackgroundEnabled) {
pGLState.pushProjectionGLMatrix();
pCamera.onApplySceneBackgroundMatrix(pGLState);
pGLState.loadModelViewGLMatrixIdentity();
this.mBackground.onDraw(pGLState, pCamera);
pGLState.popProjectionGLMatrix();
}
{
pGLState.pushProjectionGLMatrix();
this.onApplyMatrix(pGLState, pCamera);
pGLState.loadModelViewGLMatrixIdentity();
super.onManagedDraw(pGLState, pCamera);
pGLState.popProjectionGLMatrix();
}
}
if(childScene != null) {
childScene.onDraw(pGLState, pCamera);
}
}
protected void onApplyMatrix(final GLState pGLState, final Camera pCamera) {
pCamera.onApplySceneMatrix(pGLState);
}
@Override
protected void onManagedUpdate(final float pSecondsElapsed) {
this.mSecondsElapsedTotal += pSecondsElapsed;
this.mRunnableHandler.onUpdate(pSecondsElapsed);
final Scene childScene = this.mChildScene;
if(childScene == null || !this.mChildSceneModalUpdate) {
this.mBackground.onUpdate(pSecondsElapsed);
super.onManagedUpdate(pSecondsElapsed);
}
if(childScene != null) {
childScene.onUpdate(pSecondsElapsed);
}
}
public boolean onSceneTouchEvent(final TouchEvent pSceneTouchEvent) {
final int action = pSceneTouchEvent.getAction();
final boolean isActionDown = pSceneTouchEvent.isActionDown();
final boolean isActionMove = pSceneTouchEvent.isActionMove();
if(!isActionDown) {
if(this.mOnSceneTouchListenerBindingOnActionDownEnabled) {
final IOnSceneTouchListener boundOnSceneTouchListener = this.mOnSceneTouchListenerBindings.get(pSceneTouchEvent.getPointerID());
if (boundOnSceneTouchListener != null) {
/* Check if boundTouchArea needs to be removed. */
switch(action) {
case TouchEvent.ACTION_UP:
case TouchEvent.ACTION_CANCEL:
this.mOnSceneTouchListenerBindings.remove(pSceneTouchEvent.getPointerID());
}
final Boolean handled = this.mOnSceneTouchListener.onSceneTouchEvent(this, pSceneTouchEvent);
if(handled != null && handled) {
return true;
}
}
}
if(this.mTouchAreaBindingOnActionDownEnabled) {
final SparseArray<ITouchArea> touchAreaBindings = this.mTouchAreaBindings;
final ITouchArea boundTouchArea = touchAreaBindings.get(pSceneTouchEvent.getPointerID());
/* In the case a ITouchArea has been bound to this PointerID,
* we'll pass this this TouchEvent to the same ITouchArea. */
if(boundTouchArea != null) {
final float sceneTouchEventX = pSceneTouchEvent.getX();
final float sceneTouchEventY = pSceneTouchEvent.getY();
/* Check if boundTouchArea needs to be removed. */
switch(action) {
case TouchEvent.ACTION_UP:
case TouchEvent.ACTION_CANCEL:
touchAreaBindings.remove(pSceneTouchEvent.getPointerID());
}
final Boolean handled = this.onAreaTouchEvent(pSceneTouchEvent, sceneTouchEventX, sceneTouchEventY, boundTouchArea);
if(handled != null && handled) {
return true;
}
}
}
}
final Scene childScene = this.mChildScene;
if(childScene != null) {
final boolean handledByChild = this.onChildSceneTouchEvent(pSceneTouchEvent);
if(handledByChild) {
return true;
} else if(this.mChildSceneModalTouch) {
return false;
}
}
final float sceneTouchEventX = pSceneTouchEvent.getX();
final float sceneTouchEventY = pSceneTouchEvent.getY();
final SmartList<ITouchArea> touchAreas = this.mTouchAreas;
if(touchAreas != null) {
final int touchAreaCount = touchAreas.size();
if(touchAreaCount > 0) {
if(this.mOnAreaTouchTraversalBackToFront) { /* Back to Front. */
for(int i = 0; i < touchAreaCount; i++) {
final ITouchArea touchArea = touchAreas.get(i);
if(touchArea.contains(sceneTouchEventX, sceneTouchEventY)) {
final Boolean handled = this.onAreaTouchEvent(pSceneTouchEvent, sceneTouchEventX, sceneTouchEventY, touchArea);
if(handled != null && handled) {
/* If binding of ITouchAreas is enabled and this is an ACTION_DOWN event,
* bind this ITouchArea to the PointerID. */
if((this.mTouchAreaBindingOnActionDownEnabled && isActionDown) || (this.mTouchAreaBindingOnActionMoveEnabled && isActionMove)) {
this.mTouchAreaBindings.put(pSceneTouchEvent.getPointerID(), touchArea);
}
return true;
}
}
}
} else { /* Front to back. */
for(int i = touchAreaCount - 1; i >= 0; i--) {
final ITouchArea touchArea = touchAreas.get(i);
if(touchArea.contains(sceneTouchEventX, sceneTouchEventY)) {
final Boolean handled = this.onAreaTouchEvent(pSceneTouchEvent, sceneTouchEventX, sceneTouchEventY, touchArea);
if(handled != null && handled) {
/* If binding of ITouchAreas is enabled and this is an ACTION_DOWN event,
* bind this ITouchArea to the PointerID. */
if((this.mTouchAreaBindingOnActionDownEnabled && isActionDown) || (this.mTouchAreaBindingOnActionMoveEnabled && isActionMove)) {
this.mTouchAreaBindings.put(pSceneTouchEvent.getPointerID(), touchArea);
}
return true;
}
}
}
}
}
}
/* If no area was touched, the Scene itself was touched as a fallback. */
if(this.mOnSceneTouchListener != null){
final Boolean handled = this.mOnSceneTouchListener.onSceneTouchEvent(this, pSceneTouchEvent);
if(handled != null && handled) {
/* If binding of ITouchAreas is enabled and this is an ACTION_DOWN event,
* bind the active OnSceneTouchListener to the PointerID. */
if(this.mOnSceneTouchListenerBindingOnActionDownEnabled && isActionDown) {
this.mOnSceneTouchListenerBindings.put(pSceneTouchEvent.getPointerID(), this.mOnSceneTouchListener);
}
return true;
} else {
return false;
}
} else {
return false;
}
}
private Boolean onAreaTouchEvent(final TouchEvent pSceneTouchEvent, final float sceneTouchEventX, final float sceneTouchEventY, final ITouchArea touchArea) {
final float[] touchAreaLocalCoordinates = touchArea.convertSceneToLocalCoordinates(sceneTouchEventX, sceneTouchEventY);
final float touchAreaLocalX = touchAreaLocalCoordinates[Constants.VERTEX_INDEX_X];
final float touchAreaLocalY = touchAreaLocalCoordinates[Constants.VERTEX_INDEX_Y];
final boolean handledSelf = touchArea.onAreaTouched(pSceneTouchEvent, touchAreaLocalX, touchAreaLocalY);
if(handledSelf) {
return Boolean.TRUE;
} else if(this.mOnAreaTouchListener != null) {
return this.mOnAreaTouchListener.onAreaTouched(pSceneTouchEvent, touchArea, touchAreaLocalX, touchAreaLocalY);
} else {
return null;
}
}
protected boolean onChildSceneTouchEvent(final TouchEvent pSceneTouchEvent) {
return this.mChildScene.onSceneTouchEvent(pSceneTouchEvent);
}
@Override
public void reset() {
super.reset();
this.clearChildScene();
}
@Override
public void setParent(final IEntity pEntity) {
// super.setParent(pEntity);
}
// ===========================================================
// Methods
// ===========================================================
public void postRunnable(final Runnable pRunnable) {
this.mRunnableHandler.postRunnable(pRunnable);
}
public void registerTouchArea(final ITouchArea pTouchArea) {
this.mTouchAreas.add(pTouchArea);
}
public boolean unregisterTouchArea(final ITouchArea pTouchArea) {
return this.mTouchAreas.remove(pTouchArea);
}
public boolean unregisterTouchAreas(final ITouchAreaMatcher pTouchAreaMatcher) {
return this.mTouchAreas.removeAll(pTouchAreaMatcher);
}
public void clearTouchAreas() {
this.mTouchAreas.clear();
}
public SmartList<ITouchArea> getTouchAreas() {
return this.mTouchAreas;
}
public void back() {
this.clearChildScene();
if(this.mParentScene != null) {
this.mParentScene.clearChildScene();
this.mParentScene = null;
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}