package loon.action.sprite; import java.util.Comparator; import loon.Director.Origin; import loon.LObject; import loon.LTexture; import loon.LTextures; import loon.PlayerUtils; import loon.action.ActionBind; import loon.action.ActionTween; import loon.action.map.Field2D; import loon.canvas.LColor; import loon.component.layout.BoxSize; import loon.geom.Affine2f; import loon.geom.Dimension; import loon.geom.RectBox; import loon.geom.Vector2f; import loon.opengl.GLEx; import loon.utils.IArray; import loon.utils.LayerSorter; import loon.utils.TArray; public class Entity extends LObject<IEntity> implements IEntity, IArray, BoxSize { private static final int CHILDREN_CAPACITY_DEFAULT = 4; protected Origin _origin = Origin.CENTER; protected boolean _visible = true; protected boolean _deform = true; protected boolean _ignoreUpdate = false; protected boolean _childrenVisible = true; protected boolean _childrenIgnoreUpdate = false; protected boolean _childrenSortPending = false; protected int _idxTag = IEntity.TAG_INVALID; protected boolean _repaintDraw = false; protected TArray<IEntity> _childrens; protected RectBox _shear; protected LColor _baseColor = new LColor(LColor.white); protected Vector2f _offset = new Vector2f(); protected float _rotationCenterX = -1; protected float _rotationCenterY = -1; protected float _scaleX = 1f; protected float _scaleY = 1f; protected float _scaleCenterX = -1; protected float _scaleCenterY = -1; protected float _skewX = 0; protected float _skewY = 0; protected float _skewCenterX = -1; protected float _skewCenterY = -1; protected boolean _flipX = false, _flipY = false; private final static LayerSorter<IEntity> entitySorter = new LayerSorter<IEntity>(false); private boolean _stopUpdate = false; protected float _width, _height; protected LTexture _image; public Entity() { this((LTexture) null); } public Entity(final String path) { this(LTextures.loadTexture(path)); } public Entity(final LTexture texture) { this(texture, 0, 0, texture == null ? 0 : texture.getWidth(), texture == null ? 0 : texture.getHeight()); } public Entity(final LTexture texture, final float x, final float y) { this(texture, x, y, texture == null ? 0 : texture.getWidth(), texture == null ? 0 : texture.getHeight()); } public Entity(final LTexture texture, final float x, final float y, final float w, final float h) { this.setLocation(x, y); this._width = w; this._height = h; this._image = texture; } public static Entity make(LTexture texture, final float x, final float y) { return new Entity(texture, x, y); } public static Entity make(String path, final float x, final float y) { return new Entity(LTextures.loadTexture(path), x, y); } protected void onUpdateColor() { } public void setTexture(String path) { setTexture(LTextures.loadTexture(path)); } public void setTexture(LTexture tex) { this._image = tex; if (_width <= 0) { _width = _image.width(); } if (_height <= 0) { _height = _image.height(); } this._repaintDraw = (tex == null); } @Override public boolean isVisible() { return this._visible; } @Override public void setVisible(final boolean pVisible) { this._visible = pVisible; } @Override public boolean isChildrenVisible() { return this._childrenVisible; } @Override public void setChildrenVisible(final boolean v) { this._childrenVisible = v; } @Override public boolean isIgnoreUpdate() { return this._ignoreUpdate; } @Override public void setIgnoreUpdate(final boolean u) { this._ignoreUpdate = u; } @Override public boolean isChildrenIgnoreUpdate() { return this._childrenIgnoreUpdate; } @Override public void setChildrenIgnoreUpdate(final boolean c) { this._childrenIgnoreUpdate = c; } @Override public int getIndexTag() { return this._idxTag; } @Override public void setIndexTag(final int idx) { this._idxTag = idx; } @Override public boolean isRotated() { return this._rotation != 0; } @Override public float getRotationCenterX() { return this._rotationCenterX; } @Override public float getRotationCenterY() { return this._rotationCenterY; } @Override public void setRotationCenterX(final float sx) { this._rotationCenterX = sx; } @Override public void setRotationCenterY(final float sy) { this._rotationCenterY = sy; } @Override public void setRotationCenter(final float sx, final float sy) { this._rotationCenterX = sx; this._rotationCenterY = sy; } @Override public float getPivotX() { return this._rotationCenterX; } @Override public float getPivotY() { return this._rotationCenterY; } @Override public void setPivotX(final float rx) { this._rotationCenterX = rx; this._scaleCenterX = rx; } @Override public void setPivotY(final float ry) { this._rotationCenterY = ry; this._scaleCenterY = ry; } @Override public void setPivot(final float rx, final float ry) { setPivotX(rx); setPivotY(ry); } @Override public boolean isScaled() { return (this._scaleX != 1) || (this._scaleY != 1); } @Override public float getScaleX() { return this._scaleX; } @Override public float getScaleY() { return this._scaleY; } @Override public void setScaleX(final float pScaleX) { this._scaleX = pScaleX; } @Override public void setScaleY(final float pScaleY) { this._scaleY = pScaleY; } @Override public void setScale(final float pScale) { this._scaleX = pScale; this._scaleY = pScale; } @Override public void setScale(final float pScaleX, final float pScaleY) { this._scaleX = pScaleX; this._scaleY = pScaleY; } @Override public float getScaleCenterX() { return this._scaleCenterX; } @Override public float getScaleCenterY() { return this._scaleCenterY; } @Override public void setScaleCenterX(final float sx) { this._scaleCenterX = sx; } @Override public void setScaleCenterY(final float sy) { this._scaleCenterY = sy; } @Override public void setScaleCenter(final float sx, final float sy) { this._scaleCenterX = sx; this._scaleCenterY = sy; } @Override public boolean isSkewed() { return (this._skewX != 0) || (this._skewY != 0); } @Override public float getSkewX() { return this._skewX; } @Override public float getSkewY() { return this._skewY; } @Override public void setSkewX(final float sx) { this._skewX = sx; } @Override public void setSkewY(final float sy) { this._skewY = sy; } @Override public void setSkew(final float pSkew) { this._skewX = pSkew; this._skewY = pSkew; } @Override public void setSkew(final float sx, final float sy) { this._skewX = sx; this._skewY = sy; } @Override public float getSkewCenterX() { return this._skewCenterX; } @Override public float getSkewCenterY() { return this._skewCenterY; } @Override public void setSkewCenterX(final float sx) { this._skewCenterX = sx; } @Override public void setSkewCenterY(final float sy) { this._skewCenterY = sy; } @Override public void setSkewCenter(final float sx, final float sy) { this._skewCenterX = sx; this._skewCenterY = sy; } @Override public boolean isRotatedOrScaledOrSkewed() { return (this._rotation != 0) || (this._scaleX != 1) || (this._scaleY != 1) || (this._skewX != 0) || (this._skewY != 0); } @Override public float getRed() { return this._baseColor.r; } @Override public float getGreen() { return this._baseColor.g; } @Override public float getBlue() { return this._baseColor.b; } @Override public float getAlpha() { return super.getAlpha(); } @Override public LColor getColor() { return this._baseColor; } @Override public void setColor(final LColor pColor) { this._baseColor.setColor(pColor); this.onUpdateColor(); } @Override public void setColor(final int pColor) { this._baseColor.setColor(pColor); this.onUpdateColor(); } @Override public void setAlpha(final float a) { super.setAlpha(a); this._baseColor.a = a; this.onUpdateColor(); } @Override public void setColor(final float r, final float g, final float b) { this._baseColor.setColor(r, g, b); this.onUpdateColor(); } @Override public void setColor(final float r, final float g, final float b, final float a) { this._baseColor.setColor(r, g, b, a); this.onUpdateColor(); } @Override public int getChildCount() { if (this._childrens == null) { return 0; } return this._childrens.size; } @Override public IEntity getChildByTag(final int idx) { if (this._childrens == null) { return null; } for (int i = this._childrens.size - 1; i >= 0; i--) { final IEntity child = this._childrens.get(i); if (child.getIndexTag() == idx) { return child; } } return null; } @Override public IEntity getChildByIndex(final int pIndex) { if (this._childrens == null) { return null; } return this._childrens.get(pIndex); } @Override public IEntity getFirstChild() { if (this._childrens == null) { return null; } return this._childrens.get(0); } @Override public IEntity getLastChild() { if (this._childrens == null) { return null; } return this._childrens.get(this._childrens.size - 1); } @Override public void addChild(final IEntity e) { if (e == null) { return; } if (this._childrens == null) { this.allocateChildren(); } this._childrens.add(e); e.setParent(this); e.setState(State.ADDED); e.onAttached(); } @Override public void addChildAt(final IEntity e, float x, float y) { if (e != null) { e.setLocation(x, y); addChild(e); } } @Override public void sortChildren() { this.sortChildren(true); } @Override public void sortChildren(final boolean i) { if (this._childrens == null) { return; } if (i) { entitySorter.sort(this._childrens); } else { this._childrenSortPending = true; } } @Override public void sortChildren(final Comparator<IEntity> e) { if (this._childrens == null) { return; } entitySorter.sort(this._childrens, e); } @Override public boolean removeSelf() { final IEntity parent = this._super; if (parent != null) { return parent.removeChild(this); } else { return false; } } @Override public void removeChildren() { if (this._childrens == null) { return; } for (int i = this._childrens.size - 1; i >= 0; i--) { final IEntity removed = this._childrens.get(i); if (removed != null) { removed.setState(State.REMOVED); removed.onDetached(); } // 删除精灵同时,删除缓动动画 if (removed != null && removed instanceof ActionBind) { removeActionEvents((ActionBind) removed); } } this._childrens.clear(); } @Override public boolean removeChild(final IEntity e) { if (e == null) { return true; } if (this._childrens == null) { return false; } boolean removed = this._childrens.remove(e); if (removed) { e.setState(State.REMOVED); e.onDetached(); } // 删除精灵同时,删除缓动动画 if (removed && e instanceof ActionBind) { removeActionEvents((ActionBind) e); } return removed; } @Override public IEntity removeChild(final int idx) { if (this._childrens == null) { return null; } for (int i = this._childrens.size - 1; i >= 0; i--) { if (this._childrens.get(i).getIndexTag() == idx) { final IEntity removed = this._childrens.removeIndex(i); if (removed != null) { removed.setState(State.REMOVED); removed.onDetached(); } // 删除精灵同时,删除缓动动画 if (removed != null && (removed instanceof ActionBind)) { removeActionEvents((ActionBind) removed); } return removed; } } return null; } @Override public void onAttached() { } @Override public void onDetached() { } @Override public Object getUserData() { return this.Tag; } @Override public void setUserData(final Object u) { this.Tag = u; } @Override public final void createUI(final GLEx g) { this.createUI(g, 0, 0); } @Override public final void createUI(final GLEx g, final float offsetX, final float offsetY) { if (this._visible) { this.onManagedPaint(g, offsetX, offsetY); } } @Override public void reset() { this._visible = true; this._ignoreUpdate = false; this._childrenVisible = true; this._childrenIgnoreUpdate = false; this._rotation = 0f; this._scaleX = 1f; this._scaleY = 1f; this._skewX = 0f; this._skewY = 0f; this._flipX = false; this._flipY = false; this._baseColor.reset(); if (this._childrens != null) { final TArray<IEntity> entities = this._childrens; for (int i = entities.size - 1; i >= 0; i--) { IEntity e = entities.get(i); if (e != null) { e.reset(); } } } } protected void prePaint(final GLEx g) { } protected void paint(final GLEx g) { paint(g, 0, 0); } protected void paint(final GLEx g, float offsetX, float offsetY) { if (_alpha < 0.01) { return; } boolean exist = _image != null || (_width > 0 && _height > 0) || _repaintDraw; if (exist) { int blend = g.getBlendMode(); g.setBlendMode(_blend); boolean update = ((_rotation != 0 || !(_scaleX == 1f && _scaleY == 1f) || !(_skewX == 0 && _skewY == 0)) || _flipX || _flipY) && _deform; float nx = offsetX + this._location.x + _offset.x; float ny = offsetY + this._location.y + _offset.y; if (update) { g.saveTx(); g.saveBrush(); Affine2f tx = g.tx(); final float rotation = this._rotation; final float scaleX = this._scaleX; final float scaleY = this._scaleY; if (rotation != 0) { final float rotationCenterX = this._rotationCenterX == -1 ? (nx + _origin.ox(this._width)) : nx + this._rotationCenterX; final float rotationCenterY = this._rotationCenterY == -1 ? (ny + _origin.oy(this._height)) : ny + this._rotationCenterY; tx.translate(rotationCenterX, rotationCenterY); tx.preRotate(rotation); tx.translate(-rotationCenterX, -rotationCenterY); } final boolean flipX = this._flipX; final boolean flipY = this._flipY; if (flipX || flipY) { final float rotationCenterX = this._rotationCenterX == -1 ? (nx + _origin.ox(this._width)) : nx + this._rotationCenterX; final float rotationCenterY = this._rotationCenterY == -1 ? (ny + _origin.oy(this._height)) : ny + this._rotationCenterY; if (flipX && flipY) { Affine2f.transform(tx, rotationCenterX, rotationCenterY, Affine2f.TRANS_ROT180); } else if (flipX) { Affine2f.transform(tx, rotationCenterX, rotationCenterY, Affine2f.TRANS_MIRROR); } else if (flipY) { Affine2f.transform(tx, rotationCenterX, rotationCenterY, Affine2f.TRANS_MIRROR_ROT180); } } if ((scaleX != 1) || (scaleY != 1)) { final float scaleCenterX = this._scaleCenterX == -1 ? (nx + _origin.ox(this._width)) : nx + this._scaleCenterX; final float scaleCenterY = this._scaleCenterY == -1 ? (ny + _origin.oy(this._height)) : ny + this._scaleCenterY; tx.translate(scaleCenterX, scaleCenterY); tx.preScale(scaleX, scaleY); tx.translate(-scaleCenterX, -scaleCenterY); } final float skewX = this._skewX; final float skewY = this._skewY; if ((skewX != 0) || (skewY != 0)) { final float skewCenterX = this._skewCenterX == -1 ? (nx + _origin.ox(this._width)) : nx + this._skewCenterX; final float skewCenterY = this._skewCenterY == -1 ? (ny + _origin.oy(this._height)) : ny + this._skewCenterY; tx.translate(skewCenterX, skewCenterY); tx.preShear(skewX, skewY); tx.translate(-skewCenterX, -skewCenterY); } } if (_repaintDraw) { boolean elastic = (_shear != null); if (elastic) { g.setClip(drawX(offsetX + _shear.x), drawY(offsetY + _shear.y), _shear.width, _shear.height); } float tmp = g.alpha(); g.setAlpha(_alpha); repaint(g, offsetX, offsetY); g.setAlpha(tmp); if (elastic) { g.clearClip(); } } else { if (_image != null) { if (_shear == null) { g.draw(_image, nx, ny, _width, _height, _baseColor); } else { g.draw(_image, nx, ny, _width, _height, _shear.x, _shear.y, _shear.width, _shear.height, _baseColor); } } else { g.fillRect(nx, ny, _width, _height, _baseColor); } } if (update) { g.restoreBrush(); g.restoreTx(); } g.setBlendMode(blend); } } public void setRepaint(boolean r) { this._repaintDraw = r; } public boolean isRepaint() { return this._repaintDraw; } protected void repaint(GLEx g, float offsetX, float offsetY) { } protected void postPaint(final GLEx g) { } private void allocateChildren() { this._childrens = new TArray<IEntity>(Entity.CHILDREN_CAPACITY_DEFAULT); } protected void onManagedPaint(final GLEx g, float offsetX, float offsetY) { final TArray<IEntity> children = this._childrens; if ((children == null) || !this._childrenVisible) { this.prePaint(g); this.paint(g, offsetX, offsetY); this.postPaint(g); } else { if (this._childrenSortPending) { entitySorter.sort(this._childrens); this._childrenSortPending = false; } final int childCount = children.size; int i = 0; for (; i < childCount; i++) { final IEntity child = children.get(i); if (child.getLayer() < 0) { child.createUI(g, offsetX, offsetY); } else { break; } } this.prePaint(g); this.paint(g, offsetX, offsetY); this.postPaint(g); for (; i < childCount; i++) { children.get(i).createUI(g, offsetX, offsetY); } } } protected void onManagedUpdate(final long elapsedTime) { if (_stopUpdate) { return; } if ((this._childrens != null) && !this._childrenIgnoreUpdate) { final TArray<IEntity> entities = this._childrens; final int entityCount = entities.size; for (int i = 0; i < entityCount; i++) { entities.get(i).update(elapsedTime); } } onUpdate(elapsedTime); } protected void onUpdate(final long elapsedTime) { } @Override public void update(long elapsedTime) { if (!this._ignoreUpdate) { this.onManagedUpdate(elapsedTime); } } public int width() { return (int) getWidth(); } public int height() { return (int) getHeight(); } @Override public float getWidth() { return (_width * this._scaleX); } @Override public float getHeight() { return (_height * this._scaleY); } @Override public Field2D getField2D() { return null; } @Override public boolean isBounded() { return false; } @Override public boolean isContainer() { return _childrens != null && _childrens.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(); } @Override public RectBox getCollisionBox() { return getRect(x(), y(), getWidth(), getHeight()); } @Override public LTexture getBitmap() { return _image; } public void clearImage() { if (_image != null) { _image.close(); _image = null; } } @Override public void setWidth(float w) { this._width = w; } @Override public void setHeight(float h) { this._height = h; } public void setSize(float w, float h) { setWidth(w); setHeight(h); } public void setBounds(float x, float y, float width, float height) { this.setLocation(x, y); this.setSize(width, height); } public Dimension getDimension() { return new Dimension(this._width * this._scaleX, this._height * this._scaleY); } private void allocateShear() { if (_shear == null) { _shear = new RectBox(0, 0, this._width, this._height); } } public RectBox getClip() { return getShear(); } public RectBox getShear() { allocateShear(); return _shear; } public void setShear(RectBox s) { allocateShear(); this._shear.setBounds(s); } public void setShear(float x, float y, float w, float h) { allocateShear(); this._shear.setBounds(x, y, w, h); } public void setClip(float x, float y, float w, float h) { setShear(x, y, w, h); } public boolean isDeform() { return _deform; } public void setDeform(boolean d) { this._deform = d; } @Override public void setParent(ISprite s) { if (s instanceof IEntity) { setParent((IEntity) s); } else if (s instanceof ISprite) { setParent(new SpriteToEntity(s)); } } public Origin getOrigin() { return _origin; } public void setOrigin(Origin o) { this._origin = o; } public boolean isStopUpdate() { return _stopUpdate; } public Entity setStopUpdate(boolean s) { this._stopUpdate = s; return this; } @Override public Entity setFlipX(boolean x) { this._flipX = x; return this; } @Override public Entity setFlipY(boolean y) { this._flipY = y; return this; } @Override public Entity 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 void toString(final StringBuilder s) { s.append(this.getClass().getSimpleName()); if ((this._childrens != null) && (this._childrens.size > 0)) { s.append(" ["); final TArray<IEntity> entities = this._childrens; for (int i = 0; i < entities.size; i++) { entities.get(i).toString(s); if (i < (entities.size - 1)) { s.append(", "); } } s.append("]"); } } @Override public String toString() { final StringBuilder stringBuilder = new StringBuilder(); this.toString(stringBuilder); return stringBuilder.toString(); } public ActionTween selfAction() { return PlayerUtils.set(this); } public boolean isActionCompleted() { return PlayerUtils.isActionCompleted(this); } @Override protected void finalize() throws Throwable { super.finalize(); if (!isDisposed()) { this.close(); } } @Override public int size() { return _childrens == null ? 0 : _childrens.size; } @Override public void clear() { if (_childrens != null) { removeChildren(); } } @Override public boolean isEmpty() { return (_childrens == null ? true : _childrens.size == 0); } public Vector2f getOffset() { return _offset; } public void setOffset(float x, float y) { this._offset.set(x, y); } public void setOffset(Vector2f offset) { this._offset = offset; } public float getOffsetX() { return _offset.x; } public void setOffsetX(float offsetX) { this._offset.setX(offsetX); } public float getOffsetY() { return _offset.y; } public void setOffsetY(float offsetY) { this._offset.setY(offsetY); } protected float drawX(float offsetX) { return offsetX + this._location.x + _offset.x; } protected float drawY(float offsetY) { return offsetY + this._location.y + _offset.y; } @Override public void close() { if (!isDisposed()) { if (_image != null) { _image.close(); _image = null; } } _stopUpdate = false; setState(State.DISPOSED); removeChildren(); removeActionEvents(this); } }