Search code examples
actionscript-2thisrootmovieclipnested-function

Problem with this and root in Actionscript 2.0


To start with, I'm not new to actionscript (AS2.0) neither am I an experienced programmer in this language and I barely use any of it's object oriented features (frankly, I've never even written a class). Anyway, This is my code


var instName;
var num=0;
setInterval(createSym,100);
function createSym(){
    instName="sym"+num++;
    this.attachMovie("sym",instName,this.getNextHighestDepth());
    eval(instName)._x=100;
    eval(instName)._y=100;  
    var t1=setInterval(moveSym,8,instName);
    function moveSym(instName){
        eval(instName)._x+=1;
    }
    var t2=setInterval(checkSym,1,instName);
    function checkSym(instName){
        if(eval(instName)._x>=600){
            clearInterval(t1);
            clearInterval(t2);
            eval(instName).removeMovieClip();
        }
    }
}

At first I thought I'm having some kind of scope problem with nested functions but I've accidentally used the same logic in another program and it works perfectly (I would also appreciate some help in understanding how runtime memory is managed in languages like this). But then I realized that this code works if I change this in this.attachMovie to _root or if I change calling technique (without changing this to _root) to


setInterval(mycaller,100);
function caller(){
     createSym();
}

I can't make out how these two ways differ from each other. Thanks for any help with this :)


Solution

  • The scope changes in AS2 with setInterval. Just to make sure, do trace(this) inside the callback and you'll see it's not the _root, as expected, but you can pass a container movieclip as an extra argument to the callback function so you can add the library items into.

    Also, the code looks complicated for no reason:

    var instName;//keep track of the new instance for each clip
    var num=0;//number of symbols
    setInterval(createSym,100);//create a symbol every 100 milliseconds
    function createSym(){
        instName="sym"+num++;//update instance name
        this.attachMovie("sym",instName,this.getNextHighestDepth());//attach a new clip
        eval(instName)._x=100;//set initial position
        eval(instName)._y=100;  
        var t1=setInterval(moveSym,8,instName);//add another interval to move the symbol
        function moveSym(instName){
            eval(instName)._x+=1;
        }
        var t2=setInterval(checkSym,1,instName);//and another inverval to check if the clip is 'outside' limits, clear intervals and remove clip
        function checkSym(instName){
            if(eval(instName)._x>=600){
                clearInterval(t1);
                clearInterval(t2);
                eval(instName).removeMovieClip();
            }
        }
    }
    

    Each person has a coding style, so there isn't a right/wrong at this point, as long as it work. Here's how I rewrote it so it would make sense for me:

    var clips:Array = [];
    var currentClips:Number = 0;
    var totalClips:Number = 100;
    setInterval(update,40,this);//interval is at 40 ms ~ 25 fps, also pass a target movie clip to attache library items into
    
    function update(targetClip:MovieClip) {
        if(currentClips < totalClips){//still need clips ?
            var clip:MovieClip = targetClip.attachMovie('sym','sym'+currentClips,targetClip.getNextHighestDepth());//add a clip
            clip._x = 100;//initialize position
            clip._y = Math.random() * 100;
            clips.push(clip);//update array and clips counter
            currentClips++;
        }
        //update existing clips
        for(var i:Number = 0 ; i < currentClips; i++) {
            clips[i]._x+=10;
            if(clips[i]._x > Stage.width) {//if a clips is outsite, remove it, update the array and counter, and another should be created instead
                clips[i].removeClip();
                clips.splice(i,1);
                currentClips--;
            }
        }
    }
    

    Notice that clips are removed and added each time they exit the stage, which might take a few resources, that could be saved if we simply reuse the same symbol by repositioning it:

    var clips:Array = [];
    var currentClips:Number = 0;
    var totalClips:Number = 100;
    setInterval(update,40,this);//interval is at 40 ms ~ 25 fps, also pass a target movie clip to attache library items into
    
    function update(targetClip:MovieClip) {
        if(currentClips < totalClips){//still need clips ?
            var clip:MovieClip = targetClip.attachMovie('sym','sym'+currentClips,targetClip.getNextHighestDepth());//add a clip
            clip._y = Math.random() * 100;//initialize position
            clips.push(clip);//update array and clips counter
            currentClips++;
        }
        //update existing clips
        for(var i:Number = 0 ; i < currentClips; i++) {
            clips[i]._x+=10;
            if(clips[i]._x > Stage.width) clips[i]._x = 0;//reuse same clips, simply update position
        }
    }
    

    Also, I noticed it doesn't look very fun as is, so I've added a variable for velocity (_vx), since MovieClip is a dynamic class and you can add properties to it at runtime. Note that this isn't a good practice. The goal was to get a bit of depth in the way the clips animate:

    var clips:Array = [];
    var currentClips:Number = 0;
    var totalClips:Number = 100;
    setInterval(update,40,this);//interval is at 40 ms ~ 25 fps, also pass a target movie clip to attache library items into
    
    function update(targetClip:MovieClip) {
        if(currentClips < totalClips){//still need clips ?
            var clip:MovieClip = targetClip.attachMovie('sym','sym'+currentClips,targetClip.getNextHighestDepth());//add a clip
            clip._y = Math.random() * 100;//initialize position
            clip._vx = 5 + Math.random() * 5;//clips have different velocities - give a bit of depth
            clips.push(clip);//update array and clips counter
            currentClips++;
        }
        //update existing clips
        for(var i:Number = 0 ; i < currentClips; i++) {
            clips[i]._x += clips[i]._vx;
            if(clips[i]._x > Stage.width) clips[i]._x = 0;//reuse same clips, simply update position
        }
    }
    

    Speaking of depth, the clips aren't depth sorted, but I diverge... Regarding your problem with _root and scope, the issue with with setInterval because scope changes, but you can use an argument to the callback to get past the issue.