Accessing different Classes in Actionscript 3 - actionscript-3

I am trying to program a little game but stumbled upon some problems.
I have several classes. One of them is the wall class. Which when hitting the player should remove a heart from the health bar.
Hitting the player works pretty good but I cant remove the heart which is inside the UI class.
Main.as
public class TwinRunner extends MovieClip
{
private var _timer:Timer;
private var _userInterface:UI = new UI();
public function TwinRunner()
{
//Timer initialize
_timer = new Timer(800, 1);
_timer.addEventListener(TimerEvent.TIMER_COMPLETE, onUpdateTime);
_timer.start();
stage.addChild(_userInterface);
mcButton.addEventListener(MouseEvent.CLICK, onButtonClick);
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
Here I am adding the UI class as an instance.
Wall.as
x += _vx;
//Remove Wall when visible Sword hits
if(MovieClip(parent).mcSword.isVisible)
{
if(this.hitTestObject(MovieClip(parent).mcSword))
{
trace("wall destroyed");
parent.removeChild(this);
}
}
//Remove Wall when Player hit
else if(this.hitTestObject(MovieClip(parent).mcPlayer))
{
trace("player hit");
parent.removeChild(this);
MovieClip(parent)._userInterface.heartMeter = false;
}
//When the Wall is outside of the screen it gets removed
if (x + width / 2 < 0)
{
parent.removeChild(this);
}
}
MovieClip(parent)._userInterface.heartMeter = false; <- This part gives me an error...
And last the UI.as
public class UI extends MovieClip
{
private var heart1:heartFill = new heartFill();
public function UI()
{
addChild(heart1);
heart1.x = 200;
heart1.y = 200;
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(event:Event):void
{
addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onRemovedFromStage(event:Event):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
}
private function onEnterFrame(event:Event):void
{
}
public function set heartMeter(visibility:Boolean):void
{
heart1.visible = false;
}
}
I can access the setter function from the Main class but not from the wall.as class.
Is there a way to do so? I am still learning so I hope this is not such a stupid question :D
Thanks in advance!

Your problem is that you're removing the child (wall) from the parent, which then casuses the parent var to be null - see comments in code below:
parent.removeChild(this); //this will make parent null
MovieClip(parent)._userInterface.heartMeter = false; //parent is now null because this has been removed
Simply switching those two lines around should fix your main issue.
Also, _userInterface is declared as a private var in your main class, so if you want to access it in your wall class (like you are trying to do), you'll need to make it public or you'll get another error.
Something you may want to think about, is using events instead. That way, your code isn't directly referencing other classes like the UI. separation of class dependencies makes testing and troubleshooting easier.
So something like this in your wall class:
else if(this.hitTestObject(MovieClip(parent).mcPlayer))
{
trace("player hit");
stage.dispatchEvent(new Event(PLAYER_HIT)); //PLAYER_HIT would be a public static constant in this class: public static const PLAYER_HIT:String = "PlayerHit";
parent.removeChild(this); //this needs to be last because after this line, stage and parent will be null
}
Then in your UI Class:
private function onAddedToStage(event:Event):void
{
addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(Wall.PLAYER_HIT,playerHitHandler);
}
private function playerHitHandler(e:Event):void {
heartMeter = false;
}

Related

object's stage reference lost when re-instantiated after being removed from memory & displaylist

I'm building a game. When you die or win a level you're prompted to continue or return to the main menu. If you chose to go to the main menu you can start a new game. When you start a new game, the game is object is created again & it's children have lost their reference to the stage. I'm not sure why this is happening and I've spent over a week trying to figure out why. Here's some code (and descriptions of the code) from the game that should hopefully provide enough insight as to why the problem may be occurring:
if the "new game" button is clicked on the startMenu the NavigationEvent.START event is dispatched. a LevelEvent.COMPLETE event is dispatched by WeeBeeGame when the level is completed.
public class DocumentClass extends MovieClip {
public var startMenu:StartMenuGenerator = new StartMenuGenerator();
public var weeBeeGame:WeeBeeGame;
public var youWonBox:YouWonBox = new YouWonBox();
public function DocumentClass() {
// constructor code
addChild(startMenu);
startMenu.addEventListener(NavigationEvent.START, startGameHandler);
}
public function startGameHandler(e:NavigationEvent) : void {
this.removeChild(startMenuBG);
removeChild(startMenu);
weeBeeGame = new WeeBeeGame();
this.addChild(weeBeeGame);
weeBeeGame.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler);
}
public function levelCompleteHandler(e:LevelEvent) : void {
youWonBox.x = this.stage.stageWidth/2;
youWonBox.y = this.stage.stageHeight/2;
addChild(youWonBox);
youWonBox.addEventListener(MouseEvent.CLICK, mouseClickHandler);
}
private function mouseClickHandler(e:MouseEvent) : void {
if(e.target.name === "mainmenubtn"){
mainmenuHandler();
}
}
private function continueHandler() : void {
youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
private function mainmenuHandler() : void {
youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
WeeBeeGame.collisionDOC.removeChildren();
removeChild(weeBeeGame);
weeBeeGame = null;
this.addChild(startMenuBG);
addChild(startMenu);
removeChild(youWonBox);
}
}
the code that dispatches a LevelEvent.COMPLETE event is not shown but it dispatches when the level is complete. collisionDOC needs to be static because it's needed in a lot of other classes and holds the display objects needed for a 3rd party pixel-level collision detection.
public class WeeBeeGame extends MovieClip {
public var bee: Bee;
public var beeHurt:BeeHurt;
public var spawningDaemon:SpawningDaemon;
public static var collisionDOC:DisplayObjectContainer;
public function WeeBeeGame() {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
}
private function addedToStageHandler(e:Event) : void {
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
collisionDOC = new MovieClip();
addChild(collisionDOC);
bee = new Bee();
collisionDOC.addChild(bee);
beeHurt = new BeeHurt(bee.x, bee.y);
addChild(beeHurt);
beeHurt.visible = false;
spawningDaemon = new SpawningDaemon(currentLevel);
this.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler, false, 0, true);
}
private function levelCompleteHandler(e:LevelEvent) : void {
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
}
the first line to throw a 1009 error (Cannot access a property or method of a null object reference) is the line containing stage.mouseX because the stage reference is null.
public class Bee extends MovieClip {
public function Bee() {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(e:Event) : void {
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
this.x = this.stage.stageWidth / 2;
this.y = this.stage.stageHeight - this.stage.stageHeight / 9;
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
}
private function enterFrameHandler(e:Event) : void {
if(stage == null){
trace('stage is null' + stage.mouseX);
}
}
}
When the swf is first opened and a new WeeBeeGame is created its children have references to the stage. But when WeeBeeGame and it's children are removed from the display list and memory they lose reference and even if they're re-instantiated their references are still lost. How do I fix this? I'm very confused. Thank you all!!
ENTER_FRAME handlers continue to execute even when the display object is removed form the stage. So when you removeChild(weeBeeGame) those ENTER_FRAME handlers are still trying to access stage.mouseX each frame. You need to stop the ENTER_FRAME handlers.
Probably the easiest fix is to add an Event.REMOVED_FROM_STAGE handler to remove the Event.ENTER_FRAME handlers.
A better fix IMO is to not add any ENTER_FRAME handlers from your game objects, but rather expose a public function like update() which gets called from a single ENTER_FRAME handler in your WeeBeeGame when the game is running. Then to stop the game completely you can stop all updates by simply removing that single ENTER_FRAME handler. This is a common practice.

AS3: How can I add a children (instance) on stage every time I press the button without replacing the existing children?

How can I add a children (instance) on stage every time I press the button without replacing the existing children?
I have four Classes: Symbol1, Symbol3, Symbol4, all.
When I Press Symbol3 which is a button I want to create an instance of Symbol1 on the stage through class all.as. With Symbol4 I want to delete one of the created instance in order of creation on stage.
Example: I have pressed Symbol3 three times and I have created three instances of Symbol1 on stage. Now if I press Symbol4 I will delete the first created instance. If I press Symbol4 one more time I will delete the second created instance.
public class Symbol3 extends SimpleButton
{
private var creator:all;
private var child:Symbol1 = new Symbol1 ;
private var child2:Symbol1 = new Symbol1 ;
private var child3:Symbol222 = new Symbol222 ;
public function Symbol3()
{
addEventListener(MouseEvent.CLICK, onCLICK);
}
private function onCLICK(s:MouseEvent)
{
creator = new all(child);
stage.addChild(creator);
}
}
.
public class all extends MovieClip
{
private var _thief1:MovieClip;
public function all(par1:MovieClip)
{
_thief1 = par1;
addEventListener(Event.ADDED_TO_STAGE, onADDED_TO_STAGE);
}
private function onADDED_TO_STAGE(e:Event)
{
removeEventListener(Event.ADDED_TO_STAGE, onADDED_TO_STAGE);
this.addChild(_thief1);
_thief1.x = Math.random() * 200;
_thief1.y = Math.random() * 200;
}
}
.
public class Symbol4 extends SimpleButton
{
public function Symbol4()
{
addEventListener(MouseEvent.CLICK, onCLICK);
}
private function onCLICK(s:MouseEvent)
{
stage.removeChild(?);
}
}
This I have so far.
Thanks
You should put all your addable/removable sprite in one same container, let's call it container.
Then the add button will look like this:
private function onCLICK(s:MouseEvent)
{
container.addChild(new all(new Symbol1()));
}
And the remove button:
private function onCLICK(s:MouseEvent)
{
container.removeChildAt(0);
}
When removing the child on layer 0, the other children will go one layer down and the next child to remove will come on 0.
Thanks for the help Kodiak!
I made it finally. I am not sure if this is the right approach but at least it works.
I have three Classes:
AddChild2.as - linkage to Button
Creator.as
Ship2.as - linkage to MovieClip
The tricky moment was that the stage had to be transferred as a parameter to avoid error:1009. The other think is the empty constructor function of the Creator that makes the code more flexible and independent. Now Creator can produce any passed movieClip. Again I believe that there is another better way to do this, so any improvement is welcome.
public class AddChild2 extends SimpleButton
{
private var creatorche:Creator = new Creator;
private var s:Ship2;
public function AddChild2()
{
// constructor code
addEventListener(MouseEvent.CLICK, onCLICK)
}
private function onCLICK(e:MouseEvent)
{
s = new Ship2;
creatorche.onCreator(s, stage);
}
}
.
public class Creator extends MovieClip
{
private var ship:MovieClip;
public function Creator()
{
// constructor code
}
public function onCreator(par1:MovieClip, par2:Stage)
{
ship = par1;
par2.addChild(ship);
ship.x = Math.random() * 200;
ship.y = Math.random() * 200;
}
}
.
public class Ship2 extends MovieClip
{
public function Ship2()
{
// constructor code
}
}

How to correctly use KeyboardEvent on Flash

I have a problem making a KeyboardEvent work in the game I'm starting. I have three classes, one for handling the levels, one that is the actual level and one to represent the avatar:
Level
import flash.display.MovieClip;
import flash.events.Event;
public class Fase extends Cena
{
var avatar:Avatar;
public function Fase()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
avatar = new Avatar();
this.addChild(avatar);
avatar.x = stage.width/2;
avatar.y = 30;
}
public function die()
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
(this.parent as ScreenHandler).removeChild(this);
}
}
Avatar
public class Avatar extends MovieClip
{
public function Avatar()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
//stage.focus=this;
this.addEventListener(KeyboardEvent.KEY_DOWN, apertou);
}
public function apertou(event:KeyboardEvent)
{
trace("o");
if(event.keyCode == Keyboard.LEFT)
{
this.x++;
}
}
}
I have all the packages on both classes an all works if I use the stage.focus=this on the Avatar, but if I click somewhere else during game excecution the focus is lost and it doesn't work anymore. Please can anyone help me?
Thanks in advance
Keyboard events only trigger when the object they're assigned to are the current focus.
Fortunately, the stage always has focus by default. This means you can add your event listeners to the stage to always have the keyboard events trigger as expected:
stage.addEventListener(KeyboardEvent.KEY_DOWN, apertou);
You can move the key handler from the avatar to the level or stage and then move your avatar in there.
public class Fase extends Cena
{
var avatar:Avatar;
public function Fase()
{
// constructor code
this.addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
public function onAdded(e:Event)
{
avatar = new Avatar();
this.addChild(avatar);
avatar.x = stage.width/2;
avatar.y = 30;
addEventListener(KeyboardEvent.KEY_DOWN, apertou);
}
public function die()
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
(this.parent as ScreenHandler).removeChild(this);
}
public function apertou(event:KeyboardEvent)
{
if(event.keyCode == Keyboard.LEFT)
{
avatar.x++;
}
}
}

AS3 reference custom property in a custom class for a library symbol

So I've done a fair amount of searching through SO and couldn't quite find the answer to this question. I have a movieclip in my symbol library that's exported for actionscript, and I've written a custom class for it. It mostly works great except for when I try to access a custom private property after I've added the movieclip to the stage. Below's an example:
package {
public class MyMovieClip extends MovieClip {
private var _isEnabled:Boolean = false;
public function MyMovieClip():void {
trace(this);
}
public function set isEnabled( b:Boolean ):void {
_isEnabled = b;
}
public function get isEnabled():Boolean {
return _isEnabled;
}
}
}
And then I have another class where I am adding instances of the movieclip to the stage in a loop:
package {
public class MyOtherClass extends MovieClip {
public var myMC:MyMovieClip;
public var docClass:*;
public function MyOtherClass( docRef:* ):void { // passing in a reference to the DocumentClass so I can access the stage
docClass = docRef;
init();
}
public function init():void {
for(var i:int=0; i<6; i++) {
var myMC:MyMovieClip = new MyMovieClip; // instantiate the movieclip which is exported for actionscript and has a custom class
//set a few native properties
myMC.name = "myMC" + i; //setting the name so I can reference this movieclip after it's been added to stage
myMC.y = myMC.height * i + 20;
myMC.x = 20;
myMC.alpha = .7;
}
dispatchEvent(new Event(MyOtherClass.MOVIECLIPS_ADDED)); // just to be safe, let's dispatch a custom event when all movieclips have been added
}
public function traceEnabled():void {
trace(docClass.stage.getChildByName("myMC1").isEnabled); // this throws: 1119: Access of possibly undefined property isEnabled through a reference with static type flash.display:DisplayObject
}
}
}
And finally I instantiate MyOtherClass inside my document class:
package {
public class DocumentClass extends MovieClip {
public var myOtherClass:MyOtherClass;
public function DocumentClass():void {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
public function onAddedToStage(e:Event):void {
myOtherClass = new MyOtherClass(); // upon instantiation, init is called in MyOtherClass and all of my movieclips are added to the sage
}
}
}
What gives? Why can't I access the MyMovieClip property, isEnabled, after it's been added to the stage? Is there another way? (Thanks in advance for any help)
Internally all children of a DisplayObjectContainer are referenced as DisplayObject, so when you use getChildByName, it returns a DisplayObject.
In order to access your custom properties without causing a compile-time error, you would need to cast the result of getChildByName as the Class of your custom properties. See the code below.
That however isn't your only issue (though it's the reason for the error, once you correct you will get runtime errors as well).
In your creation loop, your not adding myMC to the display list, so calling stage.getChildByName() will return null because your clips aren't on the stage.
Your also not adding your myOtherClass to the display list in the posted code.
Also, storing a reference to the document class isn't really needed. Just add the addedToStage listener in MyOtherClass and have the handler be init.
HERE IS SOME UPDATED CODE
For your MyOtherClass:
public function MyOtherClass():void {
if(stage){
init(); //if stage is ready, call init, if not wait for the added to stage event
}else{
addEventListener(Event.ADDED_TO_STAGE,init);
}
}
public function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE,init);
for(var i:int=0; i<6; i++) {
var myMC:MyMovieClip = new MyMovieClip;
myMC.name = "myMC" + i; //setting the name so I can reference this movieclip after it's been added to stage
myMC.y = myMC.height * i + 20;
myMC.x = 20;
myMC.alpha = .7;
addChild(myMC); //!!!! add to the displayList
}
dispatchEvent(new Event(MyOtherClass.MOVIECLIPS_ADDED)); // just to be safe, let's dispatch a custom event when all movieclips have been added
}
public function traceEnabled():void {
var myMC:MyMovieClip = this.getChildByName("myMC1") as MyMovieClip; //!!! cast it as MyMovieClip so you have access to all the properties/methods in that class
if(myMC){ //myMC will be null if the cast failed
trace(myMC.isEnabled);
}
}
/*
getChildByName is slow and cumbersome. Most people generally only use it for accessing things put on the timeline in the Flash IDE. Using events is a much better way of accessing your items. If traceEnabled was caused by a mouse event attached to myMC, then this would be a much better implementation:
*/
public function betterTraceEnabled(e:Event):void {
var myMC:MyMovieClip = e.currentTarget as MyMovieClip;
if(myMC){
trace(myMC.isEnabled);
}
}
AND YOUR DOCUMENT CLASS:
public class DocumentClass extends MovieClip {
public var myOtherClass:MyOtherClass;
public function DocumentClass():void {
if(stage){
onAddedToStage(null); //most of the time stage is already populated in the constructor of your document class
}else{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
}
public function onAddedToStage(e:Event):void {
myOtherClass = new MyOtherClass(); // upon instantiation, init is called in MyOtherClass and all of my movieclips are added to the sage
addChild(myOtherClass); //add it to the displayList
}
}

how to add a limited amount of objects to the stage

So I have a box exported as Box in my library. I have tried :
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var _box:Box=new Box ;
private var boxAmount:Number=0;
private var boxLimit:Number=16;
private var _root:Object;
public function Main() {
addEventListener(Event.ENTER_FRAME, eFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
}
private function eFrame(event:Event):void {
if (boxAmount <= boxLimit) {
boxAmount++;
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
} else if (boxAmount >= boxLimit) {
removeEventListener(Event.ENTER_FRAME, eFrame);
} else {
addEventListener(Event.ENTER_FRAME, eFrame);
}
}
}
}
But it did not work as planned.
What I am trying to do is make my box stay on the screen at a random place on the stage and remove it when clicked (but that will come later). This code is for some reason adding the object to the stage and then removing it and adding it again up to 16 times.
Thanks
I it seems like you have created one _box, and re-add it to the timeline on enter frame. It should work if you create a new box instance inside the eFrame function rather than before it, then you keep reassigning to the same variable name, rather than reusing the one object eg:
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var boxAmount:Number=0;
private var boxLimit:Number=16;
private var _root:Object;
public function Main() {
addEventListener(Event.ENTER_FRAME, eFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
}
private function eFrame(event:Event):void {
if (boxAmount<=boxLimit) {
boxAmount++;
var _box:Box=new Box ;
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
} else if (boxAmount >= boxLimit) {
removeEventListener(Event.ENTER_FRAME, eFrame);
} else {
addEventListener(Event.ENTER_FRAME, eFrame);
}
}
}
}
In your code you are only ever creating one box. Your enterFrame handler is just assigning it a new random position 16 times. If you want 16 boxes you'll need to create a new box each time in the enterFrame function.
But you don't need to use the ENTER_FRAME event here. You could just use a for loop or a while loop to create the 16 boxes.
Here's some code:
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var boxAmount:Number=0;
private var boxLimit:Number=16;
public function Main() {
addBoxes();
}
private function addBoxes():void {
while (boxAmount<=boxLimit) {
boxAmount++;
var box:Box = new Box();
box.y=Math.random()*stage.stageHeight;
box.x=Math.random()*stage.stageWidth;
addChild(box);
// listen for mouse clicks
box.addEventListener(MouseEvent.CLICK, onBoxClick);
}
}
private function onBoxClick(e:MouseEvent):void {
var clickedBox:Box = e.target as Box;
removeChild(clickedBox);
}
}
}
I removed your enterFrame handler and just made a function called addBoxes. I'm using a while loop to crate the boxes. Notice that each time through the loop I'm creating a NEW box, not just reusing the old one. I'm also adding a mouse click event listener to each box so it can be removed from the stage when clicked.
You'll surely want to change some of this to get it to work for your purposes, but it should get you headed in the right direction.
What you have at the moment is just repositioning the same box over and over because you only ever create one Box instance. You need to create multiple instances of Box and add them to the stage individually.
package {
import flash.display.MovieClip;
import flash.events.*;
public class Main extends MovieClip {
private var boxAmount:Number=0;
private var boxLimit:Number=16;
private var _root:Object;
public function Main() {
addEventListener(Event.ENTER_FRAME, eFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
}
private function eFrame(event:Event):void {
if (boxAmount<=boxLimit) {
boxAmount++;
//create a new box instance
var _box:Box = new Box();
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
} else {
removeEventListener(Event.ENTER_FRAME, eFrame);
}
}
}
}
Although the variable boxAmount suggests otherwise, you said you only want one box. So, to do this, you just need to move the following lines into the constructor (Main).
_box.y=Math.random()*stage.stageHeight;
_box.x=Math.random()*stage.stageWidth;
addChild(_box);
Then remove or disable the enter frame event. You don't need it in this case. To check if the box got clicked, attach the listener to the box itself, not to it's parent:
_box.addEventListener(MouseEvent.MOUSE_DOWN, mouseclick);
if (boxAmount<=boxLimit) {
// ...
} else if (boxAmount >= boxLimit) {
// ...
} else {
// ...
}
This part looks really strange. The first condition covers a case that is also covered by the second condition, together they already cover all possible cases. boxAmount is either less or equals to boxLimits or it is greater than it. Checking for equality twice is confusing. There is no need to include the last else statement. It actually has the same behaviour as the following code.
if (boxAmount<=boxLimit) {
// ...
} else if (boxAmount > boxLimit) {
// ...
}