I'm playing around with flash, and I've created multiple scenes for things like menu's, buttons, etc. When trying to add event handlers for buttons that are in one scene, but not others, the compiler complains saying that it can't reference to objects that don't exist.
I figured the solution to be simple... Get the scene name, match that against an if statement and load the event handlers through the if statements...
However, after digging around on the net for far too long, I just can't seem to find a way to do this properly. Does anyone know a way?
I've tried using the following :
var scene:Scene = myflvandclassname.currentScene;
var sName:String = MovieClip.currentScene.name;
Both lead to an error "Access of possibly undefined property Scene through a reference with static type Class".
Omit MovieClip and scenes as source for organising your project, and code on the timeline. Use Document class as entry point. This tutorial should help you to grasp main concept.
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class StackOverflow extends Sprite {
public function StackOverflow() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
setup();
}
private function setup():void {
const padding:int = 20;
//Initiate your UI components and place them in the display list
var menu:MyMenu = new MyMenu();
var buttons:MyFooterButtons = new MyFooterButtons();
var etc:AnotherComponent = new AnotherComponent();
addChild(menu);
addChild(buttons);
addChild(etc);
menu.x = menu.y = padding;
//Place them and initialise with concrete information
}
}
}
For example, MyMenu, MyFooterButtons, AnotherComponent could be MovieClip/Sprite in the library with export settings, where you did all your work with placement, styling, etc
I had a same problem. I wanted to check from an external Class the current scene name and depending on the name (name of the game level) to pass some values in some attributes… So what I did and it worked is that.
//main in 1st frame of the fla
stop();
var myCheckSceneClass: CheckSceneClass = new CheckSceneClass();
myCheckSceneClass.myCurrentScene = currentScene;
myCheckSceneClass.checkScene();
//CheckSceneClass
package {
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.display.Scene;
public class CheckSceneClass extends flash.display.MovieClip {
public var myCurrentScene : Scene;
public function CheckSceneClass () {
}
public function checkScene() {
switch (myCurrentScene.name) {
case "Scene 1":
trace("It is the 1st Scene");
break;
default:
trace("Error with Scene Name");
break;
}
}
}
}
Related
I'm gonna try explaining my situation with images, to make sure that everyone will understand what i want to succeed.
First of all i have 3 files:
GenImage.fla which is linked to class GeneralImage.as
and it only contains the following picture (I tried to make the image Movie Clip but again it's not working):
Pic1
and a file named ScrollUI.as which contains the class ScrollUI.
What i want to succeed is from my GeneralImage class to create a ScrollUi item, pass the stage, and there create a ScrollPane which makes the picture look like this:
Pic2
The center part of the second image is the ScrollPane Component, which i need to make it Scrollable through the whole image. I managed to get it in my screen but i can't put the Stage in it and make it scrollable.
These are my codes :
GeneralImage.as
package {
import flash.display.*;
import flash.events.*;
import flash.display.Stage;
import ScrollUI;
public class GeneralImage extends MovieClip
{
public function GeneralImage()
{
var k = new ScrollUI();
k.ScrollConstructor(this);
}
}
}
ScrollUI.as
package
{
import flash.display.*;
import flash.events.*;
import fl.containers.ScrollPane;
import fl.events.ScrollEvent;
import fl.controls.ScrollPolicy;
public class ScrollUI extends MovieClip
{
private var _mainStage:Stage;
var aBox:MovieClip = new MovieClip();
var aSp:ScrollPane = new ScrollPane();
public function ScrollUI()
{
}
function ScrollConstructor(stage:Object):void
{
_mainStage = (stage as MovieClip).stage;
aBox == stage as MovieClip;
aSp.source == aBox ;
_mainStage.addChild(aBox);
aSp.setSize(300,300);
aSp.move(150, 75);
aSp.scrollDrag = true;
aSp.horizontalScrollPolicy=ScrollPolicy.OFF;
_mainStage.addChild(aSp);
}
}
}
So what i want it to set the Source of the Scroll Pane ( which is named aSp ) to be the _mainStage which is the stage i get from GeneralImage
Your issues is likely these two lines:
aBox == stage as MovieClip;
aSp.source == aBox ;
You're doing a comparison by using two ==, which effectively does nothing in your case. Use a single = to assign a value.
This is how I would suggest you approach this:
In the FlashPro library, find your image asset (I'm going to assume you have it wrapped in a MovieClip), right-click (or command click) and go to it's properties. Check the "export for actionscript" box, and give it a meaningful class name (for my example, I'll assume you called it MyImage)
Then you could do the following:
ScrollUI Class
package
{
import flash.display.Sprite;
import fl.containers.ScrollPane;
import fl.events.ScrollEvent;
import fl.controls.ScrollPolicy;
public class ScrollUI extends Sprite
{
//just declare the variables here, don't assign them new values
private var aBox:MovieClip;
private var aSp:ScrollPane;
public function ScrollUI()
{
//use the actual constructor...
aSp = new ScrollPane(); //instantiate a new scroll pane
addChild(aSp); //add the scroll pane to the display
aBox = new MyImage(); //instantiate an new MyImage from the library
//set the scroll pane properties
aSp.source = aBox; //you had two = signs here before, which doesn't actually assign anything it compares
aSp.setSize(300,300);
aSp.move(150, 75);
aSp.scrollDrag = true;
aSp.horizontalScrollPolicy=ScrollPolicy.OFF;
}
}
}
Document Class
package {
import ScrollUI;
public class GeneralImage extends MovieClip
{
public function GeneralImage()
{
var k:ScrollUI = new ScrollUI(); //create a new instance of the ScrollUI class
addChild(k); //add the scrollUI object to the display;
//OR, you just do this:
//addChild(new ScrollUI());
}
}
}
You could also just set the .source property of the scroll pane to the physical path of your image.
I found the solution, Thanks Batman for his help, I changes some things into my code and the program is working.
First of all as Batman said , In my GenImage.fla i made the logo a MovieClip and i named it "wholemap"
Here are my codes :
GeneralImage.as
package {
import flash.display.*;
import flash.events.*;
import flash.display.Stage;
import ScrollUI;
public class GeneralImage extends MovieClip
{
//as Batman indicated, I should have used the ScrollUI constructor, but
//except for the Stage, i also send the wholemap that is in my GenImage.fla
//<< this.getChildByName("wholemap") as MovieClip) >>
public function GeneralImage()
{
var k = new ScrollUI(this, this.getChildByName("wholemap") as MovieClip);
}
}
}
ScrollUI.as
package
{
import flash.display.*;
import flash.events.*;
import fl.containers.ScrollPane;
import fl.events.ScrollEvent;
import fl.controls.ScrollPolicy;
public class ScrollUI extends MovieClip
{
private var _mainStage:Stage;
var aBox:MovieClip = new MovieClip();
//So our constructor gets 2 items, a Stage, and a MovieClip
public function ScrollUI(stage:Object, pic:MovieClip)
{
//We set the Stage at the variable _mainStage with that way:
_mainStage = (stage as MovieClip).stage;
//We set the Image that we will take at our clip variable :
var clip:MovieClip = pic;
//And we send the Movieclip (clip) in our ScrollConstructor function
ScrollConstructor(clip);
}
function ScrollConstructor(Clip:MovieClip):void
{
var aSp:ScrollPane = new ScrollPane();
aBox = Clip;
_mainStage.addChild(aBox);
aSp.source = aBox ;
aSp.setSize(300,300);
aSp.move(150, 75);
aSp.scrollDrag = true;
aSp.horizontalScrollPolicy=ScrollPolicy.OFF;
aSp.verticalScrollPolicy=ScrollPolicy.OFF;
_mainStage.addChild(aSp);
}
}
}
Thank you very much for your assistance, I hope if someone else come across with this problem to be able to solve it with this Answer
hi I'm relatively new to as3 (this year) and I'm getting this error
typer error #1009 cannot access a property or method of a null object
reference. at FoodObject/collisionTest()
i was hoping anyone could help
package {
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.*
import flash.utils.*
import flash.display.Stage;
public class GameScene1 extends Scene {
//public variables
//character & scenery
public var mainChar: Character;
public var testFood: FoodObject;
//constructor is used to create all necessary objects for this scene and display them
public function GameScene1(gm_: Manager) {
//constructor
super(gm_);
trace("GameScene 1 constructor");
//character
mainChar = new Character;
addChild(mainChar);
mainChar.x = 200;
mainChar.y = 200;
testFood = new FoodObject;
addChild(testFood)
testFood.x = 50
testFood.y = 200
the food object class is here.
package {
import GameScene1
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
public class FoodObject extends MovieClip {
public var Game1:GameScene1;
public function FoodObject() {
//constructor code
this.addEventListener(Event.ENTER_FRAME, collisionTest)
}
public function collisionTest(e:Event)
{
if (this.hitTestObject(Game1.mainChar))
{
trace("it works")
}
}
}
}
game manager here:
package {
import flash.display.MovieClip;
public class Manager extends MovieClip {
//stores which scene is currently loaded
public var curScene:Scene=null;
public function Manager() {
//constructor
trace("Manager Construct")
GoToScene("menu");
}
public function GoToScene(name:String)
{
if (curScene) //there was a scene already
{
curScene.OnLeaveScene(); //call its OnLeaveScene function to remove all objects
removeChild(curScene);
}
if(name=="menu") curScene = new MenuScene(this);
if(name=="select") curScene = new SelectScene(this);
if(name=="game1") curScene = new GameScene1(this);
if(name=="game2") curScene = new GameScene2(this);
if(name=="game3") curScene = new GameScene3(this);
if(name=="credit") curScene = new CreditScene(this);
addChild(curScene);
}
}
Your problem is that the concerns of your classes are not separate:
Your Scene knows both the Character and the Food object, you instantiate both classes there, nothing wrong with that.
The problem starts when you are trying to do something in the Food object, that requires knowledge of the character. The thing is: the Food object doesn't know anything about the Character.
You can solve this by simply passing the reference of Character to your Food object. In order to do this, modify the constructor like so:
private var character:Character;
public function FoodObject(character:Character) {
//constructor code
this.addEventListener(Event.ENTER_FRAME, collisionTest)
this.character = character;
}
The usage of said constructor in your Scene changes as follows:
testFood = new FoodObject(mainCharacter);
This way, Food knows the character and can do stuff with it, for example do collision tests:
public function collisionTest(e:Event)
{
if (this.hitTestObject(character)) // ==== this line changed
{
trace("it works")
}
}
However, this raises an important issue: Why should Food know the Character at all?
Sure enough, you want to do the collision test, which requires both objects.
But why do you want to do it in the Food object?
Doing the collision check in Food is cumbersome, because you have to pass a reference to Character in order to do it there.
The much preferred way of doing this is to do the collision check where both objects participating in the check are already known.
In your case, this is the Scene.
Think about how easy it is to do the check in Scene:
testFood.hitTestObject(mainCharacter);
It's that simple, because everything you need is already there.
To recap:
The collision check requires knowledge of 2 objects that you want to
check.
In order to do the check in either one, you have to pass a reference
of the other. (Character to Food as seen above or the other way
round)
It is a lot easier to do the check in some place that already knows
both objects, because no reference have to be passed around.
Your original code failed because Game1 in FoodObject is never assigned a value and therefore remains null.
Invoking methods on null causes the error you experienced.
You forgot to take the instance of GameScene1 class using new keyword.
public var Game1:GameScene1;
public function FoodObject() {
//constructor code
var _manager:Manager = new Manager();
Game1 = new GameScene1(_manager)
this.addEventListener(Event.ENTER_FRAME, collisionTest);
}
public function collisionTest(e:Event):void{
....
}
I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.
this is probably really simple I cannot for the life of me work out why this is not working. I am trying to create an object (only for testing) and assign event listeners within the constructor. In my head it should work but I am sure I must be missing something:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class Box extends MovieClip {
public function Box() {
// constructor code
var mySound:Sound = new Bark();
trace("Box created");
height=800;
width=300;
x=100;
y=100;
addEventListener(MouseEvent.MOUSE_OVER, overThis);
addEventListener(MouseEvent.CLICK, clickToPlay);
}
public function overThis(m:MouseEvent){
trace("SADF");
}
function clickToPlay(m:MouseEvent){
mySound.play();
}
}}
By doing this i wanted the "box" to be self sufficient in regards to managing its own events. (Please ignore stuff like play(), that all works when I run within the MAINDOC.as directly.
This is the main doc:
package {
import flash.display.MovieClip;
import flash.media.Sound;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
public class MainDoc extends MovieClip
{
public function MainDoc()
{
// constructor code
init();
function init()
{
createBox(300,300);
}
function createBox(newX,newY)
{
var box = new Box();
box.x = newX;
box.y = newY;
addChild(box);
}
}
}}
When I test it creates the box (which i have drawn) but does not run any events?
Hope you guys can help
Ray
You have to add some graphics to your box so your MouseEvents can work:
public function Box()
{
// constructor code
var mySound:Sound = new Bark();
trace("Box created");
// begin the filling of some graphics, you can change color/alpha as you want
this.graphics.beginFill( 0x000000, 1 );
// make a rectangle 300x800
this.graphics.drawRect(0,0,300,800);
// stop filling
this.graphics.endFill();
// you don't need it anymore
//height=800;
// you don't need it anymore
//width=300;
// place your clip where you want but you do that in the Main class so no need there
//x=100;
//y=100;
// now you have graphics attached to your MovieClip the MouseEvent must work
addEventListener(MouseEvent.MOUSE_OVER, overThis);
addEventListener(MouseEvent.CLICK, clickToPlay);
}
Hope that will help you :)
I have a small project I'm trying to help learn as3. It is a variation from the book Foundation Game Design with Actionscript 3.0. I am using an fla only to have a document class. All art is loaded within the as files. In the book, he just put all the code in the document class, I followed along and it worked as expected. I am trying to break out the code into separate classes to get a handle on OOP. One class makes a background - Background.as, one makes a character - Character.as, and one makes a button, which I instantiate 6 times for 6 different buttons - GameButton.as. And of course there is GameWorld.as which is the document class. Everything loads and shows up as expected. However when I try and add an eventListener for the buttons, I don't get any response. I have tried putting the eventListener in the GameButton.as and also tried it in the GameWorld.as neither of which has worked. Also I pass a reference to the stage when instantiating the various classes, because when I tried to addChild in the GameWorld.as, nothing would show up. I searched the site and found something similar, but it didn't seem to help. Thank you in advance for any advice you my have. Here is the code:
GameWorld.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.DisplayObject
import flash.events.MouseEvent;
import GameButton;
import Character;
import Background;
[SWR(width = "550", height = "400", backgroundColor = "#FFFFFF", frameRate = "60")]
public class GameWorld extends Sprite
{
//public variables
//Background
public var gameBackground:Background;
//Character
public var catCharacter:Character;
//Buttons
public var upButton:GameButton;
public var downButton:GameButton;
public var growButton:GameButton;
public var shrinkButton:GameButton;
public var vanishButton:GameButton;
public var spinButton:GameButton;
public function GameWorld ()
{
//Add the background to the stage
gameBackground = new Background("../images/background.png", stage);
//Add the character(s) to the stage
catCharacter = new Character("../images/character.png", stage);
//Set initial character position
catCharacter.CharacterPos(225, 150);
//Add the buttons to the stage
upButton = new GameButton("../images/up.png", stage, 25, 25);
downButton = new GameButton("../images/down.png", stage, 25, 85);
growButton = new GameButton("../images/grow.png", stage, 25, 145);
shrinkButton = new GameButton("../images/shrink.png", stage, 425, 25);
vanishButton = new GameButton("../images/vanish.png", stage, 425, 85);
spinButton = new GameButton("../images/spin.png", stage, 425, 145);
//Button event handlers
upButton.addEventListener(MouseEvent.CLICK, upButtonHandler);
}
public function upButtonHandler(event:MouseEvent)
{
trace("You clicked the up button!");
catCharacter.CharacterMove(15);
}
}
}
GameButton.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class GameButton extends Sprite
{
//public variables
public var stageRef:Stage;
public var urlRequest:URLRequest;
public var gameButtonLoader:Loader;
public var gameButtonSprite:Sprite;
//Constructor
public function GameButton (urlRequest:String, stageRef:Stage, xPos:Number, yPos:Number)
{
this.stageRef = stageRef
this.urlRequest = new URLRequest();
gameButtonLoader = new Loader();
gameButtonSprite = new Sprite();
this.urlRequest.url = urlRequest;
gameButtonLoader.load(this.urlRequest);
gameButtonSprite.addChild(gameButtonLoader);
this.stageRef.addChild(gameButtonSprite);
gameButtonSprite.buttonMode = true;
gameButtonSprite.x = xPos;
gameButtonSprite.y = yPos;
}
}
}
Character.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Character
{
//private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var characterLoader:Loader;
private var characterSprite:Sprite;
//public variables
public var character_x_pos:Number;
public var character_y_pos:Number;
//Constructor
public function Character (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
characterLoader = new Loader();
characterSprite = new Sprite();
this.urlRequest.url = urlRequest;
characterLoader.load (this.urlRequest);
characterSprite.addChild (characterLoader);
this.stageRef.addChild (characterSprite);
characterSprite.mouseEnabled = false;
}
//Set the position of the character
public function CharacterPos(xPos:Number, yPos:Number):void
{
characterSprite.x = xPos;
characterSprite.y = yPos;
}
//Move the position of the character
public function CharacterMove( yPos:Number):void
{
characterSprite.y -= yPos;
}
}
}
Background.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Background
{
//Private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var backgroundLoader:Loader;
private var backgroundSprite:Sprite;
//Constructor
public function Background (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
backgroundLoader = new Loader();
backgroundSprite = new Sprite();
this.urlRequest.url = urlRequest;
backgroundLoader.load (this.urlRequest);
backgroundSprite.addChild (backgroundLoader);
this.stageRef.addChild (backgroundSprite);
backgroundSprite.mouseEnabled = false;
}
}
}
All art is loaded within the as files.
This is not an approach I recommend. There's a reason God gave us the Flash IDE--and it's not to write code! Any time you're spending on layout and viduals in code is just wasted, unless you have an actual requirement to change the visuals at runtime. The fact that your paths are all hard-coded suggests that you don't have that requirement.
So let's step back and imagine that you have a Symbol that contains 6 Symbols that you've created as just Flash buttons (when you select Button as the Symbol type). These will be SimpleButtons, but in the Class below we're just going to type them as DisplayObject. The Class doesn't care what they are, but using Simplebutton gives them up, over, down and hit states that require no code.
Note that the below assumes you have "automatically declare stage instances" off, which is IMO the best way to do things.
package view {
public class NavBar extends Sprite {
//because you put these on stage in the Symbol, they will be available in the constructor
public var upButton:DisplayObject;
public var downButton:DisplayObject;
public var growButton:DisplayObject;
public var shrinkButton:DisplayObject;
public var rotateButton:DisplayObject;
public var vanishButton:DisplayObject;
//makes it easier to do the same setup on all buttons
protected var allButtons:Vector.<DisplayObject> = <DisplayObject>([upButton, downButton, growButton, shrinkButton, rotateButton, vanishButton]);
public function NavBar() {
super();
for each (var btn:DisplayObject in allButtons) {
btn.buttonMode = true;
btn.mouseChildren = false;
btn.addEventListener(MouseEvent.CLICK, onButtonClick);
}
}
protected function onButtonClick(e:MouseEvent):void {
switch (e.target) {
case upButton:
dispatchEvent(new CharacterEvent(CharacterEvent.UP));
break;
case downButton:
dispatchEvent(new CharacterEvent(CharacterEvent.DOWN));
break;
case growButton:
dispatchEvent(new CharacterEvent(CharacterEvent.GROW));
break;
case shrinkButton:
dispatchEvent(new CharacterEvent(CharacterEvent.SHRINK));
break;
case rotateButton:
dispatchEvent(new CharacterEvent(CharacterEvent.ROTATE));
break;
case vanishButton:
dispatchEvent(new CharacterEvent(CharacterEvent.VANISH));
break;
default:
break;
}
}
}
}
Note that there's zero layout code. This code is dependent on a custom Event Class. I'm going to write that Event Class so that it always bubbles. That way, it can be dispatched anywhere on the display list and received at the top level:
package control {
class CharacterEvent extends Event {
public static var UP:String = 'characterUp';
public static var DOWN:String = 'characterDown';
public static var GROW:String = 'characterGrow';
public static var SHRINK:String = 'characterShrink';
public static var ROTATE:String = 'characterRotate';
public static var VANISH:String = 'characterVanish';
public function CharacterEvent(type:String) {
super(type, true, true);//always bubbles
}
public function clone():Event {
return new CharacterEvent(type);
}
}
}
Now, if you want to manually handle instantiation of the Symbol that has view.NavBar as its base class, it will look like this:
package {
public var navBar:NavBar;
class GameWorld {
public function GameWorld() {
try {
var navClass:Class = getDefinitionByName('NavBarSymbol') as Class;
} catch (e:Error) {
trace('You need to have a Library symbol called NavBarSymbol');
}
if (navClass) {
navBar = new navClass() as NavBar;
//unnecessary layout code here
//Note that this is NOT the responsibility of the child Class!
addChild(navBar);
}
//instantiate character
...
//by listening to the whole Document, you can add other things that
//dispatch Character events on the display list, like a keyboard listener
addEventListener(CharacterEvent.UP, moveCharacterUp);
//listeners for the rest of the character events...
}
public function moveCharacterUp(e:CharacterEvent):void {
//character move logic
}
//other handlers
}
}
Personally, I'd just add the navBar to the stage, and then there's no need to manage it at all (not even reference it with a variable), simply add the event listeners for the various character events.
The root of your problem doesn't seem to be the character code. However, I'm going to give you a few "best practice" pointers about it.
The convention in AS3 is for Class members (properties and methods) to be camel case starting with a lower case letter. So, characterPos() and characterMove().
Your Class already contains character in the name, so really these should just be pos() and move() (though there's no need now to shorten position()).
The only thing your child Classes are doing with their references to the parent are adding themselves. They don't need and shouldn't have a reference to the parent for this purpose. It is the parent's responsibility to add the Children (or the responsibility of the Flash Player if you use the stage).
That said, you could give your Character a reference to the parent Class typed as IEventDispatcher and allow the Character to listen to this channel. This concept is called an event bus.
Note that the reason that so many people do what you're doing is that Adobe failed to document how to properly use OOP with the timeline. Unfortunately, by the time a few of us started documenting that around late 2009/early 2010, the damage was done and everyone assumed that if you wanted to write good code you had to pretend the timeline and stage didn't exist.
I know I've covered a lot of ground, and probably most of what I said directly contradicts what you thought you knew, so please don't hesitate to ask any questions you might have.