All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flixel.FlxGroup Maven / Gradle / Ivy

The newest version!
package org.flixel;

import java.util.Comparator;

import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.reflect.ClassReflection;

/**
 * This is an organizational class that can update and render a bunch of FlxBasics.
 * NOTE: Although FlxGroup extends FlxBasic, it will not automatically
 * add itself to the global collisions quad tree, it will only add its members.
 * 
 * @author Ka Wing Chin
 * @author Thomas Weston
 */
public class FlxGroup extends FlxBasic
{
	/**
	 * Use with sort() to sort in ascending order.
	 */
	static public final int ASCENDING = -1;
	/**
	 * Use with sort() to sort in descending order.
	 */
	static public final int DESCENDING = 1;

	/**
	 * Array of all the FlxBasics that exist in this group.
	 */
	public Array members;
	/**
	 * The number of entries in the members array.
	 * For performance and safety you should check this variable
	 * instead of members.length unless you really know what you're doing!
	 */
	public int length;

	/**
	 * Internal tracker for the maximum capacity of the group.
	 * Default is 0, or no max capacity.
	 */
	protected int _maxSize;
	/**
	 * Internal helper variable for recycling objects a la FlxEmitter.
	 */
	protected int _marker;

	/**
	 * Helper for sort.
	 */
	protected String _sortIndex;
	/**
	 * Helper for sort.
	 */
	protected int _sortOrder;

	/**
	 * Constructor
	 */
	public FlxGroup(int MaxSize)
	{
		super();
		members = new Array(MaxSize);
		length = 0;
		_maxSize = MaxSize;
		_marker = 0;
		_sortIndex = null;
	}

	/**
	 * Constructor
	 */
	public FlxGroup()
	{
		this(0);
	}

	/**
	 * Override this function to handle any deleting or "shutdown" type operations you might need,
	 * such as removing traditional Flash children like Sprite objects.
	 */
	@Override
	public void destroy()
	{
		if(members != null)
		{
			FlxBasic basic;
			int i = 0;
			while(i < length)
			{
				basic = members.get(i++);
				if(basic != null)
					basic.destroy();
			}
			members.clear();
			members = null;
		}
		_sortIndex = null;
	}

	/**
	 * Just making sure we don't increment the active objects count.
	 */
	@Override
	public void preUpdate()
	{
	}

	/**
	 * Automatically goes through and calls update on everything you added.
	 */
	@Override
	public void update()
	{
		FlxBasic basic;
		int i = 0;
		while(i < length)
		{
			basic = members.get(i++);
			if((basic != null) && basic.exists && basic.active)
			{
				basic.preUpdate();
				basic.update();
				basic.postUpdate();
			}
		}
	}

	/**
	 * Automatically goes through and calls render on everything you added.
	 */
	@Override
	public void draw()
	{
		FlxBasic basic;
		int i = 0;
		while(i < length)
		{
			basic = members.get(i++);
			if((basic != null) && basic.exists && basic.visible)
				basic.draw();
		}
	}

	/**
	 * The maximum capacity of this group.  Default is 0, meaning no max capacity, and the group can just grow.
	 */
	public int getMaxSize()
	{
		return _maxSize;
	}

	/**
	 * The maximum capacity of this group.  Default is 0, meaning no max capacity, and the group can just grow.
	 */
	public void setMaxSize(int Size)
	{
		_maxSize = Size;
		if(_marker >= _maxSize)
			_marker = 0;
		if((_maxSize == 0) || (members == null) || (_maxSize >= members.size))
			return;

		//If the max size has shrunk, we need to get rid of some objects
		FlxBasic basic;
		int i = _maxSize;
		int l = members.size;
		while(i < l)
		{
			basic = members.get(i++);
			if(basic != null)
				basic.destroy();
		}
		members.truncate(length = _maxSize);
	}

	/**
	 * Adds a new FlxBasic subclass (FlxBasic, FlxSprite, Enemy, etc) to the group.
	 * FlxGroup will try to replace a null member of the array first.
	 * Failing that, FlxGroup will add it to the end of the member array,
	 * assuming there is room for it, and doubling the size of the array if necessary.
	 * 
	 * 

WARNING: If the group has a maxSize that has already been met, * the object will NOT be added to the group!

* * @param Object The object you want to add to the group. * * @return The same FlxBasic object that was passed in. */ public FlxBasic add(FlxBasic Object) { if(Object == null) { FlxG.log("WARNING: Cannot add 'null' object to a FlxGroup."); return null; } //Don't bother adding an object twice. if(members.indexOf(Object, true) >= 0) return Object; //First, look for a null entry where we can add the object. int i = 0; int l = members.size; while(i < l) { if(members.get(i) == null) { members.set(i, Object); if(i >= length) length = i+1; return Object; } i++; } //Failing that, expand the array (if we can) and add the object. if(_maxSize > 0) { if(members.size >= _maxSize) return Object; } //If we made it this far, then we successfully grew the group, //and we can go ahead and add the object at the first open slot. members.add(Object); length = i+1; return Object; } /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * *

If you specified a maximum size for this group (like in FlxEmitter), * then recycle will employ what we're calling "rotating" recycling. * Recycle() will first check to see if the group is at capacity yet. * If group is not yet at capacity, recycle() returns a new object. * If the group IS at capacity, then recycle() just returns the next object in line.

* *

If you did NOT specify a maximum size for this group, * then recycle() will employ what we're calling "grow-style" recycling. * Recycle() will return either the first object with exists == false, * or, finding none, add a new object to the array, * doubling the size of the array if necessary.

* *

WARNING: If this function needs to create a new object, * and no object class was provided, it will return null * instead of a valid object!

* * @param ObjectClass The class type you want to recycle (e.g. FlxSprite, EvilRobot, etc). Do NOT "new" the class in the parameter! * * @return A reference to the object that was created. Don't forget to cast it back to the Class you want (e.g. myObject = (myObjectClass) myGroup.recycle(myObjectClass);). */ public FlxBasic recycle(Class ObjectClass) { FlxBasic basic; if(_maxSize > 0) { if(length < _maxSize) { if(ObjectClass == null) return null; try { return add(ClassReflection.newInstance(ObjectClass)); } catch(Exception e) { throw new RuntimeException(e); } } else { basic = members.get(_marker++); if(_marker >= _maxSize) _marker = 0; return basic; } } else { basic = getFirstAvailable(ObjectClass); if(basic != null) return basic; if(ObjectClass == null) return null; try { return add(ClassReflection.newInstance(ObjectClass)); } catch(Exception e) { throw new RuntimeException(e); } } } /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * *

If you specified a maximum size for this group (like in FlxEmitter), * then recycle will employ what we're calling "rotating" recycling. * Recycle() will first check to see if the group is at capacity yet. * If group is not yet at capacity, recycle() returns a new object. * If the group IS at capacity, then recycle() just returns the next object in line.

* *

If you did NOT specify a maximum size for this group, * then recycle() will employ what we're calling "grow-style" recycling. * Recycle() will return either the first object with exists == false, * or, finding none, add a new object to the array, * doubling the size of the array if necessary.

* *

WARNING: If this function needs to create a new object, * and no object class was provided, it will return null * instead of a valid object!

* * @param ObjectClass The class type you want to recycle (e.g. FlxSprite, EvilRobot, etc). Do NOT "new" the class in the parameter! * * @return A reference to the object that was created. Don't forget to cast it back to the Class you want (e.g. myObject = (myObjectClass) myGroup.recycle(myObjectClass);). */ public FlxBasic recycle() { return recycle(null); } /** * Removes an object from the group. * * @param Object The FlxBasic you want to remove. * @param Splice Whether the object should be cut from the array entirely or not. * * @return The removed object. */ public FlxBasic remove(FlxBasic Object,boolean Splice) { int index = members.indexOf(Object, true); if((index < 0) || (index >= members.size)) return null; if(Splice) { members.removeIndex(index); length--; } else members.set(index, null); return Object; } /** * Removes an object from the group. * * @param Object The FlxBasic you want to remove. * * @return The removed object. */ public FlxBasic remove(FlxBasic Object) { return remove(Object,false); } /** * Replaces an existing FlxBasic with a new one. * * @param OldObject The object you want to replace. * @param NewObject The new object you want to use instead. * * @return The new object. */ public FlxBasic replace(FlxBasic OldObject,FlxBasic NewObject) { int index = members.indexOf(OldObject, true); if((index < 0) || (index >= members.size)) return null; members.set(index, NewObject); return NewObject; } /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call * myGroup.sort("y",ASCENDING) at the bottom of your * FlxState.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",DESCENDING). * * @param Index The String name of the member variable you want to sort on. Default value is "y". * @param Order A FlxGroup constant that defines the sort order. Possible values are ASCENDING and DESCENDING. Default value is ASCENDING. */ public void sort(String Index,int Order) { _sortIndex = Index; _sortOrder = Order; members.sort(sortHandler); } /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call * myGroup.sort("y",ASCENDING) at the bottom of your * FlxState.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",DESCENDING). * * @param Index The String name of the member variable you want to sort on. Default value is "y". */ public void sort(String Index) { sort(Index,ASCENDING); } /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call * myGroup.sort("y",ASCENDING) at the bottom of your * FlxState.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",DESCENDING). * */ public void sort() { sort("y",ASCENDING); } /** * Go through and set the specified variable to the specified value on all members of the group. * * @param VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor". * @param Value The value you want to assign to that variable. * @param Recurse Default value is true, meaning if setAll() encounters a member that is a group, it will call setAll() on that group rather than modifying its variable. */ public void setAll(String VariableName,Object Value,boolean Recurse) { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if(basic != null) { if(Recurse && (basic instanceof FlxGroup)) ((FlxGroup) basic).setAll(VariableName,Value,Recurse); else { try { ClassReflection.getField(basic.getClass(), VariableName).set(basic, Value); } catch(Exception e) { throw new RuntimeException(e); } } } } } /** * Go through and set the specified variable to the specified value on all members of the group. * * @param VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor". * @param Value The value you want to assign to that variable. */ public void setAll(String VariableName,Object Value) { setAll(VariableName,Value,true); } /** * Go through and call the specified function on all members of the group. * Currently only works on functions that have no required parameters. * * @param FunctionName The string representation of the function you want to call on each object, for example "kill()" or "init()". * @param Recurse Default value is true, meaning if callAll() encounters a member that is a group, it will call callAll() on that group rather than calling the group's function. */ public void callAll(String FunctionName,boolean Recurse) { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if(basic != null) { if(Recurse && (basic instanceof FlxGroup)) ((FlxGroup) basic).callAll(FunctionName, Recurse); else { try { ClassReflection.getMethod(basic.getClass(), FunctionName).invoke(basic); } catch(Exception e) { throw new RuntimeException(e); } } } } } /** * Go through and call the specified function on all members of the group. * Currently only works on functions that have no required parameters. * * @param FunctionName The string representation of the function you want to call on each object, for example "kill()" or "init()". */ public void callAll(String FunctionName) { callAll(FunctionName,true); } /** * Call this function to retrieve the first object with exists == false in the group. * This is handy for recycling in general, e.g. respawning enemies. * * @param ObjectClass An optional parameter that lets you narrow the results to instances of this particular class. * * @return A FlxBasic currently flagged as not existing. */ public FlxBasic getFirstAvailable(Class ObjectClass) { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if((basic != null) && !basic.exists && ((ObjectClass == null) || (ClassReflection.isInstance(ObjectClass, basic)))) return basic; } return null; } /** * Call this function to retrieve the first object with exists == false in the group. * This is handy for recycling in general, e.g. respawning enemies. * * @return A FlxBasic currently flagged as not existing. */ public FlxBasic getFirstAvailable() { return getFirstAvailable(null); } /** * Call this function to retrieve the first index set to 'null'. * Returns -1 if no index stores a null object. * * @return An int indicating the first null slot in the group. */ public int getFirstNull() { int i = 0; int l = members.size; while(i < l) { if(members.get(i) == null) return i; else i++; } return -1; } /** * Call this function to retrieve the first object with exists == true in the group. * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. * * @return A FlxBasic currently flagged as existing. */ public FlxBasic getFirstExtant() { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if((basic != null) && basic.exists) return basic; } return null; } /** * Call this function to retrieve the first object with alive == true in the group. * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. * * @return A FlxBasic currently flagged as not dead. */ public FlxBasic getFirstAlive() { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if((basic != null) && basic.exists && basic.alive) return basic; } return null; } /** * Call this function to retrieve the first object with alive == false in the group. * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. * * @return A FlxBasic currently flagged as dead. */ public FlxBasic getFirstDead() { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if((basic != null) && !basic.alive) return basic; } return null; } /** * Call this function to find out how many members of the group are not dead. * * @return The number of FlxBasics flagged as not dead. Returns -1 if group is empty. */ public int countLiving() { int count = -1; FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if(basic != null) { if(count < 0) count = 0; if(basic.exists && basic.alive) count++; } } return count; } /** * Call this function to find out how many members of the group are dead. * * @return The number of FlxBasics flagged as dead. Returns -1 if group is empty. */ public int countDead() { int count = -1; FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if(basic != null) { if(count < 0) count = 0; if(!basic.alive) count++; } } return count; } /** * Returns a member at random from the group. * * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. * @param Length Optional restriction on the number of values you want to randomly select from. * * @return A FlxBasic from the members list. */ public FlxBasic getRandom(int StartIndex,int Length) { if(Length == 0) Length = length; return FlxU.getRandom(members,StartIndex,Length); } /** * Returns a member at random from the group. * * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. * * @return A FlxBasic from the members list. */ public FlxBasic getRandom(int StartIndex) { return getRandom(StartIndex,0); } /** * Returns a member at random from the group. * * @return A FlxBasic from the members list. */ public FlxBasic getRandom() { return getRandom(0,0); } /** * Remove all instances of FlxBasic subclass (FlxSprite, FlxBlock, etc) from the list. * WARNING: does not destroy() or kill() any of these objects! */ public void clear() { length = 0; members.clear(); } /** * Calls kill on the group's members and then on the group itself. */ @Override public void kill() { FlxBasic basic; int i = 0; while(i < length) { basic = members.get(i++); if((basic != null) && basic.exists) basic.kill(); } super.kill(); } /** * Helper function for the sort process. * * @param Obj1 The first object being sorted. * @param Obj2 The second object being sorted. * * @return An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). */ // TODO: sortHandler only works with floats protected Comparator sortHandler = new Comparator() { @Override public int compare(FlxBasic Obj1,FlxBasic Obj2) { try { if((Float) ClassReflection.getField(Obj1.getClass(), _sortIndex).get(Obj1) < (Float) ClassReflection.getField(Obj2.getClass(), _sortIndex).get(Obj2)) return _sortOrder; else if((Float) ClassReflection.getField(Obj1.getClass(), _sortIndex).get(Obj1) > (Float) ClassReflection.getField(Obj2.getClass(), _sortIndex).get(Obj2)) return -_sortOrder; } catch(Exception e) { throw new RuntimeException(e); } return 0; } }; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy