package okj.easy.graphics.graphics2d; import org.ege.utils.SpriteBackend; import org.ege.utils.Updater; import org.ege.utils.exception.EasyGEngineRuntimeException; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Circle; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.utils.Animator; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.FloatArray; /** * SpriteCAA : <b>ASYNCHRONIZE COMPOSITE ANIMATE Sprite</b> * * @author Ngo Trong Trung */ public class Spriter implements SpriteBackend, Disposable, Animator { private final SpriteBackend[] mSpriteList; private final Scaler[] mScaler; // Config private final int[] mDrawable; private int drawableSize = 0; private final int[] mRunnable; private int runnbaleSize = 0; private final int[] mCollision; private int collisionSize = 0; // param private boolean RUN; private final int limit; private int size = 0; private SpriteBackend mOriginSprite; private float mOriginWidth; private float mOriginHeight; private final FloatArray rect = new FloatArray(4); // -------------------------------------------------- private int idx = 0; // -------------------------------------------------- private float x; private float y; private float w; private float h; private Array<Updater> mUpdater = new Array<Updater>(0); /** * Construct a default spriter with size 13 child sprite */ public Spriter() { this(13); } /** * Construct a spriter with given sprite limit ( should override this * method) * * @param limit */ protected Spriter(int limit) { this.limit = limit; mSpriteList = new SpriteBackend[limit]; mScaler = new Scaler[limit]; mDrawable = new int[limit]; mRunnable = new int[limit]; mCollision = new int[limit]; for (int i = 0; i < limit; i++) { mRunnable[i] = mDrawable[i] = -1; } } /******************************************************** * Layer manage ********************************************************/ private Scaler calculateScaler (int id, SpriteBackend sprite, float x, float y, float width, float height) { if (mScaler[id] == null) { mScaler[id] = new Scaler(); } final Scaler scale = mScaler[id]; scale.sprite = sprite; scale.xRatio = x / mOriginWidth; scale.yRatio = y / mOriginHeight; scale.widthRatio = width / mOriginWidth; scale.heightRatio = height / mOriginHeight; return scale; } public void bindOriginLayer (SpriteBackend sprite) { w = mOriginWidth = sprite.getWidth(); h = mOriginHeight = sprite.getHeight(); this.x = sprite.getX(); this.y = sprite.getY(); mOriginSprite = sprite; calculateScaler(0, sprite, 0, 0, w, h); mSpriteList[0] = sprite; if (size == 0) ++size; else refresh(); } public void bindLayer (float x, float y, float width, float height, SpriteBackend sprite) { if (size > limit) throw new EasyGEngineRuntimeException("Out bound of sprite limit"); if (size == 0) { w = mOriginWidth = sprite.getWidth(); h = mOriginHeight = sprite.getHeight(); this.x = sprite.getX(); this.y = sprite.getY(); mOriginSprite = sprite; calculateScaler(0, sprite, 0, 0, w, h); } else { final Scaler scale = calculateScaler(size, sprite, x, y, width, height); scale.apply(); } mSpriteList[size++] = sprite; } public Spriter bindLayer (float[] attributes, SpriteBackend... sprites) { if (attributes.length / 4 != sprites.length) throw new EasyGEngineRuntimeException( "Attributes length must be equal regions.length * 4"); int i = 0; for (SpriteBackend sprite : sprites) { idx = i * 4; bindLayer(attributes[idx++], attributes[idx++], attributes[idx++], attributes[idx++], sprite); ++i; } return this; } public void bindLayer (int id, float x, float y, float width, float height, SpriteBackend sprite) { if (size > limit) throw new EasyGEngineRuntimeException("Out bound of sprite limit"); if (id > size) return; else if (id == size) { bindLayer(x, y, width, height, sprite); return; } mSpriteList[id] = sprite; if (id == 0) { w = mOriginWidth = sprite.getWidth(); h = mOriginHeight = sprite.getHeight(); this.x = sprite.getX(); this.y = sprite.getY(); mOriginSprite = sprite; calculateScaler(0, sprite, 0, 0, w, h); refresh(); } else { final Scaler scale = calculateScaler(id, sprite, x, y, width, height); scale.apply(); } } public SpriteBackend getSprite (int id) { return mSpriteList[id]; } public int getLayerId (SpriteBackend sprite) { final SpriteBackend[] list = Spriter.this.mSpriteList; for (int i = 0; i < size; i++) if (list[i] == sprite) return i; return -1; } /******************************************************** * Color method ********************************************************/ public void setColor (float r, float g, float b, float a) { final SpriteBackend[] list = Spriter.this.mSpriteList; for (int i = 0; i < size; i++) list[i].setColor(r, g, b, a); } public void setColor (Color color) { final SpriteBackend[] list = Spriter.this.mSpriteList; for (int i = 0; i < size; i++) list[i].setColor(color); } public void setColor (Color[] color, int[] layer) { if (color.length != layer.length) throw new EasyGEngineRuntimeException("Color length must be the same with layer length"); final SpriteBackend[] list = Spriter.this.mSpriteList; int j = 0; for (int i : layer) { list[i].setColor(color[j]); j++; } } public void setColor (Color color, int[] layer) { if (layer.length > size) throw new EasyGEngineRuntimeException("Layer length must <= size"); final SpriteBackend[] list = Spriter.this.mSpriteList; for (int i : layer) list[i].setColor(color); } public void setColor (float r, float g, float b, float a, int[] layer) { if (layer.length > size) throw new EasyGEngineRuntimeException("Layer length must <= size"); final SpriteBackend[] list = Spriter.this.mSpriteList; for (int i : layer) list[i].setColor(r, g, b, a); } public Spriter setColor (Color color, int layer) { mSpriteList[layer].setColor(color); return this; } public Spriter setColor (float r, float g, float b, float a, int layer) { mSpriteList[layer].setColor(r, g, b, layer); return this; } /******************************************************** * Configuration ********************************************************/ @Override public void setBounds (float x, float y, float width, float height) { this.x = x; this.y = y; this.w = width; this.h = height; mOriginSprite.setBounds(x, y, width, height); refresh(); } @Override public void setSize (float width, float height) { this.w = width; this.h = height; mOriginSprite.setSize(width, height); refresh(); } @Override public void setPosition (float x, float y) { final float deltaX = x - this.x; final float deltaY = y - this.y; this.x = x; this.y = y; mOriginSprite.setPosition(x, y); for (int i = 1; i < size; i++) mSpriteList[i].translate(deltaX, deltaY); } public void setX (float x) { final float deltaX = x - this.x; this.x = x; mOriginSprite.setX(x); for (int i = 1; i < size; i++) mSpriteList[i].translateX(deltaX); } @Override public void setY (float y) { final float deltaY = y - this.y; this.y = y; mOriginSprite.setY(y); for (int i = 1; i < size; i++) mSpriteList[i].translateY(deltaY); } @Override public void translate (float xAmount, float yAmount) { this.x += xAmount; this.y += yAmount; mOriginSprite.translate(xAmount, yAmount); for (int i = 1; i < size; i++) mSpriteList[i].translate(xAmount, yAmount); } @Override public void translateX (float xAmount) { this.x += xAmount; mOriginSprite.translateX(xAmount); for (int i = 1; i < size; i++) mSpriteList[i].translateX(xAmount); } @Override public void translateY (float yAmount) { this.y += yAmount; mOriginSprite.translateY(yAmount); for (int i = 1; i < size; i++) mSpriteList[i].translateY(yAmount); } // ------------------------------------------------------ @Override public void setOrigin (float originX, float originY) { mOriginSprite.setOrigin(originX, originY); float newOriginX; float newOriginY; for (int i = 1; i < size; i++) { final SpriteBackend sprite = mSpriteList[i]; newOriginX = sprite.getX() - x; newOriginY = sprite.getY() - y; sprite.setOrigin(originX - newOriginX, originY - newOriginY); } } public void setOrigin (int layer, float originX, float originY) { mSpriteList[layer].setOrigin(originX, originY); } // ------------------------------------------------------ @Override public void setRotation (float degree) { mOriginSprite.setRotation(degree); for (int i = 1; i < size; i++) mSpriteList[i].setRotation(degree); } public Spriter setRotation (int layer, float degree) { mSpriteList[layer].setRotation(degree); return this; } // ------------------------------------------------------ @Override public void rotate (float degree) { mOriginSprite.rotate(degree); for (int i = 1; i < size; i++) mSpriteList[i].rotate(degree); } public void rotate (int layer, float degree) { mSpriteList[layer].rotate(degree); } // ------------------------------------------------------ @Override public void setScale (float scaleXY) { mOriginSprite.setScale(scaleXY); } @Override public void setScale (float scaleX, float scaleY) { mOriginSprite.setScale(scaleX, scaleY); } @Override public void scale (float amount) { mOriginSprite.scale(amount); } public void setScale (int layer, float scaleXY) { mSpriteList[layer].setScale(scaleXY); } public void setScale (int layer, float scaleX, float scaleY) { mSpriteList[layer].setScale(scaleX, scaleY); } public void scale (int layer, float amount) { mSpriteList[layer].scale(amount); } /******************************************************** * Getter ********************************************************/ @Override public float[] getVertices () { return mOriginSprite.getVertices(); } @Override public float getX () { return x; } public float getX (int layer) { return mSpriteList[layer].getX(); } @Override public float getCenterX () { return mOriginSprite.getCenterX(); } public float getCenterX (int layer) { return mSpriteList[layer].getCenterX(); } @Override public float getY () { return y; } public float getY (int layer) { return mSpriteList[layer].getY(); } @Override public float getCenterY () { return mOriginSprite.getCenterY(); } public float getCenterY (int layer) { return mSpriteList[layer].getCenterY(); } @Override public float getWidth () { return w; } public float getWidth (int layer) { return mSpriteList[layer].getWidth(); } @Override public float getHeight () { return h; } public float getHeight (int layer) { return mSpriteList[layer].getHeight(); } @Override public float getOriginX () { return mOriginSprite.getOriginX(); } public float getOriginX (int layer) { return mSpriteList[layer].getOriginX(); } @Override public float getOriginY () { return mOriginSprite.getOriginY(); } public float getOriginY (int layer) { return mSpriteList[layer].getOriginY(); } @Override public float getRotation () { return mOriginSprite.getRotation(); } public float getRotation (int layer) { return mSpriteList[layer].getRotation(); } @Override public float getScaleX () { return mOriginSprite.getScaleX(); } public float getScaleX (int layer) { return mSpriteList[layer].getScaleX(); } @Override public float getScaleY () { return mOriginSprite.getScaleY(); } public float getScaleY (int layer) { return mSpriteList[layer].getScaleY(); } public int getLimit () { return limit; } public int getSize () { return size; } /******************************************************** * ********************************************************/ @Override public Rectangle getBoundingRectangle () { final Rectangle origin = mOriginSprite.getBoundingRectangle(); for (int i = 0; i < collisionSize; i++) origin.merge(mSpriteList[mCollision[i]].getBoundingRectangle()); return origin; } @Override public float[] getBoundingFloatRect (float offset) { final FloatArray result = Spriter.this.rect; result.clear(); for (int i = 0; i < collisionSize; i++) { final float[] rect = mSpriteList[mCollision[i]].getBoundingFloatRect(offset); result.addAll(rect); } if (result.size != result.items.length) result.shrink(); return result.items; } @Override public Circle getBoundingCircle () { return null; } private boolean containCollision (int layer) { for (int i = 0; i < collisionSize; i++) if (mCollision[i] == layer) return true; return false; } public Spriter addCollisionLayer (int layer) { if (collisionSize < limit && !containCollision(layer) && mSpriteList[layer] != null) mCollision[collisionSize++] = layer; return this; } public Spriter setCollisionLayer (int... layer) { if (layer.length > limit) return this; collisionSize = 0; for (int i : layer) { if (mSpriteList[i] != null) mCollision[collisionSize++] = i; } return this; } public Spriter removeCollisionLayer (int layer) { int i; outer: for (i = 0; i < collisionSize; i++) if (mCollision[i] == layer) break outer; System.arraycopy(mCollision, i + 1, mCollision, i, collisionSize - i); mCollision[--collisionSize] = -1; return this; } public Spriter removeCollsionLayer (int[] layer) { if (layer.length > collisionSize) return this; for (int i : layer) { removeCollisionLayer(i); } return this; } public int[] getCollisionLayer () { return mCollision; } public void clearCollision () { for (int i = 0; i < limit; i++) mCollision[i] = -1; collisionSize = 0; } /******************************************************** * ********************************************************/ private boolean containDrawable (int layer) { for (int i = 0; i < drawableSize; i++) if (mDrawable[i] == layer) return true; return false; } public Spriter addDrawableLayer (int layer) { if (drawableSize < limit && !containDrawable(layer) && mSpriteList[layer] != null) mDrawable[drawableSize++] = layer; return this; } public Spriter setDrawableLayer (int... layer) { if (layer.length > limit) return this; drawableSize = 0; for (int i : layer) { if (mSpriteList[i] != null) mDrawable[drawableSize++] = i; } return this; } public Spriter removeDrawableLayer (int layer) { int i; outer: for (i = 0; i < drawableSize; i++) if (mDrawable[i] == layer) break outer; System.arraycopy(mDrawable, i + 1, mDrawable, i, drawableSize - i); mDrawable[--drawableSize] = -1; return this; } public Spriter removeDrawableLayer (int[] layer) { if (layer.length > drawableSize) return this; for (int i : layer) { removeDrawableLayer(i); } return this; } public int[] getDrawbleLayer () { return mDrawable; } public void clearDrawable () { stop(); for (int i = 0; i < limit; i++) mDrawable[i] = -1; drawableSize = 0; } public void draw (SpriteBatch batch) { final int[] drawable = Spriter.this.mDrawable; for (int i = 0; i < drawableSize; i++) { mSpriteList[drawable[i]].draw(batch); } } @Override public void draw (SpriteBatch batch, float alpha) { final int[] drawable = Spriter.this.mDrawable; for (int i = 0; i < drawableSize; i++) { mSpriteList[drawable[i]].draw(batch, alpha); } } /******************************************************** * ********************************************************/ private boolean containRunnable (int layer) { for (int i = 0; i < runnbaleSize; i++) if (mRunnable[i] == layer) return true; return false; } public Spriter addRunnableLayer (int layer) { if (runnbaleSize < limit && !containRunnable(layer) && mSpriteList[layer] != null) mDrawable[runnbaleSize++] = layer; return this; } public Spriter setRunnableLayer (int... layer) { if (layer.length > limit) return this; runnbaleSize = 0; for (int i : layer) { final SpriteBackend sprite = mSpriteList[i]; if (sprite != null && sprite instanceof Animator) mRunnable[runnbaleSize++] = i; } return this; } public Spriter removeRunnableLayer (int layer) { int i; outer: for (i = 0; i < runnbaleSize; i++) if (mRunnable[i] == layer) break outer; System.arraycopy(mRunnable, i + 1, mRunnable, i, runnbaleSize - i); mRunnable[--runnbaleSize] = -1; return this; } public Spriter removeRunnableLayer (int[] layer) { if (layer.length > runnbaleSize) return this; for (int i : layer) { removeRunnableLayer(i); } return this; } public void clearRunnable () { stop(); for (int i = 0; i < limit; i++) mRunnable[i] = -1; runnbaleSize = 0; } public int[] getRunnableLayer () { return mRunnable; } public void setFrameDuration (float frameDuration) { final int[] runnable = this.mRunnable; for (int i = 0; i < runnbaleSize; i++) { ((Animator) mSpriteList[runnable[i]]).setFrameDuration(frameDuration); } } public void setFrameDuration (float frameDuration, int[] layer) { for (int i : layer) { if (i < 0 && i >= size) continue; else { final SpriteBackend sprite = mSpriteList[i]; if (sprite instanceof Animator) ((Animator) sprite).setFrameDuration(frameDuration); } } } public void setFrameDuration (float[] frameDurations, int[] layer) { if (frameDurations.length != layer.length) return; int i = 0; for (int l : layer) { if (l < 0 && l >= size) { ++i; continue; } else { final SpriteBackend sprite = mSpriteList[i]; if (sprite instanceof Animator) ((Animator) sprite).setFrameDuration(frameDurations[i]); } } } public void start () { RUN = true; for (int i = 0; i < runnbaleSize; i++) ((Animator) mSpriteList[mRunnable[i]]).start(); } public void start (float frameDuration) { RUN = true; for (int i = 0; i < runnbaleSize; i++) { ((Animator) mSpriteList[mRunnable[i]]).start(frameDuration); } } @Override public void start (float frameDuration, int playMode) { RUN = true; for (int i = 0; i < runnbaleSize; i++) ((Animator) mSpriteList[mRunnable[i]]).start(frameDuration, playMode); } @Override public void pause () { RUN = false; } @Override public boolean isRunning () { return RUN; } public void stop () { RUN = false; resetFrame(); } public void switchState () { RUN = !RUN; } public void resetFrame () { for (int i = 0; i < runnbaleSize; i++) ((Animator) mSpriteList[mRunnable[i]]).resetFrame(); } public void resetFrame (int... layer) { for (int i : layer) ((Animator) mSpriteList[i]).resetFrame(); } public void update (float delta) { if (!RUN) { // ============= update updatable ============= for (int i = 0, n = mUpdater.size; i < n; i++) { final Updater tmp = mUpdater.get(i); if (!tmp.isStoped()) tmp.update(this, delta); else { mUpdater.removeValue(tmp, true); --i; --n; } } return; } final int[] runnable = this.mRunnable; for (int i = 0; i < runnbaleSize; i++) ((Animator) mSpriteList[runnable[i]]).update(delta); // ============= update updatable ============= for (int i = 0, n = mUpdater.size; i < n; i++) { final Updater tmp = mUpdater.get(i); if (!tmp.isStoped()) tmp.update(this, delta); else { mUpdater.removeValue(tmp, true); --i; --n; } } } public void postUpdater (Updater updater) { if (mUpdater.contains(updater, true)) return; updater.start(); this.mUpdater.add(updater); } public int sizeUpdater () { return mUpdater.size; } public void noUpdater () { this.mUpdater.clear(); } /******************************************************** * ********************************************************/ @Override public void reset () { stop(); setPosition(0, 0); setSize(0, 0); setOrigin(0, 0); setRotation(0); setScale(1); } @Override public void dispose () { for (int i = 0; i < limit; i++) { mScaler[i] = null; mSpriteList[i] = null; } mOriginSprite = null; } private void refresh () { for (int i = 0; i < size; i++) { mScaler[i].apply(); } } class Scaler { float xRatio; float yRatio; float widthRatio; float heightRatio; SpriteBackend sprite; void apply () { sprite.setBounds(x + xRatio * w, y + yRatio * h, widthRatio * w, heightRatio * h); } String info () { return "xRatio : " + xRatio + " " + "yRatio : " + yRatio + " " + "widthRatio : " + widthRatio + " " + "heightRatio : " + heightRatio + " "; } } }