1

Taking this thread to the next level and now making a Main.as class to display different "screens" of my game. For right now I only have 1 screen called ControlPanel, but this will eventually have multiple levels (each level will be a separate screen) and a level selection screen, etc. added. So at this point I'm losing control over how to give things access to the stage (in other words... it's getting really thick with multiple levels and this noob's brain is being overloaded lol).

To start off, I took all of my graphics that were on the stage by default (buttons, lights, meters, score text, etc.) and created a new symbol (MovieClip) that I called ControlPanel and checked off "Export for ActionScript" with a class name of ControlPanel. So now my games fla stage is black and I made it's document class Main.as. which looks like this:

public class Main extends MovieClip {

    public var controlPanel:ControlPanel;

    public function Main() {
        addEventListener(Event.ADDED_TO_STAGE, added);
    }

    private function added(evt:Event):void {
        removeEventListener(Event.ADDED_TO_STAGE, added);

        controlPanel = new ControlPanel(this);
        addChild(controlPanel);
    }
}

Running this worked perfectly in that my Control Panel screen popped right onto the screen. Of course all the buttons didn't work yet but that is the next step. So I modified my old Game.as to now be called ControlPanel and read like so:

public class ControlPanel extends MovieClip {
    private var docRef:Main;

    private var _player:Player;
    private var _controller:Controller;

    public function ControlPanel($docRef:Main):void {
        this.docRef = $docRef;

        addEventListener(Event.ADDED_TO_STAGE, added);
    }

    private function added(evt:Event):void {
        removeEventListener(Event.ADDED_TO_STAGE, added);

        _player = new Player(docRef);
        _controller = new Controller(_player, docRef);

        addChild(_player);
    }

Now this is adding my Player class and my Controller class. They basically have a similar makeup so I'm just going to show the Player.as class here:

public class Player extends MovieClip {
    private var docRef:Main;
    private var _lights:uint;

    public function Player($docRef:Main):void {
        this.docRef = $docRef;

        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }

    private function lightsOut():void {
        switch(_lights) {
        case 1:
        docRef.controlPanel.greenLight1.visible=false;
        break;
        case 2:
        docRef.controlPanel.greenLight2.visible=false;//works
        break;
        case 3:
        docRef.controlPanel.greenLight3.visible=false;//works
        break;
        }
    }

The problem is, now that I try and test it, I'm no longer getting the Control Panel graphic that I was seeing before, but I'm now getting the error message:

TypeError: Error #1009: Cannot access a property or method of a null object reference. at ControlPanel() at Main/added()

Also I commented out the controlPanel lines in Main and just did a trace(this); and it returned

[object Main]

Shouldn't it be object Stage or something like that? Never tried that before so I don't know what it used to be. Where did I go wrong here?

UPDATE: OK so my Controller isn't just like the Player class but needs instances of both the stage and the view passed to it maybe??? Well here is what I've got for a controller and some of the event listeners that are needed:

public class Controller extends MovieClip {
    private var _model:Object;
    private var _dHandle:Object;
    private var panelView:ControlPanelView; //started playing with this idea but maybe that is the wrong way to go?? Stick with docRef here? or both?

    public function Controller(model:Object, dHandle:Object, $view:ControlPanelView):void {
        this._model = model;
        this._dHandle = dHandle;
        this.panelView = $view;

        docRef.stage.addEventListener(KeyboardEvent.KEY_DOWN, processKeyDown);
        docRef.stage.addEventListener(MouseEvent.MOUSE_DOWN, processMouseDown);
        docRef.ControlPanelView.fireButton.addEventListener(MouseEvent.CLICK, processFirePress);
        docRef.ControlPanelView.cWButton.addEventListener(MouseEvent.CLICK, processCWPress);
        docRef.ControlPanelView.cCWButton.addEventListener(MouseEvent.CLICK, processCCWPress);

As you can see, this is a hodgepodge of ideas torn between the docRef and the View idea. I need to have access to the stage for my mouse and keyboard listeners, but then I also need access to the buttons in the ControlPanelView symbol. Do I need to pass both the view and the docRef here? How to I maintain access to the stage and the ControlPanelView graphic?

Community
  • 1
  • 1
FlashNoob468
  • 175
  • 1
  • 17
  • The trace is correct, it's saying that you have an object of type Main, which is your class. Do you really need Player to receive a reference to your document class now, or does it just need a reference to ControlPanel? Both should work in this case, but the latter would be easier to read. – shanethehat Aug 02 '11 at 21:31
  • Hang on, do I have this right, you have a library item set to 'export for actionscript', which you have called `ControlPanel`, and you have also got a .as file that contains a class called `ControlPanel`? – shanethehat Aug 02 '11 at 21:35
  • Check your symbol linkages in the library. Also check that your package names match what is in the library. – Plastic Sturgeon Aug 02 '11 at 21:37
  • Yes I took all the graphics and put them into the symbol called ControlPanel and when I clicked Export for ActionScript it just used the default name of ControlPanel so I left it. Then I needed to control it so I figured it was already being created, why not put some real code into it. So I changed Game.as to now be ControlPanel.as. And yeah I also made my variable that hold the new ControlPanel called lowercase controlPanel. Maybe it's just a few too many and I need to change to something like cPanel? Or can I not have my Symbol and controlling class have the same name? – FlashNoob468 Aug 02 '11 at 21:54

1 Answers1

1

You can't have something exporting from the library and a class file with the same name. When you set a library item to 'Export for ActionScript', Flash creates a class of that name to represent that object, and so to then create a class of the same name yourself will cause a conflict.

You have two options for how to proceed:

Inheritance

You can use the class that you have created for your ControlPanel as a code base for the symbol in your library. To do this, first give it a different name, say ControlPanelBase. Then you can assign it to be the base class of your symbol. You do this in the IDE by going into your symbol properties and changing the Base class from flash.display.MovieClip to ControlPanelBase (or whatever name you choose). Click the little green tick to make sure the class is found, then click OK.

Now, when you create a new ControlPanel using the new ControlPanel(this) notation, it will create an object that uses the graphics, but also the code from the base class. Because you are inheriting, you will need to change all occurances of the word private to protected to allow them to be seen by any descendant classes.

Separate the view

This is my preference because when you stop using the IDE for anything more than generating asset packs, you avoid having to recompile your assets whenever you change the code.

Change your library symbol's name to something more descriptive, like ControlPanelView. Now, when you create your ControlPanel using new ControlPanel(this) it will not attach any graphics, so you will need to do that yourself:

public class ControlPanel extends MovieClip {
    private var docRef:Main;

    private var _player:Player;
    private var _controller:Controller;

    public var view:ControlPanelView; //note that this is public

    public function ControlPanel($docRef:Main):void {
        this.docRef = $docRef;

        addEventListener(Event.ADDED_TO_STAGE, added);
    }

    private function added(evt:Event):void {
        removeEventListener(Event.ADDED_TO_STAGE, added);

        view = addChild(new ControlPanelView()) as ControlPanelView; //add the graphics

        _player = new Player(docRef);
        _controller = new Controller(_player, docRef);

        addChild(_player);
    }
}

Now, to access those lights from inside Player, you would say

docRef.controlPanel.view.greenLight1.visible=false;

You can see that this is becoming a bit of a mouthful, but to shorten it you could pass a reference to the ControlPanel instead of Main when you create the player, or indeed you could pass a reference to the view if that is all you ever need to talk to.

in ControlPanel:

_player = new Player(view);

new Player class:

public class Player extends MovieClip {
    private var panelView:ControlPanelView;
    private var _lights:uint;

    public function Player($view:ControlPanelView):void {
        this.panelView = $view;

        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }

    private function lightsOut():void {
        switch(_lights) {
        case 1:
        panelView.greenLight1.visible=false;
        break;
        case 2:
        panelView.greenLight2.visible=false;//works
        break;
        case 3:
        panelView.greenLight3.visible=false;//works
        break;
        }
    }
}

Regarding your update, remember that your Main class and the stage are not the same thing. Every display object has a stage property, which will all point to the same thing. That means that to add your listeners to the stage, there is no difference between Main.stage, view.stage and this.stage (assuming the controller is on the stage). So your controller could look like this:

public class Controller extends EventDispatcher {
    private var _model:Player;
    private var panelView:ControlPanelView;

    public function Controller($model:Player, $view:ControlPanelView):void {
        this._model = $model;
        this.panelView = $view;

        panelView.stage.addEventListener(KeyboardEvent.KEY_DOWN, processKeyDown);
        panelView.stage.addEventListener(MouseEvent.MOUSE_DOWN, processMouseDown);
        panelView.fireButton.addEventListener(MouseEvent.CLICK, processFirePress);
        panelView.cWButton.addEventListener(MouseEvent.CLICK, processCWPress);
        panelView.cCWButton.addEventListener(MouseEvent.CLICK, processCCWPress);
    }
}

and in ControlPanel, you would create it like this:

_controller = new Controller(_player, view);

Note that I've also made your controller extend EventDispatcher rather than MovieClip. It is good practise to extend from the bare minimum class that you need, so if this will never be on the stage but it will put out events, EventDispatcher is the bare minimum. If it will never need to dispatch events, only add listeners to others, then it need not extend anything.

Basically, only extend MovieClip if you need timeline functionality or your class is going to be a Base Class for something in the library. For any other standard display object you should extend either Sprite or Shape, the former if you intend to add children, the latter if you only intend to draw vector graphics. If your object is never going on the display tree, most often you will need to extend either EventDispatcher or nothing at all.

shanethehat
  • 15,460
  • 11
  • 57
  • 87
  • OK my controller is more complicated then I thought. I added a bit of it's code to the bottom of my original post. I'm sorry I'm a pita but I REALLY, REALLY appreciate all of the help you have given. – FlashNoob468 Aug 02 '11 at 22:30
  • @FlashNoob468 - I've addressed your update. Hopefully that will clear things up. – shanethehat Aug 02 '11 at 22:44
  • OK I didn't know that about the stage. That actually clears up a lot of things I was just accepting as working in some round about way. HOWEVER.... I got everything changed over to the new system and I am back where I started with the `TypeError: Error #1009: Cannot access a property or method of a null object reference. at ControlPanel() at Main/added()` Should I still be adding the ControlPanel in the Main or should I now be adding ControlPanelView? Right now my main still looks like the original listed above. – FlashNoob468 Aug 02 '11 at 22:56
  • What is the Class Name of the movieclip you're exporting from Flash? – shanethehat Aug 02 '11 at 22:58
  • Just to be sure I'm looking at the same thing you are asking, you are talking about when I go to the properties of the ControlPanelView symbol I have created in my library, and look down at the Class blank right? I had forced it to be ControlPanelView. Should I have left it at ControlPanel? – FlashNoob468 Aug 02 '11 at 23:05
  • No, that's right. Not sure what's going wrong to be honest. Have you cleared your ASO files? (In the IDE, Control -> Delete ASO Files) – shanethehat Aug 02 '11 at 23:09
  • Found it! I turned on debugging and found what it doesn't like. In ControlPanel, one of the first variables I set up include a min and max range on the stage where it's possible to use the mouse. The problem is I'm doing this: `private var viewportXmin:uint = stage.stageWidth * 0.1;` and it doesn't like stage.stageWidth or maybe just stage? When I take this out and hard set a number the game runs perfectly. But since I don't know the final resolution this will be played at I need to let it be set by looking at what the current stageWidth is. How do I access stage now in ControlPanel? – FlashNoob468 Aug 03 '11 at 00:02
  • Same as you did. you are still passing a reference to your document class, Main, from which you can access the stage as `docRef.stage`. So don't set the value when you define, set it in the constructor when you have a reference to Main. – shanethehat Aug 03 '11 at 00:06
  • UGH!!! That is going to be my downfall, though I think I'm finally understanding the whole ADDED_TO_STAGE thing. Nothing like screwing it up 20 times to learn something. One last question and I'll leave you alone. That View variable that you made public, am I supposed to use that for all my classes to add visible screens to the stage? I get how I'll add say a pause screen over top of ControlPanelView, but what if I add a level select screen that doesn't have anything to do with ControlPanel? Would it need it's own public View or could I make View a variable in Main that all classes can see? – FlashNoob468 Aug 03 '11 at 00:42
  • Nevermind. I just answered my own question. It's of type ControlPanelView so of course it won't be of value to LevelSelectView or whatever I call it. So I would just make a new one. I was thinking along the lines of a screen variable which I saw (and couldn't make work) in one of my books where they added things to the screen that they wanted to have seen and removed it wen they didn't. I like this method you've helped me set up and think I can make my way around it better now. I just want to thank you, thank you, thank you again for ALL of your help. I'll leave you alone now... for today ;) – FlashNoob468 Aug 03 '11 at 01:02