/** * Copyright 2008 - 2012 * * 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.3.3 */ package loon.action.sprite; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import loon.LInput; import loon.LKey; import loon.LSystem; import loon.Touch; import loon.action.map.Config; import loon.action.map.TileMap; import loon.action.sprite.node.DefinitionReader; import loon.action.sprite.node.LNAction; import loon.action.sprite.node.LNNode; import loon.core.LObject; import loon.core.event.ActionKey; import loon.core.geom.AABB; import loon.core.geom.RectBox; import loon.core.geom.Vector2f; import loon.core.graphics.Screen; import loon.core.graphics.opengl.GLEx; import loon.core.timer.LTimerContext; import loon.physics.PBody; import loon.physics.PPhysManager; import loon.physics.PShape; import loon.physics.PWorldBox; import loon.utils.CollectionUtils; import loon.utils.MathUtils; import loon.utils.collection.ArrayMap; /** * 该类为0.3.3版最新增加的Screen类,图形渲染使用单一的SpriteBatch,相较于使用GLEx,更适合多纹理渲染。 * */ public abstract class SpriteBatchScreen extends Screen implements Config { private int keySize = 0; private float objX = 0, objY = 0; private ArrayMap keyActions = new ArrayMap(CollectionUtils.INITIAL_CAPACITY); private SpriteBatch batch; private ArrayList<SpriteBatchObject> objects; private ArrayList<SpriteBatchObject> pendingAdd; private ArrayList<SpriteBatchObject> pendingRemove; private ArrayList<TileMap> tiles = new ArrayList<TileMap>(10); private Vector2f offset = new Vector2f(); private LObject follow; private TileMap indexTile; private LNNode content; private LNNode modal; private LNNode hoverNode; private LNNode selectedNode; private LNNode[] clickNode = new LNNode[1]; private boolean isClicked; protected UpdateListener updateListener; private boolean usePhysics = false; private PPhysManager _manager; private PWorldBox _box; private boolean _fixed = false; private float _dt = 1F / 60F; private void limitWorld(boolean _fixed) { if (_fixed) { if (this._box == null) { this._box = new PWorldBox(_manager, 0f, 0f, getWidth(), getHeight()); } if (_physicsRect != null) { this._box.set(_physicsRect.x, _physicsRect.y, _physicsRect.width, _physicsRect.height); } this._box.build(); } else { if (_box != null) { this._box.removeWorld(); } } } public PPhysManager getPhysicsManager() { if (!usePhysics) { throw new RuntimeException("You do not set the physics engine !"); } return _manager; } public boolean isPhysics() { return usePhysics; } private RectBox _physicsRect; public void setPhysicsRect(float x, float y, float w, float h) { if (this._physicsRect == null) { this._physicsRect = new RectBox(x, y, w, h); } else { this._physicsRect.setBounds(x, y, w, h); } } public void setPhysics(boolean fix, PPhysManager man) { this._manager = man; this._fixed = fix; this.limitWorld(_fixed); this.usePhysics = true; } public void setPhysics(boolean fix, float scale, float gx, float gy) { if (_manager == null) { this._manager = new PPhysManager(scale, gx, gy); } else { this._manager.scale = scale; this._manager.gravity.set(gx, gy); } this._manager.setEnableGravity(true); this._manager.setStart(true); this._fixed = fix; this.limitWorld(_fixed); this.usePhysics = true; } public void setPhysics(boolean fix) { setPhysics(fix, 10F); } public void setPhysics(boolean fix, float scale) { if (_manager == null) { this._manager = new PPhysManager(scale); } else { this._manager.scale = scale; } this._manager.setEnableGravity(true); this._manager.setStart(true); this._fixed = fix; this.limitWorld(_fixed); this.usePhysics = true; } public float getTimeStep() { return this._dt; } public void setTimeStep(float dt) { this._dt = dt; } @Override public void onResume() { if (usePhysics) { _manager.setStart(true); _manager.setEnableGravity(true); } } @Override public void onPause() { if (usePhysics) { _manager.setStart(false); _manager.setEnableGravity(false); } } public boolean isFixed() { return _fixed; } public interface UpdateListener { public void act(SpriteBatchObject obj, long elapsedTime); } public synchronized void loadNodeDef(String resName) { DefinitionReader.get().load(resName); } public synchronized void loadNodeDef(InputStream res) { DefinitionReader.get().load(res); } public SpriteBatchScreen() { super(); this.objects = new ArrayList<SpriteBatchObject>(10); this.pendingAdd = new ArrayList<SpriteBatchObject>(10); this.pendingRemove = new ArrayList<SpriteBatchObject>(10); this.init(); } public SpriteBatch getSpriteBatch() { return batch; } private void init() { setNode(new LNNode(this, LSystem.screenRect)); } public void setNode(LNNode node) { if (content == node) { return; } this.content = node; } public LNNode node() { return content; } public int size() { return content == null ? 0 : content.getNodeCount(); } public void runAction(LNAction action) { if (content != null) { content.runAction(action); } } public void addNode(LNNode node) { addNode(node, 0); } public void add(LNNode node) { addNode(node, 0); } public void addNode(LNNode node, int z) { if (node == null) { return; } this.content.addNode(node, z); this.processTouchMotionEvent(); } public int removeNode(LNNode node) { int removed = this.removeNode(this.content, node); if (removed != -1) { this.processTouchMotionEvent(); } return removed; } public int removeNode(Class<? extends LNNode> clazz) { int removed = this.removeNode(this.content, clazz); if (removed != -1) { this.processTouchMotionEvent(); } return removed; } private int removeNode(LNNode container, LNNode node) { int removed = container.removeNode(node); LNNode[] nodes = container.childs; int i = 0; while (removed == -1 && i < nodes.length - 1) { if (nodes[i].isContainer()) { removed = this.removeNode(nodes[i], node); } i++; } return removed; } private int removeNode(LNNode container, Class<? extends LNNode> clazz) { int removed = container.removeNode(clazz); LNNode[] nodes = container.childs; int i = 0; while (removed == -1 && i < nodes.length - 1) { if (nodes[i].isContainer()) { removed = this.removeNode(nodes[i], clazz); } i++; } return removed; } private void processEvents() { this.processTouchMotionEvent(); if (this.hoverNode != null && this.hoverNode.isEnabled()) { this.processTouchEvent(); } if (this.selectedNode != null && this.selectedNode.isEnabled()) { this.processKeyEvent(); } } private void processTouchMotionEvent() { if (this.hoverNode != null && this.hoverNode.isEnabled() && Touch.isDrag()) { if (getTouchDY() != 0 || getTouchDY() != 0) { this.hoverNode.processTouchDragged(); } } else { if (Touch.isDrag() || Touch.isMove() || Touch.isDown()) { LNNode node = this.findNode(getTouchX(), getTouchY()); if (node != null) { this.hoverNode = node; } } } } private void processTouchEvent() { int pressed = getTouchPressed(), released = getTouchReleased(); if (pressed > LInput.NO_BUTTON) { if (!isClicked) { this.hoverNode.processTouchPressed(); } this.clickNode[0] = this.hoverNode; if (this.hoverNode.isFocusable()) { if ((pressed == Touch.TOUCH_DOWN || pressed == Touch.TOUCH_UP) && this.hoverNode != this.selectedNode) { this.selectNode(this.hoverNode); } } } if (released > LInput.NO_BUTTON) { if (!isClicked) { this.hoverNode.processTouchReleased(); } } this.isClicked = false; } private void processKeyEvent() { if (getKeyPressed() != LInput.NO_KEY) { this.selectedNode.keyPressed(); } if (getKeyReleased() != LInput.NO_KEY && this.selectedNode != null) { this.selectedNode.processKeyReleased(); } } public LNNode findNode(int x, int y) { if (content == null) { return null; } if (this.modal != null && !this.modal.isContainer()) { return content.findNode(x, y); } LNNode panel = (this.modal == null) ? this.content : (this.modal); LNNode node = panel.findNode(x, y); return node; } public void clearFocus() { this.deselectNode(); } void deselectNode() { if (this.selectedNode == null) { return; } this.selectedNode.setSelected(false); this.selectedNode = null; } public boolean selectNode(LNNode node) { if (!node.isVisible() || !node.isEnabled() || !node.isFocusable()) { return false; } this.deselectNode(); node.setSelected(true); this.selectedNode = node; return true; } public void setNodeStat(LNNode node, boolean active) { if (!active) { if (this.hoverNode == node) { this.processTouchMotionEvent(); } if (this.selectedNode == node) { this.deselectNode(); } this.clickNode[0] = null; if (this.modal == node) { this.modal = null; } } else { this.processTouchMotionEvent(); } if (node == null) { return; } if (node.isContainer()) { LNNode[] nodes = (node).childs; int size = (node).getNodeCount(); for (int i = 0; i < size; i++) { this.setNodeStat(nodes[i], active); } } } public void clearNodesStat(LNNode[] node) { boolean checkTouchMotion = false; for (int i = 0; i < node.length; i++) { if (this.hoverNode == node[i]) { checkTouchMotion = true; } if (this.selectedNode == node[i]) { this.deselectNode(); } this.clickNode[0] = null; } if (checkTouchMotion) { this.processTouchMotionEvent(); } } final void validateContainer(LNNode container) { if (content == null) { return; } LNNode[] nodes = container.childs; int size = container.getNodeCount(); for (int i = 0; i < size; i++) { if (nodes[i].isContainer()) { this.validateContainer(nodes[i]); } } } public ArrayList<LNNode> getNodes(Class<? extends LNNode> clazz) { if (content == null) { return null; } if (clazz == null) { return null; } LNNode[] nodes = content.childs; int size = nodes.length; ArrayList<LNNode> l = new ArrayList<LNNode>(size); for (int i = size; i > 0; i--) { LNNode node = nodes[i - 1]; Class<? extends LNNode> cls = node.getClass(); if (clazz == null || clazz == cls || clazz.isInstance(node) || clazz.equals(cls)) { l.add(node); } } return l; } public LNNode getTopNode() { if (content == null) { return null; } LNNode[] nodes = content.childs; int size = nodes.length; if (size > 1) { return nodes[1]; } return null; } public LNNode getBottomNode() { if (content == null) { return null; } LNNode[] nodes = content.childs; int size = nodes.length; if (size > 0) { return nodes[size - 1]; } return null; } public void setSize(int w, int h) { if (content != null) { this.content.setSize(w, h); } } public LNNode getHoverNode() { return this.hoverNode; } public LNNode getSelectedNode() { return this.selectedNode; } public LNNode getModal() { return this.modal; } public void setModal(LNNode node) { if (node != null && !node.isVisible()) { throw new RuntimeException( "Can't set invisible node as modal node!"); } this.modal = node; } public LNNode get() { if (content != null) { return content.get(); } return null; } public void commits() { if (isClose()) { return; } final int additionCount = pendingAdd.size(); if (additionCount > 0) { for (int i = 0; i < additionCount; i++) { SpriteBatchObject object = pendingAdd.get(i); objects.add(object); } pendingAdd.clear(); } final int removalCount = pendingRemove.size(); if (removalCount > 0) { for (int i = 0; i < removalCount; i++) { SpriteBatchObject object = pendingRemove.get(i); objects.remove(object); } pendingRemove.clear(); } } public SpriteBatchObject add(SpriteBatchObject object) { pendingAdd.add(object); return object; } public SpriteBatchObject remove(SpriteBatchObject object) { pendingRemove.add(object); if (usePhysics) { unbindPhysics(object); } return object; } public void removeTileObjects() { final int count = objects.size(); final Object[] objectArray = objects.toArray(); for (int i = 0; i < count; i++) { SpriteBatchObject o = (SpriteBatchObject) objectArray[i]; pendingRemove.add(o); if (usePhysics) { unbindPhysics(o); } } pendingAdd.clear(); } public SpriteBatchObject findObject(float x, float y) { for (SpriteBatchObject o : objects) { if ((o.getX() == x && o.getY() == y) || o.getRectBox().contains(x, y)) { return o; } } return null; } public TileMap getIndexTile() { return indexTile; } public void setIndexTile(TileMap indexTile) { this.indexTile = indexTile; } public void follow(LObject o) { this.follow = o; } public final void onLoad() { if (batch == null) { batch = new SpriteBatch(3000); } content.setScreen(this); for (LNNode node : content.childs) { if (node != null) { node.onSceneActive(); } } } public final void onLoaded() { create(); } public abstract void create(); public void addActionKey(Integer keyCode, ActionKey e) { keyActions.put(keyCode, e); keySize = keyActions.size(); } public void removeActionKey(Integer keyCode) { keyActions.remove(keyCode); keySize = keyActions.size(); } public void pressActionKey(Integer keyCode) { ActionKey key = (ActionKey) keyActions.getValue(keyCode); if (key != null) { key.press(); } } public void releaseActionKey(Integer keyCode) { ActionKey key = (ActionKey) keyActions.getValue(keyCode); if (key != null) { key.release(); } } public void clearActionKey() { keyActions.clear(); keySize = 0; } public void releaseActionKeys() { keySize = keyActions.size(); if (keySize > 0) { for (int i = 0; i < keySize; i++) { ActionKey act = (ActionKey) keyActions.get(i); act.release(); } } } public void setOffset(TileMap tile, float sx, float sy) { offset.set(sx, sy); tile.setOffset(offset); } public final Vector2f getOffset() { return offset; } public void putTileMap(TileMap t) { tiles.add(t); } public void removeTileMap(TileMap t) { tiles.remove(t); } public void addTileObject(SpriteBatchObject o) { add(o); } public JumpObject addJumpObject(float x, float y, float w, float h, Animation a) { JumpObject o = null; if (indexTile != null) { o = new JumpObject(x, y, w, h, a, indexTile); } else if (tiles.size() > 0) { o = new JumpObject(x, y, w, h, a, tiles.get(0)); } else { return null; } add(o); return o; } public MoveObject addMoveObject(float x, float y, float w, float h, Animation a) { MoveObject o = null; if (indexTile != null) { o = new MoveObject(x, y, w, h, a, indexTile); } else if (tiles.size() > 0) { o = new MoveObject(x, y, w, h, a, tiles.get(0)); } else { return null; } add(o); return o; } public void removeTileObject(SpriteBatchObject o) { remove(o); } private HashMap<SpriteBatchObject, PBody> _Bodys = new HashMap<SpriteBatchObject, PBody>( CollectionUtils.INITIAL_CAPACITY); public PBody findPhysics(SpriteBatchObject o) { if (usePhysics) { PBody body = _Bodys.get(o); return body; } else { throw new RuntimeException("You do not set the physics engine !"); } } public void unbindPhysics(SpriteBatchObject o) { if (usePhysics) { PBody body = _Bodys.remove(o); if (body != null) { body.setTag(null); _manager.world.removeBody(body); } } } public PBody addPhysics(boolean fix, SpriteBatchObject o, float density) { return bindPhysics(fix, add(o), density); } public PBody addPhysics(boolean fix, SpriteBatchObject o) { return bindPhysics(fix, add(o), 1F); } public PBody addTexturePhysics(boolean fix, SpriteBatchObject o, float density) { return bindTexturePhysics(fix, add(o), density); } public PBody addTexturePhysics(boolean fix, SpriteBatchObject o) { return bindTexturePhysics(fix, add(o), 1F); } public PBody bindPhysics(boolean fix, SpriteBatchObject o, float density) { if (usePhysics) { PBody body = _manager.addBox(fix, o.getRectBox(), MathUtils.toRadians(o.getRotation()), density); body.setTag(o); _Bodys.put(o, body); return body; } else { throw new RuntimeException("You do not set the physics engine !"); } } public PBody addCirclePhysics(boolean fix, SpriteBatchObject o, float density) { return bindCirclePhysics(fix, add(o), density); } public PBody addCirclePhysics(boolean fix, SpriteBatchObject o) { return bindCirclePhysics(fix, add(o), 1F); } public PBody bindCirclePhysics(boolean fix, SpriteBatchObject o) { return bindCirclePhysics(fix, add(o), 1F); } public PBody bindCirclePhysics(boolean fix, SpriteBatchObject o, float density) { if (usePhysics) { RectBox rect = o.getRectBox(); float r = (rect.width + rect.height) / 4; PBody body = _manager.addCircle(fix, o.x(), o.y(), r, MathUtils.toRadians(o.getRotation()), density); body.setTag(o); _Bodys.put(o, body); return body; } else { throw new RuntimeException("You do not set the physics engine !"); } } public PBody bindTexturePhysics(boolean fix, SpriteBatchObject o, float density) { if (usePhysics) { PBody body = _manager.addShape(fix, o.getAnimation() .getSpriteImage(), MathUtils.toRadians(o.getRotation()), density); if (body.size() > 0) { body.inner_shapes()[0].setPosition(o.x() / _manager.scale, o.y() / _manager.scale); } body.setTag(o); _Bodys.put(o, body); return body; } else { throw new RuntimeException("You do not set the physics engine !"); } } public PBody bindTexturePhysics(boolean fix, SpriteBatchObject o) { return bindTexturePhysics(fix, o, 1F); } public PBody bindPhysics(boolean fix, SpriteBatchObject o) { return bindPhysics(fix, o, 1F); } public PBody bindPhysics(PBody body, SpriteBatchObject o) { if (usePhysics) { body.setTag(o); _manager.addBody(body); _Bodys.put(o, body); return body; } else { throw new RuntimeException("You do not set the physics engine !"); } } public final void alter(LTimerContext timer) { for (int i = 0; i < keySize; i++) { ActionKey act = (ActionKey) keyActions.get(i); if (act.isPressed()) { act.act(elapsedTime); if (act.isReturn) { return; } } } if (content.isVisible()) { processEvents(); content.updateNode(timer.getMilliseconds()); } if (usePhysics) { if (_dt < 0) { _manager.step(timer.getMilliseconds()); } else { _manager.step(_dt); } } if (follow != null) { if (usePhysics) { _manager.offset(follow.getX(), follow.getY()); } for (TileMap tile : tiles) { float offsetX = getHalfWidth() - follow.getX(); offsetX = MathUtils.min(offsetX, 0); offsetX = MathUtils.max(offsetX, getWidth() - tile.getWidth()); float offsetY = getHalfHeight() - follow.getY(); offsetY = MathUtils.min(offsetY, 0); offsetY = MathUtils .max(offsetY, getHeight() - tile.getHeight()); setOffset(tile, offsetX, offsetY); tile.update(elapsedTime); } } for (SpriteBatchObject o : objects) { if (usePhysics) { PBody body = _Bodys.get(o); if (body != null) { PShape shape = body.inner_shapes()[0]; final float rotation = (shape.getAngle() * MathUtils.RAD_TO_DEG) % 360; AABB aabb = shape.getAABB(); o.setLocation(_manager.getScreenX(aabb.minX), _manager.getScreenY(aabb.minY)); o.setRotation(rotation); } } o.update(elapsedTime); if (updateListener != null) { updateListener.act(o, elapsedTime); } } update(elapsedTime); commits(); } public final void draw(GLEx g) { if (isOnLoadComplete()) { batch.begin(); before(batch); for (TileMap tile : tiles) { tile.draw(g, batch, offset.x(), offset.y()); } for (SpriteBatchObject o : objects) { objX = o.getX() + offset.x; objY = o.getY() + offset.y; if (contains(objX, objY)) { o.draw(batch, offset.x, offset.y); } } if (content.isVisible()) { content.drawNode(batch); } after(batch); batch.end(); } } public abstract void after(SpriteBatch batch); public abstract void before(SpriteBatch batch); public final void onKeyDown(LKey e) { keySize = keyActions.size(); if (keySize > 0) { int keyCode = e.getKeyCode(); for (int i = 0; i < keySize; i++) { Integer code = (Integer) keyActions.getKey(i); if (code == keyCode) { ActionKey act = (ActionKey) keyActions.getValue(code); act.press(); } } } press(e); } public abstract void press(LKey e); public final void onKeyUp(LKey e) { keySize = keyActions.size(); if (keySize > 0) { int keyCode = e.getKeyCode(); for (int i = 0; i < keySize; i++) { Integer code = (Integer) keyActions.getKey(i); if (code == keyCode) { ActionKey act = (ActionKey) keyActions.getValue(code); act.release(); } } } release(e); } public abstract void release(LKey e); public abstract void update(long elapsedTime); public abstract void close(); public void setAutoDestory(final boolean a) { super.setAutoDestory(a); if (content != null) { content.setAutoDestroy(a); } } public boolean isAutoDestory() { if (content != null) { return content.isAutoDestory(); } return super.isAutoDestory(); } public final void dispose() { if (usePhysics) { _manager.setStart(false); _manager.setEnableGravity(false); _Bodys.clear(); } this.keySize = 0; if (batch != null) { batch.dispose(); batch = null; } if (content != null) { content.dispose(); content = null; } if (indexTile != null) { indexTile.dispose(); indexTile = null; } if (objects != null) { objects.clear(); objects = null; } if (pendingAdd != null) { pendingAdd.clear(); pendingAdd = null; } if (pendingRemove != null) { pendingRemove.clear(); pendingRemove = null; } tiles.clear(); close(); } }