Is there a better (quicker, shorter or neater) way to access a child sprite (object) 3 layers or more deep? If the children have all been given names and you know the names. Is there a better way to do it than the code I made below?
var mySprite:DisplayObjectContainer = layerASprite.getChildByName("layerA") as DisplayObjectContainer;
mySprite = mySprite.getChildByName("layerB") as DisplayObjectContainer;
mySprite.getChildByName("layerC").y = 200;
If they are unique you can create a "global" registry in a form of static class variables/methods:
package
{
public class Registry
{
static private var hash:Object = new Object;
static public function register(name:String, target:DisplayObject):void
{
hash[name] = target;
}
static public function access(name:String):DisplayObject
{
return hash[name];
}
}
}
Usage:
// Any MovieClip, frame 1 or class constructor.
import Registry;
Registry.register("deepChild", this);
// From any other place.
import Registry;
Registry.access("deepChild").y = 200;
Alternately you can use a method that digs children of children by a single string argument:
function offSpring(path:String):DisplayObject
{
var aSplit:Array = path.split(".");
var result:DisplayObject = this;
while (aSplit.length)
{
var aParent:DisplayObjectContainer = result as DisplayObjectContainer;
if (!aParent) return null;
result = aParent.getChildByName(aSplit.shift());
}
return result;
}
Usage:
offSpring("layerA.layerB.layerC").y = 200;
As I'm not a big fan of static properties so I would also propose recursive search:
public function findChild(d:DisplayObject, n:String):DisplayObject {
var dc:DisplayObjectContainer = d as DisplayObjectContainer; if (!dc) return null;
for (var i:int = 0; i < dc.numChildren; i++){
var ch:DisplayObject = dc.getChildAt(i);
if (ch.name == n || (ch = findChild(ch, n))) return ch;
}return null;
}
and than you can simply type this:
var d:DisplayObject = findChild(stage, "childName");
to find first child with childName
name anywhere on stage.
I just wrote it and tested it once, but I hope it fine.
Advantages:
- You don't need to make any additional steps to work with this method. You don't even need to name containers of child you search for.
- You can start search at any
DisplayObjectContainer
you want.
- If you decide at some point that you need you need to move your child form container
A
to container B
no change in code is needed if it's still part of the same branch and has unique name.
Disadvantages:
- It could be expensive, especially if you have extensive branch.
- You need to make sure name of your child is unique across searched branch.
More sophisticated version
As searched children could usually be direct child of given container or be on closer level you could possibly search for child one level at time though it's bit tricky. For example my alpha version:
/**
* Perform parallel/sprial recursive search in this container to find child with given name.
* This means that this function will first check names of all child of this container and and then go to deeper level.
* In other words, no element will be tested on level x+1 if not all elements were tested on level x.
* This is true for all levels until whole tree is exhausted. This method is using token argument for such functionallity.
* @param n name of child element to be found.
* @param t token passed internally for sipral search. You should not specify this if you want the function to search a whole tree.
* The token has only single value which is basically a desired level at which child should be searched for.
* Level 1 means this function will only check its own childrens, level 2 means that only childs of childs of this container will be checked and so one.
* However note that if you specify a token with some level, only that single level will be searched.
* On the other hand if given token is null, this will check childs on level 1, then level 2, 3... and it will countinue until deepest level has been reached.
* @return nearest child with specified name or null if no child with given name found.
*/
public function findChild(n:String, t:SearchToken = null, ind:String = ""):SGLElement {
ind += " ";
var r:Boolean = (t) ? false : true; //is this call root of search.
t = (t) ? t.offL( -1) : new SearchToken(0); //create new token if not given or decrement current token value.
//trace(ind + "F", this.name, ":", t);
//if (!t) t = new SearchToken(0);
//--t.l;
var cl:SearchToken = new SearchToken(t.l); //current search level.
var exc:int = 0; //exhausted childrens.
if(t.l == 0){//search own children
for (var i:int = 0; i < _childs.length; i++) { //trace(ind + " c", _childs[i].name);
if (_childs[i].name == n) return _childs[i]; }
if (r) ++cl.l; else return null;
}
while( cl.l > 0){
if (exc >= _childs.length) { t.l = -1; return null;}
for (i = 0; i < _childs.length; i++) {
//trace(ind + "ch", t,":", i, _childs[i].name, _childs[i]);
if (!(_childs[i] as SGLElementContainer)) continue;
//trace(ind + "t", t, i);
t.l = cl.l;
var e:SGLElement = SGLElementContainer(_childs[i]).findChild(n, t, ind);
//++cl.l;
if (e) return e;
else if (t.l < 0) exc++;
}
//trace(ind + "END_LEVEL>>>>", t);
if (!r) return null;
//t.l = cl.l;
++cl.l;
}
return null;
}
token class
package adnss.common.utils
{
public class SearchToken
{
/**Current level**/
public var l:int;
public function SearchToken(levelValue:int) {l = levelValue;}
public function toString():String {return String(l);}
/**Set level value and return this token instance.**/
public function setL(v:int):SearchToken { l = v; return this; }
/**Add given offset value to level value and return this token instance.**/
public function offL(v:int):SearchToken { l += v; return this;}
}
}
I note I don't know what is technical name for such search so I gave it my own name and this method is not used for display list so you would need to adapt it. It's bit hard to explain this but if you have some question about it feel free to ask.