1

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;

Ger
  • 102
  • 5

2 Answers2

3

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;
Organis
  • 7,243
  • 2
  • 12
  • 14
  • That's amazing! Thanks so much. I used the first solution. The Registry. I also added `static public function remove(name:String):void { delete hash[name]; } static public function purge():void{ hash = null; }` – Ger Apr 01 '17 at 14:05
  • @Ger I advise **hash = new Object;** in purge() for it will clean the Registry for the future use instead of disabling it completely. – Organis Apr 01 '17 at 15:28
  • what you mean "disabling it" ? – Paweł Audionysos Apr 01 '17 at 17:29
  • Good idea Organis. Thanks a lot :) – Ger Apr 01 '17 at 18:04
1

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.

Community
  • 1
  • 1
  • 1
    Static properties (or singleton pattern) are all good as soon as you are sure you need only one instance of something over all the application. Audio engine, server connection, etc. My solution is simple, easy to understand (and to use, which is important), runtime-efficient. Yours, no offense, is programming for the sake of programming. I was an adventurer like you once too, but then I took an arrow to the knee and these days I resort to what's simplest and fastest. Because mobile devices do not forgive inefficiency. – Organis Apr 01 '17 at 21:02
  • What arrow? What is complex in my solution? It's simple af - check if child of given container has the name that you are looking for, if not, do the same for the children of that child. I believe it's common to search branches this way and I'm not inventing anything new. Really don't know what you mean by "programming for the sake of programming". And usage it even simpler. How you say calling `findChild(stage, "childName");` is not simple or harder to use than your solutions? – Paweł Audionysos Apr 01 '17 at 22:51
  • It's a meme http://i1.kym-cdn.com/photos/images/original/000/210/566/fcd.jpg For the rest, you might be right in algorithmic sense, but... Display list search is slower than access via associative array hash, and it is getting worse with the growth of the display object tree. – Organis Apr 01 '17 at 23:29
  • Yes, I already said it could be expensive but you don't need start search form stage level. Also usually you just search this once and store result. And as for usage it is probably most convenient way of getting`desiredMc` - if you have simple square inside mc which is inside another mc and you just want to change color of it you don't need to define special class, register anything or name your parent clips. Just say `findChild(mc, "desiredMc")`. This is also good if you are working with graphic designer and you are not sure where he put your `desiredMc`... – Paweł Audionysos Apr 02 '17 at 09:59
  • and speaking about global access did you see my recent answer it the topic? http://stackoverflow.com/questions/7214881/how-to-share-an-array-between-all-classes-in-an-application/42931561#42931561 I actually have many methods like this 'findChild()` defined as static but they are not bound to any specific data and when it comes to storing individual instances in central place I'm no convince about that... – Paweł Audionysos Apr 02 '17 at 10:03
  • Thanks Pawel. I will save this to my useful codes. I think for what I need right now the Registry one works best performance wise. But if ever I had a lot of things on stage and needed to find something once I think your could would be perfect for that job. – Ger Apr 02 '17 at 10:17
  • @PawełAudionysos "Well you are doomed..." Let me remove this curse for you. Once you need to turn away from static/global to instances you use this: https://en.wikipedia.org/wiki/Dependency_injection – Organis Apr 02 '17 at 11:52
  • Don't now why you write it here... Well, I was just saying that storing global variables is generally not desired thing and you are giving some example how to not use it so I believe you agree you should not have something like Global.pawns right? – Paweł Audionysos Apr 02 '17 at 12:53
  • @PawełAudionysos There's no such thing as "generally not desired" approach **as long as you understand how it works and what are upsides and downsides**. As long as the problem is solved and meet the desired criteria it does not matter if you are summoning vile demons in your code or blow the Trumpet of Jericho. – Organis Apr 02 '17 at 13:30
  • By "generally not desired" I mean that at least I have maybe 3-5% of code defined as static because I use it only when I'm pretty sure It's not going to introduce any trouble in future. – Paweł Audionysos Apr 02 '17 at 13:37