Search code examples
actionscript-3flashoopmodel-view-controllermovieclip

MovieClip timeline animations wont play across classes - Flash Game


First question. Please be gentle.

I’m making a simple (I'd hoped) keyboard controlled movieclip of a game character. The Movieclip has five frames, each with a simple animation inside for when the player presses the arrow keys, walking up, down, stable etc.

I made a simple prototype in the main application class file and it works PERFECTLY. I also traced currentFrame and totalFrames and that is correct too. When key is pressed it plays correct frame and it says the totalFrames are 5. Great!

However I want to make a multi level game, so have used the application class as a level switcher and each level and object is its own class. During this migration the MovieClip stops working. It is added to the stage fine and can be moved with the arrow keys but the individual animations wont play. It just flickers through each of the five keyframes. Also when I trace it, it says currentFrame is 0 (?) and totalFrames is 1 (?)

It wont be controlled by .stop or .gotoAndPlay either.

Please help!

Here is the single class version that works fine.

package
{
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;

[SWF(width="650", height="450",backgroundColor="#FFFFFF", frameRate="60")]

public class CharacterMovieclip extends MovieClip
{
    //Create and initialize the vx and vy variables
    public var vx:int = 0;
    public var vy:int = 0;                  


    [Embed(source="../swfs/characterRes.swf", symbol="Character")]
    public var Character:Class;
    public var character:MovieClip = new Character();


    public function CharacterMovieclip()
    {

        //Add Character
        stage.addChild(character);
        character.x = 200;
        character.y = 100;
        character.gotoAndStop(1);

        //Add the event listeners
        stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
        stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);     
        stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }
    public function keyDownHandler(event:KeyboardEvent):void
    {
        if (event.keyCode == Keyboard.LEFT)
        {
            vx = -5;
            character.gotoAndStop(4);
        }
        else if (event.keyCode == Keyboard.RIGHT)
        {
            vx = 5;
            character.gotoAndStop(5);
        }
        else if (event.keyCode == Keyboard.UP)
        {
            vy = -5;
            character.gotoAndStop(2);
        }
        else if (event.keyCode == Keyboard.DOWN)
        {
            vy = 5;
            character.gotoAndStop(3);
        }
    }
    public function keyUpHandler(event:KeyboardEvent):void
    {
        if (event.keyCode == Keyboard.LEFT 
            || event.keyCode == Keyboard.RIGHT)
        {
            vx = 0;
            character.gotoAndStop(1);
        } 
        else if (event.keyCode == Keyboard.DOWN 
            || event.keyCode == Keyboard.UP)
        {
            vy = 0;
            character.gotoAndStop(1);
        } 
    }
    public function enterFrameHandler(event:Event):void
    {   
        //Move the player 
        character.x += vx 
        character.y += vy;
        trace(character.currentFrame);
        trace(character.totalFrames);
        trace(character.x);

    }   

 }
}

But that all goes to hell once I try it across a few classes. Here is the character class.

package
{
import flash.display.DisplayObject;
import flash.display.MovieClip;

public class Character extends MovieClip
{
    [Embed(source="../swfs/characterRes.swf", symbol="Character")]
    private var CharacterImage:Class;

    //Private properties
    private var _characterImage:DisplayObject = new CharacterImage();
    private var _character:MovieClip = new MovieClip();

    //Public properties
    public var vx:int = 0;
    public var vy:int = 0;

    public function Character()
    {
        _character.addChild(_characterImage);
        this.addChild(_character);  
    }
 }
}

The character object is then added to the LevelOne class

package
{
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;

public class LevelOne extends Sprite
{
    //Declare the variables to hold
    //the game objects
    private var _character:Character;
    private var _background:Background;

    //A variable to store the reference
    //to the stage from the application class
    private var _stage:Object;

    public function LevelOne(stage:Object)
    {
        _stage = stage; 
        this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    }
    private function addedToStageHandler(event:Event):void
    {
        startGame();
        this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    }
    private function startGame():void
    {
        //Create Game Objects
        _character = new Character();
        _background = new Background();

        //Add them to stage
        this.addChild(_background);
        _background.x = 0;
        _background.y = 0;

        this.addChild(_character);
        _character.x = 300;
        _character.y = 50;
        _character.gotoAndStop(1);


        //Event listeners
        _stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
        _stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler); 
        this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }
    private function enterFrameHandler(event:Event):void
    {   
        //Move the game character and check its stage boundaries
        _character.x += _character.vx; 
        _character.y += _character.vy;
        checkStageBoundaries(_character);

                    trace(_character.currentFrame);
        trace(_character.totalFrames);
        trace(_character.x);
    }
    private function checkStageBoundaries(gameObject:MovieClip):void
    {
        if (gameObject.x < 50)
        {
            gameObject.x = 50;
        }
        if (gameObject.y < 50)
        {
            gameObject.y = 50;
        }
        if (gameObject.x + gameObject.width > _stage.stageWidth - 50)
        {
            gameObject.x = _stage.stageWidth - gameObject.width - 50;
        }
        if (gameObject.y + gameObject.height > _stage.stageHeight - 50)
        {
            gameObject.y = _stage.stageHeight - gameObject.height - 50;
        }
    }
    private function keyDownHandler(event:KeyboardEvent):void
    {
        if (event.keyCode == Keyboard.LEFT)
        {
            _character.vx = -5;
            _character.gotoAndStop(4);
        }
        else if (event.keyCode == Keyboard.RIGHT)
        {
            _character.vx = 5;
            _character.gotoAndStop(5);
        }
        else if (event.keyCode == Keyboard.UP)
        {
            _character.vy = -5;
            _character.gotoAndStop(2);

        }
        else if (event.keyCode == Keyboard.DOWN)
        {
            _character.vy = 5;
            _character.gotoAndStop(3);
        }
    }
    private function keyUpHandler(event:KeyboardEvent):void
    {
        if (event.keyCode == Keyboard.LEFT 
            || event.keyCode == Keyboard.RIGHT)
        {
            _character.vx = 0;
            _character.gotoAndStop(1);
        } 
        else if (event.keyCode == Keyboard.DOWN 
            || event.keyCode == Keyboard.UP)
        {
            _character.vy = 0;
            _character.gotoAndStop(1);
        }
    }

}
}

And levelOne is added to the stage by the Main application class.

package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.display.MovieClip;

[SWF(width="650", height="450", 
backgroundColor="#FFFFFF", frameRate="60")]

public class Main extends Sprite
{
    private var _levelOne:LevelOne;

    public function Main()
    {
    _levelOne = new LevelOne(stage);
        stage.addChild(_levelOne);  
    }

}
}

Somewhere in all this my MovieClip stops playing the individual animations. Its on the stage, can be moved around with arrow keys, but wont play the keyframe animations. (And trace seems to think its playing frame 0, and it only has 1 frame too.)

Any help would be hugely appreciated. My head hurts.


Solution

  • The problem is in your new Character class.

    Let's see on the first class that works perfectly, you embed symbol="Character" from the characterRes.swf file and it's your 5 frame movie clip.

    Now see on the second class, that is broken: you do the same embedding and now your 5 frame movie clip is _characterImage rather than Character class, that you create. It's right decision from the OOP point of view to hide implementation (the direct MovieClip from the library or custom class) inside the same API (in both cases Character is outside a simple MovicClip) and there can be two solutions for the problem:

    First - try to embed symbol for the hole class and remove your current code it's not needed here:

    [Embed(source="../swfs/characterRes.swf", symbol="Character")]
    public class Character extends MovieClip
    {
        //Public properties
        public var vx:int = 0;
        public var vy:int = 0;
    
        public function Character()
        {
        }
    }
    

    Second: make you Character class wrapper for the Character from swf - pass all MovieClip API from it to the _character MovieClip:

    package
    {
    import flash.display.DisplayObject;
    import flash.display.MovieClip;
    
    public class Character extends MovieClip
    {
        [Embed(source="../swfs/characterRes.swf", symbol="Character")]
        private var _characterClass:Class;
    
        //Private properties
        private var _character:MovieClip = new _characterClass();
    
        //Public properties
        public var vx:int = 0;
        public var vy:int = 0;
    
        public function Character()
        {
            addChild(_character)
        }
    
        override public function gotoAndPlay(frame:Object, scene:String=null):void
        {
            _character.gotoAndPlay(frame, scene);
        }
        override public function gotoAndStop(frame:Object, scene:String=null):void
        {
            _character.gotoAndStop(frame, scene);
        }
        override public function play():void
        {
            _character.play();
        }
        override public function stop():void
        {
            _character.stop();
        }
    
        //and other methods and getters for currentFrame, totalFrames
     }
    }
    

    The first method is more simple, the second one is more flexible, because it's separate graphics and logic - so you in future you will be able for instance pass this link in Character Constructor and load assets dynamically or use Character class as animation controller for the different MovieClips from the loaded swfs.