1
votes

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?

1
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
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
Check your symbol linkages in the library. Also check that your package names match what is in the library.Plastic Sturgeon
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

1 Answers

1
votes

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.