package org.andengine.entity.modifier;
import org.andengine.entity.IEntity;
import org.andengine.util.modifier.IModifier;
import org.andengine.util.modifier.SequenceModifier;
import org.andengine.util.modifier.SequenceModifier.ISubSequenceModifierListener;
import org.andengine.util.modifier.ease.EaseLinear;
import org.andengine.util.modifier.ease.IEaseFunction;
import android.util.FloatMath;
/**
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @since 16:50:02 - 16.06.2010
*/
public class PathModifier extends EntityModifier {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private final SequenceModifier<IEntity> mSequenceModifier;
private IPathModifierListener mPathModifierListener;
private final Path mPath;
// ===========================================================
// Constructors
// ===========================================================
public PathModifier(final float pDuration, final Path pPath) {
this(pDuration, pPath, null, null, EaseLinear.getInstance());
}
public PathModifier(final float pDuration, final Path pPath, final IEaseFunction pEaseFunction) {
this(pDuration, pPath, null, null, pEaseFunction);
}
public PathModifier(final float pDuration, final Path pPath, final IEntityModifierListener pEntityModiferListener) {
this(pDuration, pPath, pEntityModiferListener, null, EaseLinear.getInstance());
}
public PathModifier(final float pDuration, final Path pPath, final IPathModifierListener pPathModifierListener) {
this(pDuration, pPath, null, pPathModifierListener, EaseLinear.getInstance());
}
public PathModifier(final float pDuration, final Path pPath, final IPathModifierListener pPathModifierListener, final IEaseFunction pEaseFunction) {
this(pDuration, pPath, null, pPathModifierListener, pEaseFunction);
}
public PathModifier(final float pDuration, final Path pPath, final IEntityModifierListener pEntityModiferListener, final IEaseFunction pEaseFunction) {
this(pDuration, pPath, pEntityModiferListener, null, pEaseFunction);
}
public PathModifier(final float pDuration, final Path pPath, final IEntityModifierListener pEntityModiferListener, final IPathModifierListener pPathModifierListener) throws IllegalArgumentException {
this(pDuration, pPath, pEntityModiferListener, pPathModifierListener, EaseLinear.getInstance());
}
public PathModifier(final float pDuration, final Path pPath, final IEntityModifierListener pEntityModiferListener, final IPathModifierListener pPathModifierListener, final IEaseFunction pEaseFunction) throws IllegalArgumentException {
super(pEntityModiferListener);
final int pathSize = pPath.getSize();
if (pathSize < 2) {
throw new IllegalArgumentException("Path needs at least 2 waypoints!");
}
this.mPath = pPath;
this.mPathModifierListener = pPathModifierListener;
final MoveModifier[] moveModifiers = new MoveModifier[pathSize - 1];
final float[] coordinatesX = pPath.getCoordinatesX();
final float[] coordinatesY = pPath.getCoordinatesY();
final float velocity = pPath.getLength() / pDuration;
final int modifierCount = moveModifiers.length;
for(int i = 0; i < modifierCount; i++) {
final float duration = pPath.getSegmentLength(i) / velocity;
moveModifiers[i] = new MoveModifier(duration, coordinatesX[i], coordinatesX[i + 1], coordinatesY[i], coordinatesY[i + 1], null, pEaseFunction);
}
/* Create a new SequenceModifier and register the listeners that
* call through to mEntityModifierListener and mPathModifierListener. */
this.mSequenceModifier = new SequenceModifier<IEntity>(
new ISubSequenceModifierListener<IEntity>() {
@Override
public void onSubSequenceStarted(final IModifier<IEntity> pModifier, final IEntity pEntity, final int pIndex) {
if(PathModifier.this.mPathModifierListener != null) {
PathModifier.this.mPathModifierListener.onPathWaypointStarted(PathModifier.this, pEntity, pIndex);
}
}
@Override
public void onSubSequenceFinished(final IModifier<IEntity> pEntityModifier, final IEntity pEntity, final int pIndex) {
if(PathModifier.this.mPathModifierListener != null) {
PathModifier.this.mPathModifierListener.onPathWaypointFinished(PathModifier.this, pEntity, pIndex);
}
}
},
new IEntityModifierListener() {
@Override
public void onModifierStarted(final IModifier<IEntity> pModifier, final IEntity pEntity) {
PathModifier.this.onModifierStarted(pEntity);
if(PathModifier.this.mPathModifierListener != null) {
PathModifier.this.mPathModifierListener.onPathStarted(PathModifier.this, pEntity);
}
}
@Override
public void onModifierFinished(final IModifier<IEntity> pEntityModifier, final IEntity pEntity) {
PathModifier.this.onModifierFinished(pEntity);
if(PathModifier.this.mPathModifierListener != null) {
PathModifier.this.mPathModifierListener.onPathFinished(PathModifier.this, pEntity);
}
}
},
moveModifiers
);
}
protected PathModifier(final PathModifier pPathModifier) throws DeepCopyNotSupportedException {
this.mPath = pPathModifier.mPath.deepCopy();
this.mSequenceModifier = pPathModifier.mSequenceModifier.deepCopy();
}
@Override
public PathModifier deepCopy() throws DeepCopyNotSupportedException {
return new PathModifier(this);
}
// ===========================================================
// Getter & Setter
// ===========================================================
public Path getPath() {
return this.mPath;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public boolean isFinished() {
return this.mSequenceModifier.isFinished();
}
@Override
public float getSecondsElapsed() {
return this.mSequenceModifier.getSecondsElapsed();
}
@Override
public float getDuration() {
return this.mSequenceModifier.getDuration();
}
public IPathModifierListener getPathModifierListener() {
return this.mPathModifierListener;
}
public void setPathModifierListener(final IPathModifierListener pPathModifierListener) {
this.mPathModifierListener = pPathModifierListener;
}
@Override
public void reset() {
this.mSequenceModifier.reset();
}
@Override
public float onUpdate(final float pSecondsElapsed, final IEntity pEntity) {
return this.mSequenceModifier.onUpdate(pSecondsElapsed, pEntity);
}
// ===========================================================
// Methods
// ===========================================================
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public static interface IPathModifierListener {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
public void onPathStarted(final PathModifier pPathModifier, final IEntity pEntity);
public void onPathWaypointStarted(final PathModifier pPathModifier, final IEntity pEntity, final int pWaypointIndex);
public void onPathWaypointFinished(final PathModifier pPathModifier, final IEntity pEntity, final int pWaypointIndex);
public void onPathFinished(final PathModifier pPathModifier, final IEntity pEntity);
}
public static class Path {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private final float[] mXs;
private final float[] mYs;
private int mIndex;
private boolean mLengthChanged = false;
private float mLength;
// ===========================================================
// Constructors
// ===========================================================
public Path(final int pLength) {
this.mXs = new float[pLength];
this.mYs = new float[pLength];
this.mIndex = 0;
this.mLengthChanged = false;
}
public Path(final float[] pCoordinatesX, final float[] pCoordinatesY) throws IllegalArgumentException {
if (pCoordinatesX.length != pCoordinatesY.length) {
throw new IllegalArgumentException("Coordinate-Arrays must have the same length.");
}
this.mXs = pCoordinatesX;
this.mYs = pCoordinatesY;
this.mIndex = pCoordinatesX.length;
this.mLengthChanged = true;
}
public Path(final Path pPath) {
final int size = pPath.getSize();
this.mXs = new float[size];
this.mYs = new float[size];
System.arraycopy(pPath.mXs, 0, this.mXs, 0, size);
System.arraycopy(pPath.mYs, 0, this.mYs, 0, size);
this.mIndex = pPath.mIndex;
this.mLengthChanged = pPath.mLengthChanged;
this.mLength = pPath.mLength;
}
public Path deepCopy() {
return new Path(this);
}
// ===========================================================
// Getter & Setter
// ===========================================================
public Path to(final float pX, final float pY) {
this.mXs[this.mIndex] = pX;
this.mYs[this.mIndex] = pY;
this.mIndex++;
this.mLengthChanged = true;
return this;
}
public float[] getCoordinatesX() {
return this.mXs;
}
public float[] getCoordinatesY() {
return this.mYs;
}
public int getSize() {
return this.mXs.length;
}
public float getLength() {
if(this.mLengthChanged) {
this.updateLength();
}
return this.mLength;
}
public float getSegmentLength(final int pSegmentIndex) {
final float[] coordinatesX = this.mXs;
final float[] coordinatesY = this.mYs;
final int nextSegmentIndex = pSegmentIndex + 1;
final float dx = coordinatesX[pSegmentIndex] - coordinatesX[nextSegmentIndex];
final float dy = coordinatesY[pSegmentIndex] - coordinatesY[nextSegmentIndex];
return FloatMath.sqrt(dx * dx + dy * dy);
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
private void updateLength() {
float length = 0.0f;
for(int i = this.mIndex - 2; i >= 0; i--) {
length += this.getSegmentLength(i);
}
this.mLength = length;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}
}