Back to project page DolphinOES.
The source code is released under:
Apache License
If you think the Android project DolphinOES listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/******************************************************************************* * Copyright 2014 See AUTHORS file.//from w ww. j a v a2 s .c o m * * 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. ******************************************************************************/ package com.sidereal.dolphinoes.architecture; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.ObjectMap; import com.sidereal.dolphinoes.architecture.pos.Position; import com.sidereal.dolphinoes.util.DolphinOESException; /** An entity in the framework. It is added in the framework by passing the scene * we want to add it to in the constructor * {@link #GameObject(GameScene, Object...)}. * <p> * To add functionality in the form of a {@link GameBehavior}, the * {@link GameBehavior#GameBehavior(GameObject)} constructor has to be called * which will add the behavior to the passed object's {@link #behaviors}. * <p> * Additional custom functionality can be added using methods that can be * overriden such as {@link #createObject(Object...)}, {@link #update()}, * {@link #handleSceneChange()}, {@link #onResize(float, float, float, float)} * and {@link #onPositionChange()}. * <p> * To make the object exist throughout all scenes until deleted, use * {@link #isPersistent}, and to pass the object to the next scene only, use * {@link GameScene#passObjToNextScene(GameObject)} or * {@link GameScene#passListToNextScene(ArrayList)}. * <p> * The {@link GameBatch} can be set using {@link #setGameBatch(String)}, being * able to afterwards get gameBatch data from {@link #gameBatch}. * <p> * Removing the object is done using {@link GameScene#remove(GameObject)}. * * @author Claudiu Bele */ public abstract class GameObject { // region fields /** Whether or not the object is persistent through scenes, automatically * being added to the new scene. * <p> * Objects with this value equal to {@link Boolean#TRUE} will be passed to * the next scene regardless of whether or not it is found in the previous * scene's {@link GameScene#toKeepForNextScene}. * <p> * Used in {@link GameScene#GameScene()} when * processing the previous screen to retrieve the objects found inside of * it. */ protected boolean isPersistent; /** Set to false,after being added to the scene and in the scene, set to true */ protected boolean inScene; /** The variable is true from after adding to the scene until we planned on * removing it */ protected boolean enabled; /** The object's target gamebatch */ public GameBatch gameBatch; // region parent/children /** The local position of the object. It is used if the object is a child. * <p> * This acts as an offset to the parent's position, the child's position * being made by adding it's own {@link #localPosition} and the parent's * {@link #position}. */ Vector3 localPosition = new Vector3(); /** List of children. When adding a new Children, we add them to the list */ public ArrayList<GameObject> children; /** The parent of a child, assigned when adding a child to a parent. */ public GameObject parent; // endregion /** The name of the object, will be used when finding an object by name or * type. Changing this value without a setter ( using * {@link #setName(String)} ) is only useful if set in * {@link #createObject(Object...)}. */ private String name; /** The type of the object,will be used when finding an object by name or * type. Changing this value without a setter ( using * {@link #setType(String)} ) is only useful if set in * {@link #createObject(Object...)} */ private String type; /** The scene that the object is currently in */ public GameScene scene; /** The list of behaviors. Will be added by passing the object to the * constructor of a behavior */ public final List<GameBehavior> behaviors; /** Comparator used for sorting the behaviors, set based on priority by * default; */ public Comparator<GameBehavior> behaviorsComparator; /** Map of behaviors, used for retrieving behaviors by name */ public final ObjectMap<Class<? extends GameBehavior>, GameBehavior> behaviorTable; /** Object that represents the object's position. Assigned in * {@link GameObject#GameObject(GameScene, Object...)}. */ public final Position pos; /** A reference to the object. Is to be used in annonymous inner types */ public final GameObject instance; // endregion fields // region constructors /** Default GameObject constructor. * * @param scene * @param params * parameters passed to {@link #createObject(Object...)} */ public GameObject(GameScene scene, Object... params) { setEnabled(true); this.isPersistent = false; this.instance = this; this.behaviorTable = new ObjectMap<Class<? extends GameBehavior>, GameBehavior>(); this.behaviors = new ArrayList<GameBehavior>(); this.children = new ArrayList<GameObject>(); this.scene = scene; this.pos = new Position(this); this.type = (getClass().getSimpleName()); this.name = (this.getType() + " " + System.nanoTime()); this.behaviorsComparator = new Comparator<GameBehavior>() { @Override public int compare(GameBehavior o1, GameBehavior o2) { return (o1.priorityLevel - o2.priorityLevel); } }; setGameBatch("Default"); createObject(params); scene.add(this); } // endregion // region methods /** Disposes all of the assets of a {@link GameObject}, including behaviors * and all descendants. * * Method to be called when we want an object to be removed from the scene. * It will update the parent's list of children by removing the object from * the parents' list. * <p> * The method also disposes the resources of the children recursively, by * calling {@link #disposeInternal()} on all of gameobjects found in * {@link #children}. * <p> * The list of behaviors found in {@link #behaviors} is iterated over, * calling {@link GameBehavior#dispose()} in each of them. * <p> * After clearing all the data, it also calls {@link #dispose()}, which is a * method that can be customised from implementation to implementation */ final void disposeInternal() { // removes itself from the parent's list of children if the object has // parents if (parent != null) { if (parent.children != null) { synchronized (parent.children) { parent.children.remove(this); } } } // calls the same method in all of the chilldren if (children != null) { for (int i = 0; i < children.size(); i++) { children.get(i).disposeInternal(); } } // disposes all of the data that each individual gamebehavior wants to // dispose if (behaviors != null) { for (int i = 0; i < behaviors.size(); i++) { behaviors.get(i).dispose(); } behaviors.clear(); } dispose(); } /** Updates the debugging methods in each behavior attached to the GameObject * * @param info */ public final void updateDebug() { if (!enabled()) return; for (GameBehavior behavior : behaviors) { if(DolphinOES.debug.isEnabled() && DolphinOES.debug.get(behavior).enabled) { behavior.updateDebug(); } } } /** Updates the Game Object, being called every frame * <p> * If the object has a parent, it has it's position set the parent's * position to which we append the object's own {@link #localPosition} * variable of type {@link Vector2}. * <p> * The method calls the extendable method {@link #update()}, sorts the * behaviors in order of their priority value, as well as updating each of * the behaviors found in {@link #behaviors} by calling * {@link GameBehavior#update()}. */ public final void updateInternal() { if (!enabled()) return; instance.update(); Collections.sort(behaviors, behaviorsComparator); for (int i = 0; i < behaviors.size(); i++) { behaviors.get(i).updateInternal(); } } // region override-able methods /** Can add a unique event/behavior to run in the object's update thread, so * we don't have to make a new custom behavior for each class */ protected void update() { } /** Method called in {@link #GameObject(GameScene, Object...)} which can be * extended to initialize logic in individual Game Object implementations * * @param params * parameters passed from * {@link #GameObject(GameScene, Object...)} */ protected void createObject(Object... params) { } /** Called in {@link DolphinOES#handleSceneTransition()}, on the * objects that were passed from a scene to another one. */ public void handleSceneChange() { } /** Called when removing an object, in {@link #disposeInternal()}. The method * can be customized in the implementations of individual Game Objects. */ protected void dispose() { } /** Method that gets called when the screen is being resized * * @param x * the new window width * @param y * the new window height * @param oldX * old window width * @param oldY * old window height */ protected void onResize(float x, float y, float oldX, float oldY) { } /** Method that gets called whenever the position of the object is changed. * <p> * Method is called from {@link #setLocalPosition(float, float)} and * {@link #MISSING()} */ protected void onPositionChange() { } // endregion // region getters/setters public final void setGameBatch(String newTag) { if (scene == null) throw new NullPointerException("Gamescene attached to GameObject is null in setGameBatch"); for (int i = 0; i < scene.gameBatches.size(); i++) { if (scene.gameBatches.get(i).name.equals(newTag)) { if (gameBatch != null) { if (gameBatch.objects.contains(this)) gameBatch.objects.remove(this); } gameBatch = scene.gameBatches.get(i); if (!gameBatch.objects.contains(this)) { gameBatch.objects.add(this); } return; } } throw new DolphinOESException("Didn't find a GameBatch with the name equal to "+newTag); } /** Set's a Game Object's parent, updating all the object's position and * level based on parent data. * <p> * If the {@link #parent} variable is null when calling this method, the * {@link #localPosition} variable is set to {@link #position}, in order to * have the real object's position saved when updating {@link #position} to * meet the hierarchy's perspective on it. * <p> * * @param obj */ public final void setParent(GameObject obj) { // if GameObject passed is self, exit method if (obj == this) return; if (obj == null) { // if object previously had a parent and we pass a null, update the // local position of the object // to own hierarchial position. if (parent == null) return; else { // local position is hierarchial position parent = null; pos.setLocal(pos.get()); return; } } // setting the parent as an own child leads to removing the passed // parammeter // from the list of children as well as changing the child's parent to // null if (children.contains(obj)) { obj.setParent(null); children.remove(obj); } parent = obj; synchronized (obj.children) { if (obj.children == null) obj.children = new ArrayList<GameObject>(); obj.children.add(this); } // updating child position based on parent position pos.ensureHierarchialMatch(); // update child level based on parent level this.setGameBatch(obj.gameBatch.name); } // endregion misc // endregion // region name /** Default name getter * * @return */ public final String getName() { return name; } /** Changes the name of the GameObject and updates the map in which the * GameObject can be found. * <p> * If the name passed is the same as before or the scene is null, we will * not handle the change of the variable. * <p> * This method can be used in {@link #createObject(Object...)} as well as * outside of it, being an alternative to explicitly changing {@link #name}, * removing the object from its' previous place in * {@link GameScene#objectsMap } if necessary. * * @param name * The new name of the GameObject. If value passed is the same as * the old one, the method will not be handled */ public final void setName(String name) { if (this.name.equals(name)) return; if (scene == null) return; if (!scene.objectsMap.containsKey(this.getType())) { scene.objectsMap.put(this.getType(), new HashMap<String, GameObject>()); } else { if (scene.objectsMap.get(this.getType()).containsValue(this)) scene.objectsMap.get(this.getType()).remove(this.name); } scene.objectsMap.get(this.getType()).put(name, this); this.name = name; } // endregion // region type /** Default type getter * * @return */ public final String getType() { return type; } /** Changes the type of the GameObject and updates the map in which the * GameObject can be found. * <p> * If the type passed is the same as before or the scene is null, we will * not handle the change of the variable. * <p> * This method can be used in {@link #createObject(Object...)} as well as * outside of it, being an alternative to explicitly changing {@link #type}, * removing the object from its' previous place in * {@link GameScene#objectsMap } if necessary. A new Map will be created if * there isn't one already for the new GameObject type. * * @param type * The new type of the GameObject. If value passed is the same as * the old one, the method will not be handled */ public final void setType(String type) { if (this.type.equals(type)) return; if (scene == null) return; if (scene.objectsMap.containsKey(this.type) && scene.objectsMap.get(this.type).containsValue(this)) { scene.objectsMap.get(this.type).remove(this.getName()); } if (!scene.objectsMap.containsKey(type)) { scene.objectsMap.put(type, new HashMap<String, GameObject>()); } scene.objectsMap.get(type).put(this.getName(), this); this.type = type; } // endregion // region behaviors @SuppressWarnings("unchecked") public final <T extends GameBehavior> T getBehavior(Class<T> behaviorClass) { return (T) behaviorTable.get(behaviorClass); } public final boolean removeBehavior( Class<? extends GameBehavior> behaviorClass) { GameBehavior targetBehavior = getBehavior(behaviorClass); if (targetBehavior == null) return false; targetBehavior.dispose(); behaviors.remove(behaviorTable.get(behaviorClass)); behaviorTable.remove(behaviorClass); return true; } // endregion /** Returns the status of activity of a gameobject in the hierarchy. If any * of the ancestors of the gameobject is inactive, this gameobject will also * be inactive. If all of the ancestors are self-active, then it will return * the gameobject's {@link #enabled} variable. * * This return value decides whether or not to update a gameobject every * frame in {@link #updateInternal()} or not. * * @return the availability of a gameobject in a hierarchy . Us */ public final boolean enabled() { if (!isInScene()) return false; if (parent != null) { if (!parent.enabled()) return false; } return enabled; } /** Sets the availability of the gameobject. This does not set the value in * the hierarchy, and the gameobject will not update if {@link #parent} * isn't active in the hierarchy as well. * * Is set to false in {@link #GameObject(GameScene, Object...)} and when * adding the object to {@link GameScene#toRemove}. * <p> * Is set to true in {@link GameScene#addObjects}, when we add objects from * last frame to their respective {@link GameBatch} counterparts. * * @param avaiable */ public final void setEnabled(boolean avaiable) { this.enabled = avaiable; } /** Returns whether or not the user is to be removed or is in scene. * <p> * Called in {@link GameBatch#resize()} * * @return */ public final boolean isInScene() { return inScene; } public final boolean isPersistent() { if (isPersistent) return true; else if (parent != null) { return parent.isPersistent(); } return false; } // endregion // endregion }