Utility Functions For DisplayObjects
Hot on the heels of my articles about utility functions for objects and classes, today’s article has a set of utility functions for DisplayObjects.
First up is a function that nicely compliments the Timer class. Rather than waiting a given number of milliseconds/seconds, this function allows you to wait a given number of frames. This is mostly useful for delaying code until the next frame for the sake of timing or spreading processor-intensive tasks out over time. So, here is wait:
/** * Wait a given number of frames then call a callback * @param numFrames Number of frames to wait before calling the callback * @param callback Function to call once the given number of frames have passed * @author Jackson Dunstan */ public static function wait(numFrames:uint, callback:Function): void { var obj:Shape = new Shape(); obj.addEventListener( Event.ENTER_FRAME, function(ev:Event): void { numFrames--; if (numFrames == 0) { obj.removeEventListener(Event.ENTER_FRAME, arguments.callee); callback(); } } ); }
Next up is a very simple function to apply a scrollRect to an object at the object’s current size. This is very useful to apply before you add a child that may go out of the container’s bounds. So, here is a one-liner applyNaturalScrollRect:
/** * Apply a scroll rect from (0,0) to (width,height) * @param dispObj Display object to apply on * @author Jackson Dunstan */ public static function applyNaturalScrollRect(dispObj:DisplayObject): void { dispObj.scrollRect = new Rectangle(0, 0, dispObj.width, dispObj.height); }
Now a function very useful in programmer art: createRectangleShape. This simply creates a Shape that contains a simple rectangle. This may seem useless, but it really reduces typing! There’s an astonishing amount of code involved in just making a silly rectangle. I find this out every time I go to make some programmer art buttons for a test. So, stop typing and use this:
/** * Create a shape that is a simple solid color-filled rectangle * @param width Width of the rectangle * @param height Height of the rectangle * @param color Color of the rectangle * @param alpha Alpha of the rectangle * @return The created shape * @author Jackson Dunstan */ public static function createRectangleShape(width:uint, height:uint, color:uint, alpha:Number): Shape { var rect:Shape = new Shape(); var g:Graphics = rect.graphics; g.beginFill(color, alpha); g.drawRect(0, 0, width, height); g.endFill(); return rect; }
Now for a function that can really help out a lot of tasks: removeAllChildren. One major use of this function is for clearing up a DisplayObjectContainer of all its children during a destroy or dispose method whose purpose is to prepare the object for garbage collection. It’s a short function, but nevertheless nice to have encapsulated.
/** * Remove all children from a container and leave the bottom few * @param container Container to remove from * @param leave (optional) Number of bottom children to leave * @author Jackson Dunstan */ public static function removeAllChildren(container:DisplayObjectContainer, leave:int = 0): void { while (container.numChildren > leave) { container.removeChildAt(leave); } }
This next function builds on two functions from previous articles: getDisplayObjectClass and FunctionUtils.instantiate. Together you get a way to instantiate a new copy of a DisplayObject. This is really handy since you can’t simply copy or clone them normally and often want to make many copies of one without wanting to go through all the hoops to use the ApplicationDomain. So this function will do it for you:
/** * Instantiate a new instance of a certain class of display object * @param obj Display object whose class a new display object should be * instantiated of * @param args Arguments to pass to the display object's constructor * @return A new instance of the given display object's class * @author Jackson Dunstan */ public static function instantiate(obj:DisplayObject, args:Array): DisplayObject { var c:Class = ObjectUtils.getDisplayObjectClass(obj); return c == null ? null : DisplayObject(FunctionUtils.instantiate(c, args)); }
One convenient thing about DisplayObjects is that you can check stage != null to see if they are on the stage. You can also check their visibility via the visible field, but there’s no way to do the equivalent check as with stage to determine if they are absolutely visible. So this utility function will traverse the display object’s parents to see if all of them are visible:
/** * Check if a display object is visible. This checks all of its * parents' visibilities. * @param obj Display object to check * @author Jackson Dunstan */ public static function isVisible(obj:DisplayObject): Boolean { for (var cur:DisplayObject = obj; cur != null; cur = cur.parent) { if (!cur.visible) { return false; } } return true; }
Finally we come to a pair of functions that make iterating over the children and parents of a DisplayObjectContainer a whole lot simpler. They do the loop for you and form up an Array of parents or children. For getChildren, this is simple. For getParents there is more complication. You get to optionally specify whether you want the specified DisplayObjectContainer itself included and can also specify a stopping point to stop at a DisplayObjectContainer before the stage. Loops using these simply become normal loops over arrays, such as those using for-in and for-each, which can really clean up code!
/** * Get the children of a container as an array * @param container Container to get the children of * @return The children of the given container as an array * @author Jackson Dunstan */ public static function getChildren(container:DisplayObjectContainer): Array { var ret:Array = []; var numChildren:int = container.numChildren; for (var i:int = 0; i < numChildren; ++i) { ret.push(container.getChildAt(i)); } return ret; } /** * Get the parents of a display object as an array * @param obj Object whose parents should be retrieved * @param includeSelf If obj should be included in the returned array * @param stopAt Display object to stop getting parents at. Passing * null indicates that all parents should be included. * @return An array of the parents of the given display object. This * includes all parents unless stopAt is non-null. If stopAt is * non-null, it and its parents will not be included in the * returned array. * @author Jackson Dunstan */ public static function getParents(obj:DisplayObject, includeSelf:Boolean=true, stopAt:DisplayObject=null): Array { var ret:Array = []; for (var cur:DisplayObject = includeSelf ? obj : obj.parent; cur != stopAt && cur != null; cur = cur.parent ) { ret.push(cur); } return ret; }
Not included here, but also a useful method is putInFrame from a previous article. I hope you all find these utility functions as useful as I have! If any of you have any related utility functions and are OK with them being released for free public usage, I encourage you to post them in the comments.
#1 by vitaLee on December 10th, 2009 ·
#2 by jackson on December 10th, 2009 ·
I’ve added code formatting and cleaned up the HTML safety stuff that got applied. I can see some of these being very handy. Also, I really like your concise for loops over children:
Compared to a for-each over my getChildren it’s more efficient, but also more typing. Still, it’s nice to have options like these. Thanks for the contribution!
#3 by Karl Knocking on December 10th, 2009 ·
I really like the for-loop in the isVisible Function. Althoug i think i never needed such a function. ;)
One suggestion: Wouldn’t it be better to use DisplayObject or EventDispatcher in the wait-function instead of MovieClip?
Here’s a function that I often use. It let’s you find the position of one MovieClip in the coordinate system of another one:
#4 by jackson on December 10th, 2009 ·
Thanks for the tip. I actually wrote wait while I was new to AS3 and didn’t know that other DisplayObject derivatives could get the ENTER_FRAME event. It seems counter-intuitive to me, since they don’t have frames. I’ve updated the article to use a Shape since it seems like the cheapest object that will dispatch the event. Lastly, thanks for your contribution!
#5 by Matt W on December 11th, 2009 ·
#6 by jackson on December 11th, 2009 ·
getFullBounds is really interesting. I applied formatting and cleaned up some HTML safety stuff. Thanks for contributing!
#7 by Jonny Reeves on February 10th, 2010 ·
Simple but effective:
#8 by Denis on February 16th, 2010 ·
Hi Jakson,
Could you explain why did you use pre-increment in the getChildren function and not post-increment? Is it significant difference?
#9 by jackson on February 17th, 2010 ·
I don’t think it’s any faster or slower, just my habit from years of using the C++ STL where pre-incrementing an iterator is faster than post-incrementing. These days I just think it lends some visual balance to the line of code. :)
#10 by Cordia Evanoski on October 6th, 2010 ·
:O So mush Information :O … This really is he MOst Astounding Website DUDe…