Custom Events with Singleton EventDispatcher, using getDefinition from child - actionscript-3

EDIT: For whatever reason, it works in the browser but not when compiled/debugged within the IDE.
I can't get my external SWFs to pick up on dispatches from my singleton event manager (EventDispatcher). Here are the particulars:
I add children from an external SWF using the getDefinition method to my main SWF.
I'm using a singleton EventDispatcher that is in charge of listeners and dispatching.
Using a custom event class.
In this code, I am trying to get a mute button to tell the main SWF that the mute icon has been clicked (SoundClipEvent.MUTE_CLICK). After the sound has been muted, it should dispatch the event (SoundClipEvent.STATE) and confirm to the muteIcon the state. Currently, the mute icon successfully dispatches the MUTE_CLICK event and the main SWF document class is able to pick it up. MuteIcon (child SWF MC) hears nothing from the singleton.
Your help in this problem is greatly appreciated!
SoundClipManager.as:
import flash.events.Event;
import flash.events.EventDispatcher;
public dynamic class SoundClipManager extends EventDispatcher {
private static var isMuted:Boolean;
public function SoundClipManager(blocker:SingletonBlocker):void {
super();
//
if (blocker == null) {
throw new Error("Error: Instantiation failed; Use SoundClipManager.getInstance()");
}
}
public static function get muted():Boolean {
return SoundClipManager.isMuted;
}
public static function set muted(value:Boolean) {
SoundClipManager.isMuted = value;
//
SoundClipManager.getInstance().dispatchEvent(new SoundClipEvent(SoundClipEvent.STATE,SoundClipManager.muted));
}
public static function getInstance():SoundClipManager {
if (instance == null) {
instance = new SoundClipManager(new SingletonBlocker());
}
return instance;
}
public override function dispatchEvent(evt:Event):Boolean {
return super.dispatchEvent(evt);
}
private static function stateChanged(evt:*) {
trace('state changed!');
}
}
internal class SingletonBlocker {}
MuteIcon.as
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
//
public dynamic class IconMute extends MovieClip {
public function IconMute() {
this.addEventListener(Event.ADDED_TO_STAGE,this.addedToStage);
//
SoundClipManager.getInstance().addEventListener(SoundClipEvent.STATE,this.soundClipManagerStateChanged);
}
//
// Methods, Private
//
//
// Events
//
private function muteClick(evt:MouseEvent) {
SoundClipManager.getInstance().dispatchEvent(new SoundClipEvent(SoundClipEvent.MUTE_CLICK));
}
//
private function addedToStage(evt:Event) {
this.addEventListener(MouseEvent.CLICK,this.muteClick);
}
//
private function soundClipManagerStateChanged(evt:*) {
trace("state changed!");
}
}
SoundClipEvent.as
package {
//
import flash.events.Event;
//
public class SoundClipEvent extends Event {
public static const MUTE_CLICK:String = "muteClick";
public static const STATE:String = "state";
//
public var muted:Boolean;
public function SoundClipEvent(type:String,muted:Boolean = false) {
if(muted) this.muted = muted;
//
super(type,true,false);
}
}
}

getDefinitionByName(className) method will work only if className was mensioned somewhere in your code. You may just import className class in a file where you're going to call getDefinitionByName(className). That should help!

Related

as3 calling a function in Main.as Document Class from another class

I am sure this is a popular question but I can't find the exact answer I need. I simply need to access a function or functions created in the Main.as document class. I have tried several methods and they do not seem to work. Here is one example I tried.
anotherClass.as // This needs to access functions Main.as
package com
{
import Main;
public class anotherClass
{
private var stageMain:Main;
public function anotherClass()
{
// tries to call a function in Main.as called languageLoaded. NO WORK!
stageMain.languageLoaded("English");
// in the Main.as languageLoaded is a public function
}
}
}
The cleaner way is to simply pass a reference to Main to the constructor of the class you want to be able to access it.
For example, your AnotherClass could look like this:
class AnotherClass
{
private var _main:Main;
public function AnotherClass(main:Main)
{
_main = main;
_main.test(); // Success!
}
}
And your main class:
class Main
{
public function Main()
{
var another:AnotherClass = new AnotherClass(this);
}
public function test():void
{
trace("Success!");
}
}
public class MainDoc extends MovieClip // as long as it extends eventDispatcher you re fine
{
private var otherClass:OtherClass;
public function MainDoc()
{
otherClass = new OtherClass();
otherClass.addEventListener("otherClassCustomEvent", onOtherClassReady);
otherClass.startLogic();
}
public function onOtherClassReady(event:Event = null)
{
trace("from other class:", otherClass.infoToShare) // traces "from other class: YOLO!"
}
}
public class OtherClass extends EventDispatcher // must extend the event dispatcher at least
{
public var infoToShare:String;
public function OtherClass()
{
}
public function startLogic()
{
// do what you got to do
// when you have your data ready
infoToShare = "YOLO!";
dispatchEvent("otherClassCustomEvent");
}
}
Once you're confortable with that, you can start looking into building custom events that could carry the variable to send back
Ok I got the following code to work. It's really a messy solution but I didn't know of a better way. It works. I just hope it's stable and does not use a lot of resources.
If you have a much better Idea I am open.
Here is the MainDoc.as
package {
import flash.display.MovieClip;
import flash.events.*;
import com.*;
import com.views.*;
import flash.display.*;
import flash.filesystem.*;
import com.greensock.*;
import com.greensock.easing.*;
import flash.system.System;
public class mainDoc extends MovieClip
{
/// (Get Main Doc flow) this creates an instace of the main timeline
/// and then I send it
private static var _instance:mainDoc;
public static function get instance():mainDoc { return _instance; }
/// Calls the defaultVars.as in to "vars".
var vars:defaultVars = new defaultVars();
public function mainDoc()
{
/// Makes this class ready to be passed to defautVars.as
_instance = this;
// Sends the _instance to defaulVars.as to be accessed later.
vars.getMainDoc(_instance);
// Calls a function in defaultVars.as and loads a var
vars.loadButtonVars("English");
}
}
}
Here is the defaultVars.as
package com {
import flash.display.Stage;
import flash.events.*
import flash.net.*;
import flash.display.*;
import flash.filesystem.*;
public class defaultVars
{
/// Makes the MainDoc.as a MovieClip
// Not sure if this is good but it works.
public var MainDoc:MovieClip;
public function defaultVars()
{
}
public function getMainDoc(_instance:MovieClip)
{
trace("CALLED" + _instance);
/// receives the _instance var and its converted to a MovieClip
// This can now be used in any function because I declared it a public var.
MainDoc = _instance;
}
public function loadButtonVars(Language:String)
{
myLoader.load(new URLRequest("Languages/" + Language + "/vars.xml"));
myLoader.addEventListener(Event.COMPLETE, processXML);
function processXML(e:Event):void
{
myXML = new XML(e.target.data);
/// Home Screen Buttons
homeT = myXML.Button.(#Title=="homeT");
homeB1 = myXML.Button.(#Title=="homeB1");
homeB2 = myXML.Button.(#Title=="homeB2");
homeB3 = myXML.Button.(#Title=="homeB3");
homeB4 = myXML.Button.(#Title=="homeB4");
homeB5 = myXML.Button.(#Title=="homeB5");
/// HERE IS WHERE I CALL FUNCTION from MainDoc after xml is loaded.
/////////////////
trace("xml loaded!!!! " + homeB1);
MainDoc.languageLoaded(Language);
}
}
}
}

Send data from Flash to Starling class

I want to send data from mainClass (Flash class) to my Starling class. Here is the code of two classes. I need to pass data between them.
package
{
import flash.display.Sprite;
import flash.events.Event;
public class mainClass extends Sprite
{
private var myStarling:Starling;
public function mainClass ()
{
super();
stage.addEventListener(Event.RESIZE,onResize);
}
private function shangedOr(e:StageOrientationEvent):void
{
// code
}
private function onResize(e:Event):void
{
myStarling = new Starling(Main,stage);
myStarling.start();
}
}
}
Main.as class of starling :
package
{
import starling.core.Starling;
import starling.display.Sprite;
public class Main extends starling.display.Sprite
{
private var theme:AeonDesktopTheme;
public function Main()
{
super();
this.addEventListener(starling.events.Event.ADDED_TO_STAGE,addToStage);
}
private function addToStage(e:starling.events.Event):void
{
this.theme = new AeonDesktopTheme( this.stage );
drawComponent();
}
private function drawComponent():void
{
//code
}
}
}
Is there a way to pass data between Flash and Starling? I created an Event class to listen from Flash, then dispatch from Starling class to get the data I need from the Flash class but it didn't work.
Fast solution , useful in some cases: make static function in mainClass and call it when your Main instance is ready (in addToStage for example)
definately we can't write any event or class to interact with flash and starling for this issue we can use CALL_BACK Function. so that you can interact or send data from core flash to starling and starling to core flash. Function call won't give any error.

Can't dispatch custom event from one class to another in Flash AS3

This is my custom event class:
package{
import flash.events.Event;
public class PetEvent extends Event{
public static const ON_CRASH:String = "onCrash";
public function PetEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false):void{
super(type, bubbles, cancelable);
}
override public function clone():Event {
return new PetEvent(type, bubbles, cancelable);
}
}
}
This is my game handler. I create a new instance of the class Surf from which I want to listen from.
package {
import flash.display.MovieClip;
import flash.events.Event;
public class GameHandler extends MovieClip {
public var newGame:Surf;
public function GameHandler() {
newGame = new Surf();
newGame.addEventListener(PetEvent.ON_CRASH, onCrash);
addChild(newGame);
}
public function onCrash(petEvent:PetEvent):void{
trace("MPAM");
var gameOver:GameOver = new GameOver(stage.stageWidth, stage.stageHeight);
addChild(gameOver);
newGame = null;
}
}
}
And the relevant lines from the Surf class:
public function startSurfing(timerEvent:TimerEvent):void
{
moveCatandDog();
for each ( var boat:Boat in armada)
{
boat.moveBoat(boatSpeed);
if ( cat.hitTestObject(boat) || dog.hitTestObject(boat) )
{
dispatchEvent( new PetEvent(PetEvent.ON_CRASH) );
gameTimer.stop();
}
}
}
So when Surf detects a crash I want it to send the event to GameHandler and GameHandler will create a GameOver instance.
I have tried everything and I don't even get a trace. I normally don't ask questions but this is for a uni project and I'm running out of time. I would really appreciate any feedback. Thanks!
Problem solved!
I had to change my Document Class to GameHandler and make a public static variable of stage.
Previously I had Surf as my Document Class because I had some keyboard listeners set to the stage.
So the PetEvent and the dispatch in Surf were correct. I changed the GameHandler as shown below, with another solution I found in StackOverflow.
Inside the constructor of GameHandler, if the stage is ready (not null) it sets it to the public static variable STAGE (via the init function), otherwise it adds a listener and when the stage is ready it does the same thing and removes the listener.
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;
public class GameHandler extends MovieClip {
public var newGame:Surf;
public static var STAGE:Stage;
public function GameHandler() {
if (stage){
init();
} else {
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}
newGame = new Surf();
newGame.addEventListener(PetEvent.ON_CRASH, onCrash);
addChild(newGame);
}
private function init(e:Event=null):void{
removeEventListener(Event.ADDED_TO_STAGE, init);
// store stage reference when stage ready
STAGE=stage;
}
public function onCrash(petEvent:PetEvent):void{
var gameOver:GameOver = new GameOver(stage.stageWidth, stage.stageHeight);
addChild(gameOver);
newGame = null;
}
}
}
and I imported GameHandler into Surf with:
import GameHandler;
so I can set the listeners in Surf to GameHandler.STAGE.addEventListener (...)
Thanks everyone for the suggestions!

AS3 Cannot access stage from custom class

How can I access the stage and especially the width and mouse position of the flash Movie from a custom class?
package classes
{
import flash.events.*;
import flash.display.*;
public class TableManager extends Sprite
{
public function TableManager() {
sayStage();
}
public function sayStage():void
{
trace(stage);
}
}
}
This will only return nill. I know that DisplayObjects don't have any stage until they have been initiated so you can't access the stage in your constructor but even if I call sayStage() later as an instance method it won't work.
What am I doing wrong?
If TableManager is on the stage you can access the stage with this.stage.
The trick is you have to wait for the instance to be added to the stage. You can listen for the ADDED_TO_STAGE event so you know when that's happened.
package classes {
import flash.events.*;
import flash.display.*;
public class TableManager extends Sprite {
public function TableManager() {
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void {
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
sayStage();
}
public function sayStage():void {
trace(this.stage);
}
}
}
The most defensive way to write this is:
public function TableManager() {
if(this.stage) init();
else this.addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
if(e != null) this.removeEventListener(Event.ADDED_TO_STAGE, init);
sayStage();
}
If the object is already on the stage at the time of initialization, then immediately call the init function with no arguments. If not wait until its been added to the stage. Then when the init function gets called, if it was called as the result of an event, then detach the event handler, and move along.
You can pass a reference of the root movieclip (i.e. the stage) to your custom class.
e.g.
package classes
{
import flash.events.*;
import flash.display.*;
public class TableManager extends Sprite
{
private var _rootMC:MovieClip;
public function TableManager(rootMC:MovieClip) {
_rootMC = rootMC;
sayStage();
}
public function sayStage():void
{
trace(_rootMC.stage);
trace(_rootMC.stage.stageWidth);
}
}
}
Then when instantiating your instance of TableManager from the root timeline:
//the keyword 'this' is the root movieclip.
var newTM:TableManager = new TableManager(this);
stage will be null as long as the Sprite hasn't been added to the display list - it's nothing to do with initiation. E.g.
var t:TableManager = new TableManager;
stage.addChild( t ); // or your root class, or any class that's already on the displaylist
trace( t.stage ); // [Stage stage]
t.parent.removeChild( t );
trace( t.stage ); // null
As #crooksy88 suggests, either pass in the stage to the constructor, or keep it as a static somewhere, say your main document class, so that you can access it everywhere.
i think usefull for You should be create static reference to stage :
in Your main class add line and set stage :
public static var stage:Stage;
...
public function Main():void { // constructor
Main.stage = stage;
...
and than in custom class :
public function sayStage():void
{
trace(Main.stage);
trace(Main.stage.stageWidth);
}
you may access this.stage when the current object(also a sprite) is already attached to the stage.
public class TableManager extends Sprite{
public function TableManager()
{
}
public function sayStage():void
{
trace(stage);
}
}
TableManager tm=new TableManager();
//tm.sayStage(); // no
addChild(tm);
tm.sayStage(); // fine
hope this could help
here is a pretty good solution you only need to reference the stage inside your class you just pass it as a simple object, here how to do that
package {
public class Eventhndl{
private var obj:Object;
public function Eventhndl(objStage:Object):void{
obj = objStage;
trace(obj); // now the obj variable is a reference to the stage and you can work as normal you do with stage (addChild, Events Etc..)
}
}
this is how you make instance to run it, i have used the constructor method but you can change it to any function as you wish and call it whenever you need it.
import Eventhndl;
var EH:Eventhndl = new Eventhndl(stage);
here is some few Examples how to access stage from class
https://stackoverflow.com/a/40691908/1640362
https://stackoverflow.com/a/40691325/1640362

Is it possible to listen the event triggered by another class?

I've got 3 classes:
The class that recognizes the change in URL (using SWFAddress by Asual):
package swfaddress {
import flash.display.MovieClip;
import com.asual.swfaddress.*;
public class SwfAddress extends MovieClip {
private var dispatcher:Dispatch = new Dispatch;
public function SwfAddress():void {
SWFAddress.addEventListener(SWFAddressEvent.CHANGE, onChange);
}
private function onChange(e:SWFAddressEvent):void {
dispatcher.changed();
}
}
}
The class "Dispatch" that validates the URL and dispatching Event when finished
package swfaddress {
import flash.events.Event;
import flash.events.EventDispatcher;
public class Dispatch extends EventDispatcher {
public static const CHANGED:String = "changed";
public function changed ():void {
// some operations validating the URL
dispatchEvent(new Event(Dispatch.CHANGED));
}
}
}
Other class in other package that should receive info when the validation process has finished.
package menu {
import swfaddress.*
public class MenuPanel extends MovieClip {
var swfRead:Dispatch = new Dispatch;
public function MenuPanel():void {
swfRead.addEventListener(Dispatch.CHANGED, onChange);
}
private function onChange(e:Event):void {
trace("Hello World");
}
}
And the "Hello World" never appeared in the output window - so I'm not sure if it's possible that my MenuPanel has a chance to receive an info about completing the validation triggered by some other class?
You are creating two different instances of the dispatcher, and so the instance that is being called to validate your URL is not the same instance that you are listening to in the MenuPanel class.
A simple way around this would be to make the Dispatch class a singleton, so that only one instance of it exists and you can reference it from different points in your application. This works by giving the Dispatch class a static method that returns a self-contained instance of itself. You then call Dispatch.getInstance() whenever you want a reference to the class instead of using the new keyword.
Dispatch:
package swfaddress
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class Dispatch extends EventDispatcher
{
// singleton instance
private static var _instance:Dispatch;
private static var _allowInstance:Boolean;
public static const CHANGED:String = "changed";
// get singleton instance of Dispatch
public static function getInstance():Dispatch
{
if (Dispatch._instance == null) {
Dispatch._allowInstance = true;
Dispatch._instance = new Dispatch();
Dispatch._allowInstance = false;
}
return Dispatch._instance;
}
public function Dispatch()
{
if (!Dispatch._allowInstance)
{
throw new Error("Error: Use Dispatch.getInstance() instead of the new keyword.");
}
}
public function changed():void {
//some operations validating the URL
dispatchEvent(new Event(Dispatch.CHANGED));
}
}
}
Getting a reference:
private var dispatcher:Dispatch = Dispatch.getInstance();