package org.anddev.andengine.entity.sprite; import java.util.Arrays; import org.anddev.andengine.opengl.texture.region.TiledTextureRegion; import org.anddev.andengine.opengl.vertex.RectangleVertexBuffer; import org.anddev.andengine.util.MathUtils; import org.anddev.andengine.util.constants.TimeConstants; public class AnimatedSprite extends TiledSprite implements TimeConstants { // =========================================================== // Constants // =========================================================== private static final int LOOP_CONTINUOUS = -1; // =========================================================== // Fields // =========================================================== private boolean mAnimationRunning; private long mAnimationProgress; private long mAnimationDuration; private long[] mFrameEndsInNanoseconds; private int mFirstTileIndex; private int mInitialLoopCount; private int mLoopCount; private IAnimationListener mAnimationListener; private int mFrameCount; private int[] mFrames; // =========================================================== // Constructors // =========================================================== public AnimatedSprite(final float pX, final float pY, final TiledTextureRegion pTiledTextureRegion) { super(pX, pY, pTiledTextureRegion); } public AnimatedSprite(final float pX, final float pY, final float pTileWidth, final float pTileHeight, final TiledTextureRegion pTiledTextureRegion) { super(pX, pY, pTileWidth, pTileHeight, pTiledTextureRegion); } public AnimatedSprite(final float pX, final float pY, final TiledTextureRegion pTiledTextureRegion, final RectangleVertexBuffer pRectangleVertexBuffer) { super(pX, pY, pTiledTextureRegion, pRectangleVertexBuffer); } public AnimatedSprite(final float pX, final float pY, final float pTileWidth, final float pTileHeight, final TiledTextureRegion pTiledTextureRegion, final RectangleVertexBuffer pRectangleVertexBuffer) { super(pX, pY, pTileWidth, pTileHeight, pTiledTextureRegion, pRectangleVertexBuffer); } // =========================================================== // Getter & Setter // =========================================================== public boolean isAnimationRunning() { return this.mAnimationRunning; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override protected void onManagedUpdate(final float pSecondsElapsed) { super.onManagedUpdate(pSecondsElapsed); if(this.mAnimationRunning) { final long nanoSecondsElapsed = (long) (pSecondsElapsed * TimeConstants.NANOSECONDSPERSECOND); this.mAnimationProgress += nanoSecondsElapsed; if(this.mAnimationProgress > this.mAnimationDuration) { this.mAnimationProgress %= this.mAnimationDuration; if(this.mInitialLoopCount != AnimatedSprite.LOOP_CONTINUOUS) { this.mLoopCount--; } } if(this.mInitialLoopCount == AnimatedSprite.LOOP_CONTINUOUS || this.mLoopCount >= 0) { final int currentFrameIndex = this.calculateCurrentFrameIndex(); if(this.mFrames == null) { this.setCurrentTileIndex(this.mFirstTileIndex + currentFrameIndex); } else { this.setCurrentTileIndex(this.mFrames[currentFrameIndex]); } } else { this.mAnimationRunning = false; if(this.mAnimationListener != null) { this.mAnimationListener.onAnimationEnd(this); } } } } // =========================================================== // Methods // =========================================================== public void stopAnimation() { this.mAnimationRunning = false; } public void stopAnimation(final int pTileIndex) { this.mAnimationRunning = false; this.setCurrentTileIndex(pTileIndex); } private int calculateCurrentFrameIndex() { final long animationProgress = this.mAnimationProgress; final long[] frameEnds = this.mFrameEndsInNanoseconds; final int frameCount = this.mFrameCount; for(int i = 0; i < frameCount; i++) { if(frameEnds[i] > animationProgress) { return i; } } return frameCount - 1; } public AnimatedSprite animate(final long pFrameDurationEach) { return this.animate(pFrameDurationEach, true); } public AnimatedSprite animate(final long pFrameDurationEach, final boolean pLoop) { return this.animate(pFrameDurationEach, (pLoop) ? AnimatedSprite.LOOP_CONTINUOUS : 0, null); } public AnimatedSprite animate(final long pFrameDurationEach, final int pLoopCount) { return this.animate(pFrameDurationEach, pLoopCount, null); } public AnimatedSprite animate(final long pFrameDurationEach, final boolean pLoop, final IAnimationListener pAnimationListener) { return this.animate(pFrameDurationEach, (pLoop) ? AnimatedSprite.LOOP_CONTINUOUS : 0, pAnimationListener); } public AnimatedSprite animate(final long pFrameDurationEach, final int pLoopCount, final IAnimationListener pAnimationListener) { final long[] frameDurations = new long[this.getTextureRegion().getTileCount()]; Arrays.fill(frameDurations, pFrameDurationEach); return this.animate(frameDurations, pLoopCount, pAnimationListener); } public AnimatedSprite animate(final long[] pFrameDurations) { return this.animate(pFrameDurations, true); } public AnimatedSprite animate(final long[] pFrameDurations, final boolean pLoop) { return this.animate(pFrameDurations, (pLoop) ? AnimatedSprite.LOOP_CONTINUOUS : 0, null); } public AnimatedSprite animate(final long[] pFrameDurations, final int pLoopCount) { return this.animate(pFrameDurations, pLoopCount, null); } public AnimatedSprite animate(final long[] pFrameDurations, final boolean pLoop, final IAnimationListener pAnimationListener) { return this.animate(pFrameDurations, (pLoop) ? AnimatedSprite.LOOP_CONTINUOUS : 0, pAnimationListener); } public AnimatedSprite animate(final long[] pFrameDurations, final int pLoopCount, final IAnimationListener pAnimationListener) { return this.animate(pFrameDurations, 0, this.getTextureRegion().getTileCount() - 1, pLoopCount, pAnimationListener); } public AnimatedSprite animate(final long[] pFrameDurations, final int pFirstTileIndex, final int pLastTileIndex, final boolean pLoop) { return this.animate(pFrameDurations, pFirstTileIndex, pLastTileIndex, (pLoop) ? AnimatedSprite.LOOP_CONTINUOUS : 0, null); } public AnimatedSprite animate(final long[] pFrameDurations, final int pFirstTileIndex, final int pLastTileIndex, final int pLoopCount) { return this.animate(pFrameDurations, pFirstTileIndex, pLastTileIndex, pLoopCount, null); } public AnimatedSprite animate(final long[] pFrameDurations, final int[] pFrames, final int pLoopCount) { return this.animate(pFrameDurations, pFrames, pLoopCount, null); } /** * Animate specifics frames * * @param pFrameDurations must have the same length as pFrames. * @param pFrames indices of the frames to animate. * @param pLoopCount * @param pAnimationListener */ public AnimatedSprite animate(final long[] pFrameDurations, final int[] pFrames, final int pLoopCount, final IAnimationListener pAnimationListener) { final int frameCount = pFrames.length; if(pFrameDurations.length != frameCount) { throw new IllegalArgumentException("pFrameDurations must have the same length as pFrames."); } return this.init(pFrameDurations, frameCount, pFrames, 0, pLoopCount, pAnimationListener); } /** * @param pFrameDurations * must have the same length as pFirstTileIndex to * pLastTileIndex. * @param pFirstTileIndex * @param pLastTileIndex * @param pLoopCount * @param pAnimationListener */ public AnimatedSprite animate(final long[] pFrameDurations, final int pFirstTileIndex, final int pLastTileIndex, final int pLoopCount, final IAnimationListener pAnimationListener) { if(pLastTileIndex - pFirstTileIndex < 1) { throw new IllegalArgumentException("An animation needs at least two tiles to animate between."); } final int frameCount = (pLastTileIndex - pFirstTileIndex) + 1; if(pFrameDurations.length != frameCount) { throw new IllegalArgumentException("pFrameDurations must have the same length as pFirstTileIndex to pLastTileIndex."); } return this.init(pFrameDurations, frameCount, null, pFirstTileIndex, pLoopCount, pAnimationListener); } private AnimatedSprite init(final long[] pFrameDurations, final int frameCount, final int[] pFrames, final int pFirstTileIndex, final int pLoopCount, final IAnimationListener pAnimationListener) { this.mFrameCount = frameCount; this.mAnimationListener = pAnimationListener; this.mInitialLoopCount = pLoopCount; this.mLoopCount = pLoopCount; this.mFrames = pFrames; this.mFirstTileIndex = pFirstTileIndex; if(this.mFrameEndsInNanoseconds == null || this.mFrameCount > this.mFrameEndsInNanoseconds.length) { this.mFrameEndsInNanoseconds = new long[this.mFrameCount]; } final long[] frameEndsInNanoseconds = this.mFrameEndsInNanoseconds; MathUtils.arraySumInto(pFrameDurations, frameEndsInNanoseconds, TimeConstants.NANOSECONDSPERMILLISECOND); final long lastFrameEnd = frameEndsInNanoseconds[this.mFrameCount - 1]; this.mAnimationDuration = lastFrameEnd; this.mAnimationProgress = 0; this.mAnimationRunning = true; return this; } // =========================================================== // Inner and Anonymous Classes // =========================================================== public static interface IAnimationListener { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== public void onAnimationEnd(final AnimatedSprite pAnimatedSprite); } }