Skip to content

Instantly share code, notes, and snippets.

@moly
Created August 26, 2012 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moly/3479809 to your computer and use it in GitHub Desktop.
Save moly/3479809 to your computer and use it in GitHub Desktop.
FlxQuadTree and FlxList modified to use object pooling. Improves performance by cutting out many unnecessary new calls.
package org.flixel.system
{
import org.flixel.FlxG;
import org.flixel.FlxObject;
/**
* A miniature linked list class.
* Useful for optimizing time-critical or highly repetitive tasks!
* See <code>FlxQuadTree</code> for how to use it, IF YOU DARE.
*/
public class FlxList
{
/**
* Stores a reference to a <code>FlxObject</code>.
*/
public var object:FlxObject;
/**
* Stores a reference to the next link in the list.
*/
public var next:FlxList;
/**
* A pool to prevent repeated <code>new</code> calls.
*/
static public var listPool:ObjectPool = new ObjectPool(FlxList);
/**
* Creates a new link, and sets <code>object</code> and <code>next</code> to <code>null</code>.
*/
public function FlxList()
{
object = null;
next = null;
}
/**
* Clean up memory.
*/
public function destroy():void
{
object = null;
if(next != null)
next.destroy();
next = null;
listPool.disposeObject(this);
}
}
}
package org.flixel.system
{
import org.flixel.FlxBasic;
import org.flixel.FlxGroup;
import org.flixel.FlxObject;
import org.flixel.FlxRect;
/**
* A fairly generic quad tree structure for rapid overlap checks.
* FlxQuadTree is also configured for single or dual list operation.
* You can add items either to its A list or its B list.
* When you do an overlap check, you can compare the A list to itself,
* or the A list against the B list. Handy for different things!
*/
public class FlxQuadTree extends FlxRect
{
/**
* Flag for specifying that you want to add an object to the A list.
*/
static public const A_LIST:uint = 0;
/**
* Flag for specifying that you want to add an object to the B list.
*/
static public const B_LIST:uint = 1;
/**
* Controls the granularity of the quad tree. Default is 6 (decent performance on large and small worlds).
*/
static public var divisions:uint;
/**
* Whether this branch of the tree can be subdivided or not.
*/
protected var _canSubdivide:Boolean;
/**
* Refers to the internal A and B linked lists,
* which are used to store objects in the leaves.
*/
protected var _headA:FlxList;
/**
* Refers to the internal A and B linked lists,
* which are used to store objects in the leaves.
*/
protected var _tailA:FlxList;
/**
* Refers to the internal A and B linked lists,
* which are used to store objects in the leaves.
*/
protected var _headB:FlxList;
/**
* Refers to the internal A and B linked lists,
* which are used to store objects in the leaves.
*/
protected var _tailB:FlxList;
/**
* Internal, governs and assists with the formation of the tree.
*/
static protected var _min:uint;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _northWestTree:FlxQuadTree;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _northEastTree:FlxQuadTree;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _southEastTree:FlxQuadTree;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _southWestTree:FlxQuadTree;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _leftEdge:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _rightEdge:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _topEdge:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _bottomEdge:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _halfWidth:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _halfHeight:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _midpointX:Number;
/**
* Internal, governs and assists with the formation of the tree.
*/
protected var _midpointY:Number;
/**
* Internal, used to reduce recursive method parameters during object placement and tree formation.
*/
static protected var _object:FlxObject;
/**
* Internal, used to reduce recursive method parameters during object placement and tree formation.
*/
static protected var _objectLeftEdge:Number;
/**
* Internal, used to reduce recursive method parameters during object placement and tree formation.
*/
static protected var _objectTopEdge:Number;
/**
* Internal, used to reduce recursive method parameters during object placement and tree formation.
*/
static protected var _objectRightEdge:Number;
/**
* Internal, used to reduce recursive method parameters during object placement and tree formation.
*/
static protected var _objectBottomEdge:Number;
/**
* Internal, used during tree processing and overlap checks.
*/
static protected var _list:uint;
/**
* Internal, used during tree processing and overlap checks.
*/
static protected var _useBothLists:Boolean;
/**
* Internal, used during tree processing and overlap checks.
*/
static protected var _processingCallback:Function;
/**
* Internal, used during tree processing and overlap checks.
*/
static protected var _notifyCallback:Function;
/**
* Internal, used during tree processing and overlap checks.
*/
static protected var _iterator:FlxList;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _objectHullX:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _objectHullY:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _objectHullWidth:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _objectHullHeight:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _checkObjectHullX:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _checkObjectHullY:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _checkObjectHullWidth:Number;
/**
* Internal, helpers for comparing actual object-to-object overlap - see <code>overlapNode()</code>.
*/
static protected var _checkObjectHullHeight:Number;
/**
* A pool to prevent repeated <code>new</code> calls.
*/
static public var quadTreePool:ObjectPool = new ObjectPool(FlxQuadTree);
protected var _listPool:ObjectPool;
/**
* Instantiate a new Quad Tree node.
*
* @param X The X-coordinate of the point in space.
* @param Y The Y-coordinate of the point in space.
* @param Width Desired width of this node.
* @param Height Desired height of this node.
* @param Parent The parent branch or node. Pass null to create a root.
*/
public function init(X:Number, Y:Number, Width:Number, Height:Number, Parent:FlxQuadTree=null):void
{
_listPool = FlxList.listPool;
make(X,Y,Width,Height);
_headA = _tailA = _listPool.getNew();
_headB = _tailB = _listPool.getNew();
//Copy the parent's children (if there are any)
if(Parent != null)
{
var iterator:FlxList;
var ot:FlxList;
if(Parent._headA.object != null)
{
iterator = Parent._headA;
while(iterator != null)
{
if(_tailA.object != null)
{
ot = _tailA;
_tailA = _listPool.getNew();
ot.next = _tailA;
}
_tailA.object = iterator.object;
iterator = iterator.next;
}
}
if(Parent._headB.object != null)
{
iterator = Parent._headB;
while(iterator != null)
{
if(_tailB.object != null)
{
ot = _tailB;
_tailB = _listPool.getNew();
ot.next = _tailB;
}
_tailB.object = iterator.object;
iterator = iterator.next;
}
}
}
else
_min = (width + height)/(2*divisions);
_canSubdivide = (width > _min) || (height > _min);
//Set up comparison/sort helpers
_northWestTree = null;
_northEastTree = null;
_southEastTree = null;
_southWestTree = null;
_leftEdge = x;
_rightEdge = x+width;
_halfWidth = width/2;
_midpointX = _leftEdge+_halfWidth;
_topEdge = y;
_bottomEdge = y+height;
_halfHeight = height/2;
_midpointY = _topEdge+_halfHeight;
}
/**
* Clean up memory.
*/
public function destroy():void
{
_headA.destroy();
_headA = null;
//_tailA.destroy();
_tailA = null;
_headB.destroy();
_headB = null;
//_tailB.destroy();
_tailB = null;
if(_northWestTree != null)
_northWestTree.destroy();
_northWestTree = null;
if(_northEastTree != null)
_northEastTree.destroy();
_northEastTree = null;
if(_southEastTree != null)
_southEastTree.destroy();
_southEastTree = null;
if(_southWestTree != null)
_southWestTree.destroy();
_southWestTree = null;
_object = null;
_processingCallback = null;
_notifyCallback = null;
quadTreePool.disposeObject(this);
}
/**
* Load objects and/or groups into the quad tree, and register notify and processing callbacks.
*
* @param ObjectOrGroup1 Any object that is or extends FlxObject or FlxGroup.
* @param ObjectOrGroup2 Any object that is or extends FlxObject or FlxGroup. If null, the first parameter will be checked against itself.
* @param NotifyCallback A function with the form <code>myFunction(Object1:FlxObject,Object2:FlxObject):void</code> that is called whenever two objects are found to overlap in world space, and either no ProcessCallback is specified, or the ProcessCallback returns true.
* @param ProcessCallback A function with the form <code>myFunction(Object1:FlxObject,Object2:FlxObject):Boolean</code> that is called whenever two objects are found to overlap in world space. The NotifyCallback is only called if this function returns true. See FlxObject.separate().
*/
public function load(ObjectOrGroup1:FlxBasic, ObjectOrGroup2:FlxBasic=null, NotifyCallback:Function=null, ProcessCallback:Function=null):void
{
add(ObjectOrGroup1, A_LIST);
if(ObjectOrGroup2 != null)
{
add(ObjectOrGroup2, B_LIST);
_useBothLists = true;
}
else
_useBothLists = false;
_notifyCallback = NotifyCallback;
_processingCallback = ProcessCallback;
}
/**
* Call this function to add an object to the root of the tree.
* This function will recursively add all group members, but
* not the groups themselves.
*
* @param ObjectOrGroup FlxObjects are just added, FlxGroups are recursed and their applicable members added accordingly.
* @param List A <code>uint</code> flag indicating the list to which you want to add the objects. Options are <code>A_LIST</code> and <code>B_LIST</code>.
*/
public function add(ObjectOrGroup:FlxBasic, List:uint):void
{
_list = List;
if(ObjectOrGroup is FlxGroup)
{
var i:uint = 0;
var basic:FlxBasic;
var members:Array = (ObjectOrGroup as FlxGroup).members;
var l:uint = (ObjectOrGroup as FlxGroup).length;
while(i < l)
{
basic = members[i++] as FlxBasic;
if((basic != null) && basic.exists)
{
if(basic is FlxGroup)
add(basic,List);
else if(basic is FlxObject)
{
_object = basic as FlxObject;
if(_object.exists && _object.allowCollisions)
{
_objectLeftEdge = _object.x;
_objectTopEdge = _object.y;
_objectRightEdge = _object.x + _object.width;
_objectBottomEdge = _object.y + _object.height;
addObject();
}
}
}
}
}
else
{
_object = ObjectOrGroup as FlxObject;
if(_object.exists && _object.allowCollisions)
{
_objectLeftEdge = _object.x;
_objectTopEdge = _object.y;
_objectRightEdge = _object.x + _object.width;
_objectBottomEdge = _object.y + _object.height;
addObject();
}
}
}
/**
* Internal function for recursively navigating and creating the tree
* while adding objects to the appropriate nodes.
*/
protected function addObject():void
{
//If this quad (not its children) lies entirely inside this object, add it here
if(!_canSubdivide || ((_leftEdge >= _objectLeftEdge) && (_rightEdge <= _objectRightEdge) && (_topEdge >= _objectTopEdge) && (_bottomEdge <= _objectBottomEdge)))
{
addToList();
return;
}
//See if the selected object fits completely inside any of the quadrants
if((_objectLeftEdge > _leftEdge) && (_objectRightEdge < _midpointX))
{
if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY))
{
if(_northWestTree == null)
{
_northWestTree = quadTreePool.getNew();
_northWestTree.init(_leftEdge, _topEdge, _halfWidth, _halfHeight, this);
}
_northWestTree.addObject();
return;
}
if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge))
{
if(_southWestTree == null)
{
_southWestTree = quadTreePool.getNew();
_southWestTree.init(_leftEdge, _midpointY, _halfWidth, _halfHeight, this);
}
_southWestTree.addObject();
return;
}
}
if((_objectLeftEdge > _midpointX) && (_objectRightEdge < _rightEdge))
{
if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY))
{
if(_northEastTree == null)
{
_northEastTree = quadTreePool.getNew();
_northEastTree.init(_midpointX, _topEdge, _halfWidth, _halfHeight, this);
}
_northEastTree.addObject();
return;
}
if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge))
{
if (_southEastTree == null)
{
_southEastTree = quadTreePool.getNew();
_southEastTree.init(_midpointX, _midpointY, _halfWidth, _halfHeight, this);
}
_southEastTree.addObject();
return;
}
}
//If it wasn't completely contained we have to check out the partial overlaps
if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY))
{
if (_northWestTree == null)
{
_northWestTree = quadTreePool.getNew();
_northWestTree.init(_leftEdge, _topEdge, _halfWidth, _halfHeight, this);
}
_northWestTree.addObject();
}
if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY))
{
if (_northEastTree == null)
{
_northEastTree = quadTreePool.getNew();
_northEastTree.init(_midpointX, _topEdge, _halfWidth, _halfHeight, this);
}
_northEastTree.addObject();
}
if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge))
{
if(_southEastTree == null)
{
_southEastTree = quadTreePool.getNew();
_southEastTree.init(_midpointX, _midpointY, _halfWidth, _halfHeight, this);
}
_southEastTree.addObject();
}
if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge))
{
if (_southWestTree == null)
{
_southWestTree = quadTreePool.getNew();
_southWestTree.init(_leftEdge, _midpointY, _halfWidth, _halfHeight, this);
}
_southWestTree.addObject();
}
}
/**
* Internal function for recursively adding objects to leaf lists.
*/
protected function addToList():void
{
var ot:FlxList;
if(_list == A_LIST)
{
if(_tailA.object != null)
{
ot = _tailA;
_tailA = _listPool.getNew();
ot.next = _tailA;
}
_tailA.object = _object;
}
else
{
if(_tailB.object != null)
{
ot = _tailB;
_tailB = _listPool.getNew();
ot.next = _tailB;
}
_tailB.object = _object;
}
if(!_canSubdivide)
return;
if(_northWestTree != null)
_northWestTree.addToList();
if(_northEastTree != null)
_northEastTree.addToList();
if(_southEastTree != null)
_southEastTree.addToList();
if(_southWestTree != null)
_southWestTree.addToList();
}
/**
* <code>FlxQuadTree</code>'s other main function. Call this after adding objects
* using <code>FlxQuadTree.load()</code> to compare the objects that you loaded.
*
* @return Whether or not any overlaps were found.
*/
public function execute():Boolean
{
var overlapProcessed:Boolean = false;
var iterator:FlxList;
if(_headA.object != null)
{
iterator = _headA;
while(iterator != null)
{
_object = iterator.object;
if(_useBothLists)
_iterator = _headB;
else
_iterator = iterator.next;
if( _object.exists && (_object.allowCollisions > 0) &&
(_iterator != null) && (_iterator.object != null) &&
_iterator.object.exists &&overlapNode())
{
overlapProcessed = true;
}
iterator = iterator.next;
}
}
//Advance through the tree by calling overlap on each child
if((_northWestTree != null) && _northWestTree.execute())
overlapProcessed = true;
if((_northEastTree != null) && _northEastTree.execute())
overlapProcessed = true;
if((_southEastTree != null) && _southEastTree.execute())
overlapProcessed = true;
if((_southWestTree != null) && _southWestTree.execute())
overlapProcessed = true;
return overlapProcessed;
}
/**
* An internal function for comparing an object against the contents of a node.
*
* @return Whether or not any overlaps were found.
*/
protected function overlapNode():Boolean
{
//Walk the list and check for overlaps
var overlapProcessed:Boolean = false;
var checkObject:FlxObject;
while(_iterator != null)
{
if(!_object.exists || (_object.allowCollisions <= 0))
break;
checkObject = _iterator.object;
if((_object === checkObject) || !checkObject.exists || (checkObject.allowCollisions <= 0))
{
_iterator = _iterator.next;
continue;
}
//calculate bulk hull for _object
_objectHullX = (_object.x < _object.last.x)?_object.x:_object.last.x;
_objectHullY = (_object.y < _object.last.y)?_object.y:_object.last.y;
_objectHullWidth = _object.x - _object.last.x;
_objectHullWidth = _object.width + ((_objectHullWidth>0)?_objectHullWidth:-_objectHullWidth);
_objectHullHeight = _object.y - _object.last.y;
_objectHullHeight = _object.height + ((_objectHullHeight>0)?_objectHullHeight:-_objectHullHeight);
//calculate bulk hull for checkObject
_checkObjectHullX = (checkObject.x < checkObject.last.x)?checkObject.x:checkObject.last.x;
_checkObjectHullY = (checkObject.y < checkObject.last.y)?checkObject.y:checkObject.last.y;
_checkObjectHullWidth = checkObject.x - checkObject.last.x;
_checkObjectHullWidth = checkObject.width + ((_checkObjectHullWidth>0)?_checkObjectHullWidth:-_checkObjectHullWidth);
_checkObjectHullHeight = checkObject.y - checkObject.last.y;
_checkObjectHullHeight = checkObject.height + ((_checkObjectHullHeight>0)?_checkObjectHullHeight:-_checkObjectHullHeight);
//check for intersection of the two hulls
if( (_objectHullX + _objectHullWidth > _checkObjectHullX) &&
(_objectHullX < _checkObjectHullX + _checkObjectHullWidth) &&
(_objectHullY + _objectHullHeight > _checkObjectHullY) &&
(_objectHullY < _checkObjectHullY + _checkObjectHullHeight) )
{
//Execute callback functions if they exist
if((_processingCallback == null) || _processingCallback(_object,checkObject))
overlapProcessed = true;
if(overlapProcessed && (_notifyCallback != null))
_notifyCallback(_object,checkObject);
}
_iterator = _iterator.next;
}
return overlapProcessed;
}
}
}
package org.flixel.system
{
/**
* ...
* @author moly
*/
internal class ObjectPool
{
protected var _objects:Array;
protected var _objectClass:Class;
public function ObjectPool(ObjectClass:Class)
{
_objectClass = ObjectClass;
_objects = new Array();
}
public function getNew():*
{
var object:* = null;
if (_objects.length > 0)
object = _objects.pop();
else
object = new _objectClass();
return object;
}
public function disposeObject(OldObject:Object):void
{
_objects.push(OldObject);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment