Search code examples
actionscript-3adobe-animate

How to make a class file apply an eventhandler to instance or object on main stage asides from itself?


I'm pretty new with ActionScript 3 and I'm stuck with trying to perform the following above in a class file. I tried to look for a solution online, but I can't find a suitable answers, perhaps because I'm not looking for the right search terms, etc.

Anyway, I'm trying to make a basic animation in Adobe Animate CC in an FLA file, "Campfire.FLA", where pressing the mouse down on a Campfire causes a piece of Coal attaching to a levitating stick to glow, and cooldown upon letting go of the mouse button. On the main timeline, I can execute it fine, but I want to transfer the information to a class file/ document file, but to no avail.

The code is what I used on the FLA's main timeline, Frame 1, and it works below works perfectly fine:

stop();
/* Instance names:
    Fire = Instance of "FireButton"; simplebutton.
    STween = Instance of "Stick Tween"; MovieClip, simple tween animation the object, "MarshmallowStick" moving.
    Stick = Instance of "Marshmallow Stick"; MovieClip.
    CoalRock = Instance of "Coal"; MovieClip.
*/
Fire.addEventListener(MouseEvent.MOUSE_DOWN, RockHot)
function RockHot(e: MouseEvent): void {
    stopPlayReverse();
    // Causes Coal and Stick to play their animation upon clicking Fire.
    STween.Stick.play();
    STween.Stick.CoalRock.play();

}
Fire.addEventListener(MouseEvent.MOUSE_UP, RockCold)
function RockCold(e: MouseEvent): void {
    STween.Stick.CoalRock.addEventListener(Event.ENTER_FRAME, playReverse, false, 0, true);
    STween.Stick.gotoAndPlay(1);
    // Upon letting go of mouse button, it causes the coal to cool down/ play reverse. Stick resets to Frame 1.

}
function playReverse(e: Event): void {
    if (STween.Stick.CoalRock.currentFrame == 1) {
        stopPlayReverse();
    // If Coal is back on Frame 1, it stops.
    } else {
        STween.Stick.CoalRock.prevFrame();
    // If Coal is not on Frame 1 it continues going reverse where it left off.
    }
}

function stopPlayReverse(): void {
    if (STween.Stick.CoalRock.hasEventListener(Event.ENTER_FRAME)) {
        STween.Stick.CoalRock.removeEventListener(Event.ENTER_FRAME, playReverse);
    // Stops the function playreverse()
    }
}

However, when trying to migrate the information into an ActionScript File I ran into a couple of problems. First I tried making an ActionScript 3 class file for each of the objects above much of the information is blank because I had no idea how to communicate eventhandlers between them. Much of the information for the MovieClips have no information, "MarshmallowStick" is below:

package  {

    import flash.display.MovieClip;


    public class MarshmallowStick extends MovieClip {


        public function MarshmallowStick() {
               // Empty, no constructor code.
        }
    }

}

For the "Fire" Class file I tried something like:

package {

    import flash.display.*;
    import flash.events.*;
    import Coal;


    public class FireButton extends SimpleButton {
        public var CoalRock = Coal;
        public function FireButton() {
        Coalrock = new Coal ();
        this.addEventListener(MouseEvent.CLICK, RockHot)
            function RockHot(e: MouseEvent): void {
                CoalRock.play();
                trace("OK");
                trace(CoalRock);
            }
        }
    }
}

However, it turned out that upon testing, The file only appeared to create a new object named CoalRock, and is not related to the one on the mainstage. So clicking the FireButton causes only the new object to play.

I tried making a document class as seen below in a file named "Main.as":

package {

    import flash.display.*;
    import flash.events.*;


    public class Main extends MovieClip {
        public var Fire: FireButton;
        public var CoalRock: Coal;
        public var Stick: MarshmallowStick;
        public var STween: StickTween;

        public function Main() {
            CoalRock = new Coal();
            Fire = new FireButton();
            Stick = new MarshmallowStick();
/*          
            addChild(Fire);
            addChild(CoalRock);
            addChild(Stick);
            addChild(STween);
*/
// RIP, well it's pretty much the same code as above. Just without the nested symbols/ objects.
            Fire.addEventListener(MouseEvent.MOUSE_DOWN, RockHot)
            function RockHot(e: MouseEvent): void {
                stopPlayReverse();
                //Eye + Emblem glow
                Stick.play();
                CoalRock.play();
                trace("OK");

            }
            Fire.addEventListener(MouseEvent.MOUSE_UP, RockCold)
            function RockCold(e: MouseEvent): void {
                CoalRock.addEventListener(Event.ENTER_FRAME, playReverse, false, 0, true);
                Stick.gotoAndPlay(1);
            }
            function playReverse(e: Event): void {
                if (CoalRock.currentFrame == 1) {
                    stopPlayReverse();
                } else {
                    CoalRock.prevFrame();
                }
            }

            function stopPlayReverse(): void {
                if (CoalRock.hasEventListener(Event.ENTER_FRAME)) {
                    CoalRock.removeEventListener(Event.ENTER_FRAME, playReverse);
                }
            }
        }
    }

}

But it only turned out that it only affects objects added via the addChild() well as far as I have tested. But the main point of this is for the script to affect objects that already exist on the main stage/ scene.

If you want to see how it plays/ suppose to play out, you can take the main timeline code and paste it into an FLA file with instances of the ones provided.

I don't know how Stack will format it. / / is suppose to be multi-line comments.


Solution

  • That's one elaborate question.

    The thing you (probably) need the most is some feudal hierarchy so that objects tend to their own and their direct children, but not deeper and totally not upwards:

    • Container → (interface method) → Child → (tends to own children)
    • Container ← (status event) ← Child

    Normally, a child must not know of its parent and should communicate with status events. This way you can put such a child into any container with no risk of that child possibly reaching upwards expecting some parent structure that is not there.

    So, your problems.

    First. Accessing the objects that already exist on the main timeline. Well, the only trick is not to create new ones but to get references to the existing ones.

    package
    {
        // Imports.
    
        public class Main extends MovieClip
        {
            // If you have an instance of MarshmallowStick with the
            // instance name "Stick" on the main timeline,
            // you don't need to do anything else.
            //
            // Default publish settings are to auto-declare
            // the main timeline instance into the
            // appropriately named variable.
    
            public var Stick:MarshmallowStick;
    

    P.S. Figure the Stick out of Tween by yourself.

    Second. To access Coal inside Stick you need to declare an appropriate variable inside the correspondent class. Also, it makes a lot of sense to put the things needed to play things forward and backward as close to the objects they operate as it is possible.

    package
    {
        import flash.events.Event;
        import flash.display.MovieClip;
    
        public class MarshmallowStick extends MovieClip
        {
            // You don't seem to need a separate class for Coal.
            // Having it as a regular MovieClip will suffice.
            public var Coal:MovieClip;
    
            // Call this to play animation forward.
            public function playNormal():void
            {
                removeEventListener(Event.ENTER_FRAME, onReverse);
    
                gotoAndPlay(1);
                Coal.play();
            }
    
            // Call this to play animation backward.
            public function playReverse():void
            {
                Coal.stop();
                gotoAndPlay(1);
    
                addEventListener(Event.ENTER_FRAME, onReverse);
            }
    
            // This method does not need to be seen from the outside
            // so declare it as private rather than public.
            private function onReverse(e:Event):void
            {
                if (Coal.currentFrame > 1)
                {
                    Coal.prevFrame();
                }
                else
                {
                    playNormal();
                }
            }
    
            // Call this to stop playing into any direction.
            public function stopPlaying():void
            {
                stop();
                Coal.stop();
    
                removeEventListener(Event.ENTER_FRAME, onReverse);
            }
        }
    }
    

    Third. Avoid declaring functions inside functions. You can read up what closures are and how to handle them in AS3, but for the time being (and probably for the foreseeable future) you won't need them.

    package
    {
        // Imports.
    
        public class Main extends MovieClip
        {
            public var Fire:SimpleButton;
            public var Stick:MarshmallowStick;
    
            public function Main()
            {
                Fire.addEventListener(MouseEvent.MOUSE_DOWN, makeHot);
    
                // The rest of the code.
            }
    
            function makeHot(e:MouseEvent):void
            {
                Stick.playNormal();
                trace("OK");
            }
    

    Fourth. Do not subclass buttons. It is pretty enough to create a simple class-free button and subscribe to its MouseEvent.CLICK or MouseEvent.MOUSE_DOWN events to process all the necessary actions in some place that knows what this buttons is for. I said that above already. Feudal hierarchy. Button should fire an event, then its holder should capture that event and react. The button itself should not know where it is or what its purpose is, even less to try doing things.