AS3: Accessing custom class public functions from a MovieClip on a timeline - actionscript-3

I've got a AS3 program with a Main.as custom class.
In this class I load an instance of a 'menu' movieclip which has simpleButton instances inside... How do I access the Main class public functions by the menu movieclip buttons?
I.e. Menu button -> gotoPage(5); (which is a Main public function)
If I try to access the Main function with the above statement, it gives
"1180: Call to a possibly undefined method gotoPage.

Create a static method called GetMain() on the Main class that would return the instance of Main (Main should be a singleton).
package whatever
{
public class Main
{
private static var _instance:Main = null;
public static function getMain():Main
{
return _instance;
}
// Main constructor
function Main(..):void
{
_instance = this;
}
}
}
To refer to the instance of Main() from your Menu class, you could use:
Main.getMain().gotoPage(5);

You want to do this with events. If your menu movieclip is a child of Main.as as you say, name the instance buttons inside of the menu movieclip, and set up the listeners in Main.as:
1) Put the below code in the constructor: public function Main(){...
menu.button_a.addEventListener(MouseEvent.CLICK, onButtonClick);
menu.button_b.addEventListener(MouseEvent.CLICK, onButtonClick);
2) and then write the onButtonClick function in Main.as
private function onButtonClick(e:MouseEvent):void{
switch(e.currentTarget.name){
case "button_a":
//call the Main.as function you want here
break;
case "button_b":
//call a different Main.as function
break;
}

ruedaminute's answer on dispatching events from the buttons and having main process those events is by far the best way to handle this, but there are many ways to do this in as3 - but try to use the aforementioned technique. Some of the other techniques.
Make a function in Main such as public function GotoPage(iPageNum:int):void{}
from a button - try this._parent.GotoPage(1);
but this._parent might not be main, do a trace(this._parent), and keep trying
it might end up being
this._parent._parent._parent.GotoPage(1) depending on your display tree hierachry.
Again, this is REALLY bad OOP practices, but well, it will work.
Another tecnique - use a singleton for main- looks like u already are - add that same public method, then from the button click, you could do Main.getMain().GotoPage(1);
That is a bit better, in that you can change the display tree and not have to figure out where the heck Main is in the display tree, but singletons also are discouraged for a variety of reasons, but in this case I would say it makes since.
Good Luck!
~ JT

Related

method (function) in subClass not being called on mouse event

My goal is:
define a subClass of Sprite called Ship
use an event at runtime to call a function within this new class
It seems that I've figured out how to create my Ship class using a package in a linked .as file. But I can't seem to access the function within that class. Can anyone see what I'm doing wrong?
var ShipMc:Ship = new Ship();
addChild(ShipMc);// This successfully adds an instance, so I know the class is working.
addEventListener(MouseEvent.CLICK, ShipMc.addShip);//But this doesn't seem to run the function
This code works fine for instantiating a Sprite, but the code in the Ship.as file, specifically the function, is not working. No runtime errors, but nothing traced to the output window, either.
package
{
import flash.display.Sprite
public class Ship extends Sprite
{
public function addShip():void
{
trace("running addShip function")
}
}
}
The last time a coded anything in flash it was AS2!
I'll just mention that I've tried using addShip():void and just addShip(). Same response with both. It should be with :void, right? Anyway, the fact that neither one throws, tells me that this section of code isn't even getting read, I think.
Any help is much appreciated! Pulling my hair out.
Your code is not working because it contains some problems, so let's see that.
You should know that you are attaching the MouseEvent.CLICK event listener to the main timeline which didn't contain any clickable object yet now (it's empty), so let's start by adding something to your Ship class to avoid that :
public class Ship extends Sprite
{
// the constructor of your class, called when you instantiate this class
public function Ship()
{
// this code will draw an orange square 100*100px at (0, 0)
graphics.beginFill(0xff9900);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}
public function addShip():void
{
trace("addShip function run");
}
}
N.B: You can attach the MouseEvent.CLICK event listener to the stage, which will work even if you have nothing in the stage.
Now, if you test your app, you'll get an clickable orange square at the top left corner of your stage, but the compiler will fire an error (ArgumentError) because it's waiting for a listener function (the Ship.addShip() function here) which accept an MouseEvent object.
So to avoid that error, your Ship.addShip() function can be like this for example :
public function addShip(e:MouseEvent):void
{
trace("addShip function run");
}
Then your code should work.
You can also simplify things by using another listener function in your main code which can call the Ship.addShip() function, like this for example :
var ShipMc:Ship = new Ship();
addChild(ShipMc);
addEventListener(MouseEvent.CLICK, onMouseClick);
function onMouseClick(e:MouseEvent): void
{
ShipMc.addShip();
}
For more about all that, you can take a look on AS3 fundamentals where you can find all what you need to know about AS3.
Hope that can help.

Making a simple tamagoci game getting no compiler errors but receiving no output

Kind of new Actionscript and I'm just trying to make a simple tamagoci game. I've wrote all the code out but and receiving no compiler errors but for some reason I'm also not receiving any output messages for my mouse event listeners. Here is all my code, I really can't find the problem and any help would be greatly appreciated. Thanks.
package{
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class Main extends MovieClip{
public var feedButton:MovieClip;
public var tamagoci:MovieClip;
public var disButton:MovieClip;
public var dietButton:MovieClip;
public function Main() {
this.init();
}
private function init():void {
this.feedButton.addEventListener(MouseEvent.MOUSE_DOWN, onfeedMouseDownHandler);
this.disButton.addEventListener(MouseEvent.MOUSE_DOWN, ondisMouseDownHandler);
this.dietButton.addEventListener(MouseEvent.MOUSE_DOWN, ondietMouseDownHandler);
}
private function onfeedMouseDownHandler(event:MouseEvent)void{
this.tamagoci.scaleX += 0.1;
this.tamagoci.scaleY += 0.1;
}
private function ondisMouseDownHandler(event:MouseEvent)void{
this.tamagoci.gotoAndPlay(5);
}
private function ondietMouseDownHandler(event:MouseEvent)void{
this.tamagoci.scaleX -= 0.1;
this.tamagoci.scaleY -= 0.1;
}
Are you using Flash Professional?
You're declaring your variable types in your class here;
public var feedButton:MovieClip;
public var tamagoci:MovieClip;
public var disButton:MovieClip;
public var dietButton:MovieClip;
But then in your constructor, all you are doing is running init();
public function Main() {
this.init();
}
So, this could one of a few things. The most likely is that you have declared your variables, but you haven't initialised them. You've created the variables to hold your objects, but according to your code, they're empty. More specifically, a variable or class property that doesn't assign an object to a variable of an object type will contain a default value of null.
You could prove this in your code by simply putting a condition inside your init(); method;
if(tamagoci == null){
trace("I haven't been assigned an object of type class yet!")
}
So it could be 1 of these 3 things:
1: If you have written your own classes for these class properties/variables, then you need to instantiate them with the new keyword. The general syntax is;
variable_name = new ClassName(parameter_1, parameter_2);
If you are using classes you have written yourself, you have to create an instance of the object, assign it to a variable, and then add it to the stage using addChild();. For example, lets say you've written your own Tamagoci class;
tamagoci = new Tamagoci();
tamagoci.x = 100; // set the x location
tamagoci.y = 200; // set the y location
addChild(tamagoci);
Notice the use of Tamagoci. This is just an example, but this is the class name, which shouldn't be confused with variable/property name. It could just have easily been;
tamagoci = new MovieClip();
But then, this is just an empty MovieClip. It needs a property to display on the screen. A Shape, A Bitmap, or another container class object like MovieClip or Sprite (container classes allow you to nest display objects inside them). But on a basic level, it must contain a visual component to appear on the stage.
2:
Have you made Main your document class? This is the class which will get automatically called when your Flash movie plays. To set this, click on your stage, and in the properties dialogue box on the right, under PUBLISH, type in the name of your class, which is "Main".
3:
If you have created MovieClips in your library in Flash Professional, then you need to go to your library, right click the MovieClips, and select properties. From there, you need to make sure Export for Actionscript is ticked.
Now, if you click on your MovieClips on the stage, then open the Properties tab in the top right of Flash Professional's default layout, then right at the top should be a text field, and if you hover over it, Instance name will pop up as a tool tip. This is where you name your stage objects. Once that is done, you have access to them in your timeline.
If this is how you've done this, then you don't need to declare the variables in your main class, as they are already declared on your stage by Flash Professional and instantiated automatically.

Purpose of class extends MovieClip AS3

I have just started learning AS3 and my school has provided some Class files with the basic structure already written. Here is an example:
package {
//Add in your import statements here
//...
public class MathsQuiz extends MovieClip
{
//Add in your class variables here
//...
public function MathsQuiz()
{
}
public function startGame()
{
//Get the game loop to execute
addEventListener(Event.ENTER_FRAME,update);
}
public function update(evt:Event)
{
//This is the game loop
//Handle user input
//Handle game logic
//Handle display
}
}//end class
}//end package
My Question is, what does "extends MovieClip" actually do?
Thank you for your time!
The MovieClip class inherits from the following classes: Sprite, DisplayObjectContainer, InteractiveObject, DisplayObject (can be added to the display list, moved around via its x and y properties as Marcela said), and EventDispatcher, but unlike the Sprite object, a MovieClip object is Dynamic (A dynamic class defines an object that can be altered at run time by adding or changing properties and methods. A class that is not dynamic, such as the String class, is a sealed class. You cannot add properties or methods to a sealed class at run time.) and has a timeline.
If your class doesn't use timeline (looks like that is the case), you can extends Sprite and will have the same results and better performance. If you want a dynamic class you can just use the dynamic attribute when you declare a class.
The extends keyword allows a class to inherit any publicly-accessible or protected member variables and functions of a base class (in this case, MovieClip).
In this example, this means that MathsQuiz, on top of any functionality you may add, will also function as a MovieClip. On a basic level, this means that it is a display object that can be added to the display list and moved around via its x and y properties.
For a more in-depth understanding, do some research on OOP Inheritance.

How to change the value of a certain variable in the document class through another class?

here is my problem. In my document class TowerDefenseGame.as, I defined a variable Turrent1Flag:
package
{
import flash.display.MovieClip;
import flash.events.*;
import flash.display.Sprite;
import flash.display.Shape;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldType;
public class TowerDefenseGame extends MovieClip
{
public var Turrent1Flag==0;
}
public function TowerDefenseGame()
{
......
}
Now, in another class Turrent1Button.as, I need to create a mouse click event, by which the Turrent1Flag is set to 1:
package
{
import flash.display.MovieClip;
import flash.events.*;
import flash.display.Sprite;
import TowerDefenseGame;
public class TurretButton1 extends MovieClip
{
public var ButtonBase:Sprite=new Sprite();
public var TurretBase:Sprite=new Sprite();
public var Gun:Sprite=new Sprite();
public function TurretButton1()
{
......
this.addEventListener(MouseEvent.CLICK, MouseClick);
}
public function MouseClick(event:MouseEvent):void
{
MovieClip(root).Turret1Flag = 1;
}
Well, this does not work. I am using Adobe flash cs6 and it says the value cannot be accessed. Someone know how to do this?
try this:
private static var _instance:TowerDefenseGame;
public static function get instance():TowerDefenseGame { return _instance; }
public function TowerDefenseGame()
{
_instance = this;
}
public function MouseClick(event:MouseEvent):void
{
TowerDefenseGame.instance.Turret1Flag = 1;
}
So, to start out, AS3 makes it difficult to so what you've been used to doing in AS2 on purpose, to allow for better Object Oriented practices. When you maintain high and tight walls between Classes, it becomes easier to change Class A without having any effect whatsoever on Class B. Class B only knows about the doors and windows that are the "official" ways into the house of Class A, so it doesn't matter if you move the couch. You also make it extremely easy to replace Class A with Class C that has similar doors and windows.
When you reach through the walls by introducing global state, you can't replace Class B with Class C without changing Class A, because Class A has a direct reference to Class B and knows exactly where the couch is.
One way to handle this is through Inversion of Control (IoC). So, for our house, the couch might be supplied from outside and whatever component supplied it might keep a reference to it, so it would be able to access the couch no matter where in the house it went. So, you might choose to create your TurretButton and pass that into whoever owns that, while your TowerDefenseGame keeps a reference to it and listens directly to it, changing its own flag in response to the click.
Looking at your code, you probably don't need to go that far, because I don't see any sign that your TurretButton is actually nested. In that case, you can listen directly to it, whether you're creating it on the stage or creating it through code (which I'm not a fan of). If you're using the stage in the IDE and it exists on the stage, then just create a public variable of type TurretButton (or you could probably use SimpleButton since you no longer have need for a special Class here based on the code you've shown). That instance will be available in the constructor of your TowerDefenseGame. Just add your event listener to it, and then the listener and the variable you want to change are in the same scope. Ergo, the problem you were trying to solve never existed--you were simply looking at the problem from a perspective that overcomplicated things.
If, in fact, your code is nested in a way that's not shown, you can use ActionScript 3's event system, which is fabulous, to handle the issue without introducing direct coupling and without having to create the button through code and push it down to where it's used. One way is to just listen for any mouse click (since that is a bubbling event) and look to see what was clicked. Another solution is to generate a custom event from the button that you can then listen to from the top level to change the flag. That would look something like:
package view.button {
public class TurretButton extends MovieClip {
public function TurretButton() {
super();
mouseChildren = false;
addEventListener(MouseEvent.CLICK, broadcastTurretEvent);
}
protected function broadcastTurretEvent(e:Event):void {
dispatchEvent(new Event('turretClicked', true));//the true parameter makes it bubble
}
}
}
Then your tower Class would look like
package {
public class TowerDefenseGame extends MovieClip {
//the fact that you're calling it Turret1Flag suggests you're going to have more than 1
protected var turretFlags:Array /*of Boolean*/ = [false, false];
//your turret button instances
//doesn't show nesting, this is just to indicate that
//these are named instances so you know how what they're called
//they could be nested at any level
public var turret1:TurretButton;
public var turret2:TurretButton;
//...etc.
public function TowerDefenseGame() {
super();
addEventListener('turretClicked', onTurretClicked);
}
protected function onTurretClicked(e:Event):void {
//you can also just extract the number and do the math
//to get the correct array index
switch(e.target.name) {
case 'turret1':
turretFlags[0] = !turretFlags[0];
break;
case 'turret2':
turretFlags[1] = !turretFlags[1];
break;
}
}
}
}
Note how well this scales. You don't have to have a different Class for each button to change each separate flag. It's also easy to add more buttons without a whole lot of code changes. You could take this solution further and create a custom event that has a property that says which button was clicked, and you could supply the value to use to that for the button through dependency injection, etc.
I also made the assumption in my code that flags should be boolean and that they're turning on with the first click and off with the second click, etc. You could take the same idea and change it to, for example, increment with every click. I suspect you don't actually want to do what your code was showing and just turn it on with the first click and just leave it on forever.
Another note is that if you think you might want to code in AS3 over the longer term, you should probably learn the coding conventions that are used in AS3. One of these is that Class names start with a capital letter, but properties and methods do not.
It's probably not a great habit to get into for everything, but a static variable looks like it would work here.
public class TowerDefenseGame extends MovieClip
{
public static var Turrent1Flag = 0;
}
In Turrent1Button.as:
public function MouseClick(event:MouseEvent):void
{
TowerDefenseGame.Turret1Flag = 1;
}

eventDispatcher from a sub class to another class

I have been struggling with the following for a couple of hours now, how do you call a custom class from another class if you do not extend that class for example.
I have on my main Base class a timer event, Base.myTimer.addEventListener(TimerEvent.TIMER, processTime) - Base class
Then I remove the method later in the code Base.mytimer.removeEventListener(TimerEvent.TIMER, processTime. - Base class
I have a button (Btn class) that when its done processing I want to call that method again, but I can't get it to work because the method does not exist in the button class but on the Base class so flash obviously gives me the error processTime is not defined.
For example now I want to re-instantiate the event listener from within the button, so I have
Base.myTimer.addEventListener(TimerEvent.TIMER, processTime);
or this.parent.parent["myTimer"].addEventListener()
myTimer is a static Timer in Base class.
I can make a normal dispatchEvent if its not a custom method for example Base.myTimer.dispatchEvent(new TimerEvent(TimerEvent.TIMER)).
The examples I have seen so far have not resolved my issue. Any help would be appreciated.
Looks like the button class is part of the child tree of the Base class. In this case, you can just do a dispatchEvent from the button class when it is clicked
dispatchEvent(new Event("AddListenerAgain", true));
In the Base class, you must be having access to the button class already, hence you can say:
button.addEventListener("AddListenerAgain", addListener);
Then in the Base class
private function addListener(e:Event) : void {
myTimer.addEventListener(TimerEvent.TIMER, processTime);
}
In this example, I have dispatched and listened for raw strings. This is not a recommended practice. You must read on how to dispatch custom events to do it correctly.
You could pass a reference to an instance of Base class into your Button instance.
// Button class
package {
import Base;
// Other imports...
public class Button {
public function Button(base:Base):void {
// processTime and myTimer must be public.
// I put this line in the constructor for brevity, but if you stored base
// in an instance variable, you could put this anywhere in the button
// class.
Base.myTimer.addEventListener(TimerEvent.TIMER, base.processTime)
}
}
}
// Create button like this.
var button:Button = new Button(base);
// Or if button is created inside of Base
var button:Button = new Button(this);
Even better would be to create two methods in the Base class, for adding and removing the listeners, and make myTimer and processTime private:
public class Base {
public function addTimerListeners():void {
myTimer.addEventListener(TimerEvent.TIMER, processTime)
}
public function removeTimerListeners():void {
myTimer.removeEventListener(TimerEvent.TIMER, processTime)
}
}
Then you can call these two methods from outside of the class. This keeps the inner workings of your class more hidden. If you decided you wanted to change myTimer to a instance variable instead of static, you wouldn't have to make any changes to code outside of your Base class. This is called encapsulation and is a good practice.