/**
* Copyright 2008 - 2011
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.1
*/
package loon.action.sprite;
import loon.Director.Origin;
import loon.LObject;
import loon.LSystem;
import loon.LTexture;
import loon.LTextures;
import loon.LTrans;
import loon.PlayerUtils;
import loon.action.ActionBind;
import loon.action.ActionTween;
import loon.action.collision.CollisionHelper;
import loon.action.map.Field2D;
import loon.canvas.LColor;
import loon.component.layout.BoxSize;
import loon.geom.Affine2f;
import loon.geom.Point;
import loon.geom.RectBox;
import loon.geom.Vector2f;
import loon.opengl.GLEx;
import loon.opengl.TextureUtils;
import loon.utils.Flip;
import loon.utils.IArray;
import loon.utils.LayerSorter;
import loon.utils.MathUtils;
import loon.utils.TArray;
import loon.utils.res.MovieSpriteSheet;
public class Sprite extends LObject<ISprite> implements Flip<Sprite>, ISprite, IArray, LTrans, BoxSize {
private final static LayerSorter<ISprite> childSorter = new LayerSorter<ISprite>(false);
private Origin _origin = Origin.CENTER;
private TArray<ISprite> _childList = null;
// 默认每帧刷新时间
final static private long defaultTimer = 150;
// 是否可见
private boolean visible = true;
// 精灵名称
private String spriteName;
// 精灵图片
private LTexture image;
// 动画
private Animation animation = new Animation();
private int transform;
private float _scaleX = 1f, _scaleY = 1f;
private boolean _flipX = false, _flipY = false;
private int maxFrame;
private Vector2f _pivot = new Vector2f(-1, -1);
/**
* 默认构造函数
*
*/
public Sprite() {
this(0, 0);
}
/**
* 以下参数分别为 坐标x,坐标y
*
* @param x
* @param y
*/
public Sprite(float x, float y) {
this("Sprite" + System.currentTimeMillis(), x, y);
}
/**
* 以下参数分别为 精灵名,坐标x,坐标y
*
* @param spriteName
* @param x
* @param y
*/
private Sprite(String spriteName, float x, float y) {
this.setLocation(x, y);
this.spriteName = spriteName;
this.visible = true;
this.transform = LTrans.TRANS_NONE;
}
/**
* 以下参数分别为 取材文件,每行取材宽度,每列取材长度
*
* @param fileName
* @param row
* @param col
*/
public Sprite(String fileName, int row, int col) {
this(fileName, -1, 0, 0, row, col, defaultTimer);
}
/**
* 以下参数分别为 取材文件,每行取材宽度,每列取材长度,平均每桢显示时间
*
* @param fileName
* @param row
* @param col
* @param timer
*/
public Sprite(String fileName, int row, int col, long timer) {
this(fileName, -1, 0, 0, row, col, timer);
}
/**
* 以下参数分别为 取材文件,坐标x,坐标y,每行取材宽度,每列取材长度
*
* @param fileName
* @param x
* @param y
* @param row
* @param col
*/
public Sprite(String fileName, float x, float y, int row, int col) {
this(fileName, x, y, row, col, defaultTimer);
}
/**
* 以下参数分别为 取材文件,坐标x,坐标y,每行取材宽度,每列取材长度,平均每桢显示时间
*
* @param fileName
* @param x
* @param y
* @param row
* @param col
* @param timer
*/
private Sprite(String fileName, float x, float y, int row, int col, long timer) {
this(fileName, -1, x, y, row, col, timer);
}
/**
* 以下参数分别为 取材文件,最大分解桢数,坐标x,坐标y,每行取材宽度,每列取材长度
*
* @param fileName
* @param maxFrame
* @param x
* @param y
* @param row
* @param col
*/
public Sprite(String fileName, int maxFrame, float x, float y, int row, int col) {
this(fileName, maxFrame, x, y, row, col, defaultTimer);
}
/**
* 以下参数分别为 取材文件,最大分解桢数,坐标x,坐标y,每行取材宽度,每列取材长度,平均每桢显示时间
*
* @param fileName
* @param maxFrame
* @param x
* @param y
* @param row
* @param col
* @param timer
*/
public Sprite(String fileName, int maxFrame, float x, float y, int row, int col, long timer) {
this("Sprite" + System.currentTimeMillis(), fileName, maxFrame, x, y, row, col, timer);
}
/**
* 以下参数分别为 精灵名,取材文件,最大分解桢数,坐标x,坐标y,每行取材宽度,每列取材长度,平均每桢显示时间
*
* @param spriteName
* @param fileName
* @param maxFrame
* @param x
* @param y
* @param row
* @param col
* @param timer
*/
public Sprite(String spriteName, String fileName, int maxFrame, float x, float y, int row, int col, long timer) {
this(spriteName, TextureUtils.getSplitTextures(fileName, row, col), maxFrame, x, y, timer);
}
/**
* 注入指定图片
*
* @param fileName
*/
public Sprite(String fileName) {
this(LTextures.loadTexture(fileName));
}
/**
* 注入指定图片
*
* @param images
*/
public Sprite(final LTexture img) {
this(new LTexture[] { img }, 0, 0);
}
/**
* 以下参数分别为 图像数组
*
* @param images
*/
public Sprite(LTexture[] images) {
this(images, 0, 0);
}
/**
* 以下参数分别为 图像数组,坐标x,坐标y
*
* @param images
* @param x
* @param y
*/
public Sprite(LTexture[] images, float x, float y) {
this(images, x, y, defaultTimer);
}
/**
* 以下参数分别为 图像数组,平均每桢显示时间
*
* @param images
* @param timer
*/
public Sprite(LTexture[] images, long timer) {
this(images, -1, 0, 0, defaultTimer);
}
/**
* 以下参数分别为 图像数组,坐标x,坐标y,平均每桢显示时间
*
* @param images
* @param x
* @param y
* @param timer
*/
public Sprite(LTexture[] images, float x, float y, long timer) {
this(images, -1, x, y, timer);
}
/**
* 以下参数分别为 图像数组,最大分解桢数,坐标x,坐标y,平均每桢显示时间
*
* @param spriteName
* @param images
* @param maxFrame
* @param x
* @param y
* @param timer
*/
public Sprite(LTexture[] images, int maxFrame, float x, float y, long timer) {
this("Sprite" + System.currentTimeMillis(), images, maxFrame, x, y, timer);
}
/**
* 以下参数分别为 精灵名,图像数组,最大分解桢数,坐标x,坐标y,平均每桢显示时间
*
* @param spriteName
* @param images
* @param maxFrame
* @param x
* @param y
* @param timer
*/
public Sprite(String spriteName, LTexture[] images, int maxFrame, float x, float y, long timer) {
this.setLocation(x, y);
this.spriteName = spriteName;
this.setAnimation(animation, images, maxFrame, timer);
this.visible = true;
this.transform = LTrans.TRANS_NONE;
}
/**
* 以资源SpriteSheet构建精灵
*
* @param sheet
* @param x
* @param y
* @param timer
*/
public Sprite(MovieSpriteSheet sheet, float x, float y, long timer) {
this("Sprite" + System.currentTimeMillis(), sheet, x, y, timer);
}
/**
* 以资源SpriteSheet构建精灵
*
* @param spriteName
* @param sheet
* @param x
* @param y
* @param timer
*/
public Sprite(String spriteName, MovieSpriteSheet sheet, float x, float y, long timer) {
this.setLocation(x, y);
this.spriteName = spriteName;
LTexture[] texs = sheet.getTextures();
this.setAnimation(animation, texs, texs.length, timer);
this.visible = true;
this.transform = LTrans.TRANS_NONE;
}
/**
* 是否在播放动画
*
* @param running
*/
public void setRunning(boolean running) {
animation.setRunning(running);
}
/**
* 返回当前总桢数
*
* @return
*/
public int getTotalFrames() {
return animation.getTotalFrames();
}
/**
* 设定当前帧
*
* @param index
*/
public void setCurrentFrameIndex(int index) {
animation.setCurrentFrameIndex(index);
}
/**
* 返回当前桢索引
*
* @return
*/
public int getCurrentFrameIndex() {
return animation.getCurrentFrameIndex();
}
/**
* 获得当前精灵的窗体居中横坐标
*
* @param x
* @return
*/
public int centerX(int x) {
return centerX(this, x);
}
/**
* 获得指定精灵的窗体居中横坐标
*
* @param sprite
* @param x
* @return
*/
public static int centerX(Sprite sprite, int x) {
int newX = (int) (x - (sprite.getWidth() / 2));
if (newX + sprite.getWidth() >= LSystem.viewSize.getWidth()) {
return (int) (LSystem.viewSize.getWidth() - sprite.getWidth() - 1);
}
if (newX < 0) {
return x;
} else {
return newX;
}
}
/**
* 获得当前精灵的窗体居中纵坐标
*
* @param y
* @return
*/
public int centerY(int y) {
return centerY(this, y);
}
/**
* 获得指定精灵的窗体居中纵坐标
*
* @param sprite
* @param y
* @return
*/
public static int centerY(Sprite sprite, int y) {
int newY = (int) (y - (sprite.getHeight() / 2));
if (newY + sprite.getHeight() >= LSystem.viewSize.getHeight()) {
return (int) (LSystem.viewSize.getHeight() - sprite.getHeight() - 1);
}
if (newY < 0) {
return y;
} else {
return newY;
}
}
/**
* 插入指定动画
*
* @param myAnimation
* @param images
* @param maxFrame
* @param timer
*/
private void setAnimation(Animation myAnimation, LTexture[] images, int max, long timer) {
this.maxFrame = max;
if (maxFrame != -1) {
for (int i = 0; i < maxFrame; i++) {
myAnimation.addFrame(images[i], timer);
}
} else {
for (int i = 0; i < images.length; i++) {
myAnimation.addFrame(images[i], timer);
}
}
}
/**
* 插入指定动画
*
* @param fileName
* @param maxFrame
* @param row
* @param col
* @param timer
*/
public void setAnimation(String fileName, int maxFrame, int row, int col, long timer) {
setAnimation(new Animation(), TextureUtils.getSplitTextures(fileName, row, col), maxFrame, timer);
}
/**
* 插入指定动画
*
* @param fileName
* @param row
* @param col
* @param timer
*/
public void setAnimation(String fileName, int row, int col, long timer) {
setAnimation(fileName, -1, row, col, timer);
}
/**
* 插入指定动画
*
* @param images
* @param maxFrame
* @param timer
*/
public void setAnimation(LTexture[] images, int maxFrame, long timer) {
setAnimation(new Animation(), images, maxFrame, timer);
}
/**
* 插入指定动画
*
* @param images
* @param timer
*/
public void setAnimation(LTexture[] images, long timer) {
setAnimation(new Animation(), images, -1, timer);
}
/**
* 插入指定动画
*
* @param animation
*/
public void setAnimation(Animation animation) {
this.animation = animation;
this.maxFrame = animation.getTotalFrames();
}
public Animation getAnimation() {
return animation;
}
protected void onUpdate(long elapsedTime) {
}
/**
* 变更动画
*/
public void update(long elapsedTime) {
if (visible) {
animation.update(elapsedTime);
onUpdate(elapsedTime);
if (_childList != null && _childList.size > 0) {
for (ISprite spr : _childList) {
if (spr != null) {
spr.update(elapsedTime);
}
}
}
}
}
/**
* 变更定位器坐标
*
* @param vector
*/
public void updateLocation(Vector2f vector) {
this.setX(MathUtils.round(vector.getX()));
this.setY(MathUtils.round(vector.getY()));
}
public LTexture getImage() {
return animation.getSpriteImage();
}
@Override
public float getWidth() {
LTexture si = animation.getSpriteImage();
if (si == null) {
return -1;
}
return (int) (si.width() * _scaleX);
}
@Override
public float getHeight() {
LTexture si = animation.getSpriteImage();
if (si == null) {
return -1;
}
return (int) (si.height() * _scaleY);
}
/**
* 获得精灵的中间位置
*
* @return
*/
public Point getMiddlePoint() {
return new Point(x() + getWidth() / 2, y() + getHeight() / 2);
}
/**
* 获得两个精灵的中间距离
*
* @param second
* @return
*/
public float getDistance(Sprite second) {
return (float) this.getMiddlePoint().distanceTo(second.getMiddlePoint());
}
/**
* 返回碰撞盒
*
* @return
*/
public RectBox getCollisionBox() {
return getRect(x(), y(), getWidth(), getHeight());
}
/**
* 检查是否与指定精灵位置发生了矩形碰撞
*
* @param sprite
* @return
*/
public boolean isRectToRect(Sprite sprite) {
return CollisionHelper.isRectToRect(this.getCollisionBox(), sprite.getCollisionBox());
}
/**
* 检查是否与指定精灵位置发生了圆形碰撞
*
* @param sprite
* @return
*/
public boolean isCircToCirc(Sprite sprite) {
return CollisionHelper.isCircToCirc(this.getCollisionBox(), sprite.getCollisionBox());
}
/**
* 检查是否与指定精灵位置发生了方形与圆形碰撞
*
* @param sprite
* @return
*/
public boolean isRectToCirc(Sprite sprite) {
return CollisionHelper.isRectToCirc(this.getCollisionBox(), sprite.getCollisionBox());
}
private LColor filterColor;
@Override
public void createUI(GLEx g) {
createUI(g, 0, 0);
}
@Override
public void createUI(GLEx g, float offsetX, float offsetY) {
if (!visible) {
return;
}
if (_alpha < 0.01) {
return;
}
if (animation.getCurrentFrameIndex() > maxFrame) {
animation.reset();
}
image = animation.getSpriteImage();
final boolean notImg = image == null;
if (animation != null && animation.size > 0 && notImg) {
return;
}
float width = notImg ? getContainerWidth() : image.getWidth();
float height = notImg ? getContainerHeight() : image.getHeight();
boolean update = (_rotation != 0) || !(_scaleX == 1f && _scaleY == 1f) || _flipX || _flipY;
int tmp = g.color();
int blend = g.getBlendMode();
try {
g.setBlendMode(_blend);
float nx = this._location.x + offsetX;
float ny = this._location.y + offsetY;
if (update) {
g.saveTx();
Affine2f tx = g.tx();
final float centerX = this._pivot.x == -1 ? (nx + _origin.ox(width)) : nx + this._pivot.x;
final float centerY = this._pivot.y == -1 ? (ny + _origin.oy(height)) : ny + this._pivot.y;
if (_rotation != 0 && notImg) {
tx.translate(centerX, centerY);
tx.preRotate(_rotation);
tx.translate(-centerX, -centerY);
}
if (_flipX || _flipY) {
if (_flipX && _flipY) {
Affine2f.transform(tx, centerX, centerY, Affine2f.TRANS_ROT180);
} else if (_flipX) {
Affine2f.transform(tx, centerX, centerY, Affine2f.TRANS_MIRROR);
} else if (_flipY) {
Affine2f.transform(tx, centerX, centerY, Affine2f.TRANS_MIRROR_ROT180);
}
}
if (((_scaleX != 1) || (_scaleY != 1)) && notImg) {
tx.translate(centerX, centerY);
tx.preScale(_scaleX, _scaleY);
tx.translate(-centerX, -centerY);
}
}
g.setAlpha(_alpha);
if (!notImg) {
if (LTrans.TRANS_NONE == transform) {
g.draw(image, nx, ny, width, height, filterColor, _rotation, _pivot, _scaleX, _scaleY);
} else {
g.drawRegion(image, 0, 0, (int) width, (int) height, transform, (int) nx, (int) ny,
LTrans.TOP | LTrans.LEFT, filterColor, _pivot, _scaleX, _scaleY, _rotation);
}
}
if (_childList != null && _childList.size > 0) {
for (ISprite spr : _childList) {
if (spr != null) {
float px = 0, py = 0;
ISprite parent = spr.getParent();
if (parent != null) {
px += parent.getX();
py += parent.getY();
for (; (parent = parent.getParent()) != null;) {
px += parent.getX();
py += parent.getY();
}
}
spr.createUI(g, px, py);
}
}
}
} finally {
g.setColor(tmp);
if (update) {
g.restoreTx();
}
g.setBlendMode(blend);
}
}
public float getScreenX() {
float x = 0;
ISprite parent = _super;
if (parent != null) {
x += parent.getX();
for (; (parent = parent.getParent()) != null;) {
x += parent.getX();
}
}
return x;
}
public float getScreenY() {
float y = 0;
ISprite parent = _super;
if (parent != null) {
y += parent.getY();
for (; (parent = parent.getParent()) != null;) {
y += parent.getY();
}
}
return y;
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public void setVisible(boolean visible) {
this.visible = visible;
}
public String getSpriteName() {
return spriteName;
}
public void setSpriteName(String spriteName) {
this.spriteName = spriteName;
}
public int getTransform() {
return transform;
}
public void setTransform(int transform) {
this.transform = transform;
}
public LColor getFilterColor() {
return new LColor(filterColor);
}
public void setFilterColor(LColor filterColor) {
this.filterColor = filterColor;
}
@Override
public LTexture getBitmap() {
return this.image;
}
@Override
public float getScaleX() {
return _scaleX;
}
public void setScaleX(float _scaleX) {
this._scaleX = _scaleX;
}
public float getScaleY() {
return _scaleY;
}
public void setScaleY(float _scaleY) {
this._scaleY = _scaleY;
}
public void setPivotX(float pX) {
_pivot.setX(pX);
}
public void setPivotY(float pY) {
_pivot.setY(pY);
}
public float getPivotX() {
return _pivot.getX();
}
public float getPivotY() {
return _pivot.getY();
}
public void setPivot(float pX, float pY) {
_pivot.set(pX, pY);
}
@Override
public Field2D getField2D() {
return null;
}
@Override
public boolean isBounded() {
return false;
}
@Override
public boolean isContainer() {
return _childList != null && _childList.size > 0;
}
@Override
public boolean inContains(float x, float y, float w, float h) {
return getCollisionBox().contains(x, y, w, h);
}
@Override
public RectBox getRectBox() {
return getCollisionBox();
}
public void setScale(float s) {
this.setScale(s, s);
}
@Override
public void setScale(float sx, float sy) {
this._scaleX = sx;
this._scaleY = sy;
}
public void setSize(float width, float height) {
this._scaleX = getWidth() / width;
this._scaleY = getHeight() / height;
}
public int getMaxFrame() {
return maxFrame;
}
public void setMaxFrame(int maxFrame) {
this.maxFrame = maxFrame;
}
@Override
public void setWidth(float w) {
this._scaleX = (w / getWidth());
}
@Override
public void setHeight(float h) {
this._scaleY = (h / getHeight());
}
public void addChildAt(ISprite spr, float x, float y) {
if (spr != null) {
spr.setLocation(x, y);
addChild(spr);
}
}
public void addChild(ISprite spr) {
if (_childList == null) {
_childList = new TArray<ISprite>();
}
spr.setParent(this);
spr.setState(State.ADDED);
_childList.add(spr);
childSorter.sort(_childList);
}
public boolean removeChild(ISprite spr) {
if (spr == null) {
return true;
}
if (_childList == null) {
_childList = new TArray<ISprite>();
}
boolean removed = _childList.remove(spr);
if (removed) {
spr.setState(State.REMOVED);
}
// 删除精灵同时,删除缓动动画
if (removed && spr instanceof ActionBind) {
removeActionEvents((ActionBind) spr);
}
return removed;
}
public boolean removeChild(int idx) {
if (idx < 0) {
return true;
}
if (_childList == null) {
_childList = new TArray<ISprite>();
}
for (int i = this._childList.size - 1; i >= 0; i--) {
if (i == idx) {
final ISprite removed = this._childList.removeIndex(i);
final boolean exist = (removed == null);
if (exist) {
removed.setState(State.REMOVED);
}
// 删除精灵同时,删除缓动动画
if (exist && (removed instanceof ActionBind)) {
removeActionEvents((ActionBind) removed);
}
return exist;
}
}
return false;
}
public void removeChilds() {
if (this._childList == null) {
return;
}
for (int i = this._childList.size - 1; i >= 0; i--) {
final ISprite removed = this._childList.get(i);
boolean exist = (removed == null);
if (exist) {
removed.setState(State.REMOVED);
}
// 删除精灵同时,删除缓动动画
if (exist && removed instanceof ActionBind) {
removeActionEvents((ActionBind) removed);
}
}
this._childList.clear();
}
@Override
public void setColor(LColor color) {
setFilterColor(color);
}
@Override
public LColor getColor() {
return getFilterColor();
}
public Origin getOrigin() {
return _origin;
}
public void setOrigin(Origin o) {
this._origin = o;
}
public ActionTween selfAction() {
return PlayerUtils.set(this);
}
public boolean isActionCompleted() {
return PlayerUtils.isActionCompleted(this);
}
@Override
public Sprite setFlipX(boolean x) {
this._flipX = x;
return this;
}
@Override
public Sprite setFlipY(boolean y) {
this._flipY = y;
return this;
}
@Override
public Sprite setFlipXY(boolean x, boolean y) {
setFlipX(x);
setFlipY(y);
return this;
}
@Override
public boolean isFlipX() {
return _flipX;
}
@Override
public boolean isFlipY() {
return _flipY;
}
@Override
public int size() {
return (_childList == null ? 0 : _childList.size);
}
@Override
public void clear() {
if (_childList != null) {
removeChilds();
}
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void close() {
this.visible = false;
if (image != null) {
image.close();
}
if (animation != null) {
animation.close();
}
setState(State.DISPOSED);
removeChilds();
removeActionEvents(this);
}
}