Pass Boolean through classes - actionscript-3

I am fairly new to AS3 and I want to know how to set a boolean to true or false in one package class and have it do something in another package class.
This is where I first set my boolean "mute":
package
{
public class AvoiderGame extends MovieClip
{
public var mute:Boolean;
public function Game()
{
mute = false;
And I want to change it on this package and have it effect the first package where I originally put it:
package
{
public class DocumentClass extends MovieClip
{
public function onRequestMute( navigationEvent:NavigationEvent ):void
{
mute = true;
}
I know it should be fairly easy but I'm not that great in certain parts of as3.

You can make it a static variable. you need to refer to it as "AvoiderGame.mute" everywhere not inside of the owning class.
//declaration in AvoiderGame
public static var mute:Boolean;
//use in some other file
AvoiderGame.mute = (true | false);
This is considered a bad practice overall though. I suggest looking up how to make a simple sound manager class to take care of this type of global variable.
Try searching for: "sound manager as3", "singleton pattern as3"

Related

Animation in class files?

I've found myself needing to refer to certain animations in some unknown frame in my classes. What would be the best way to do this? Should I be creating a custum name and specify that all instances of the class should label a certain animation the same thing. For instance:
public class CanBeHurt{
public CanBeHurt() extends MovieClip{
// constructor code here
}
public function hurt():void{
gotoAndPlay("hurt");
}
}
Any instance of this class would then be forced to label the start of the hurt animation "hurt". Alternatively I could take in strings in the constructor that specify the frame, like so:
public class CanBeHurt{
private var hurtAnimationLabel: String;
public CanBeHurt(hurtAnimationLabel: String) extends MovieClip{
this.hurtAnimationLabel = hurtAnimationLabel;
}
public function hurt():void{
gotoAndPlay(hurtAnimationLabel);
}
}
But unfortunately with more complex objects, I already have a bunch of other input arguments for the constructor and with so many animations, I really don't want to add anymore to it. And lastly, the last option I came up with is:
public class CanBeHurt{
private var hurtAnimationLabel: String;
public CanBeHurt() extends MovieClip{
this.hurtAnimationLabel = this.currentLabels[0];
}
public function hurt():void{
gotoAndPlay(hurtAnimationLabel);
}
}
Unfortunately, with multiple animations, now I'm forced to have my animation labels in a certain order to be able to refer to them properly. Of the 3 methods, I've found the first one to be the most satisfying, but is there a better way?
You don't need to store label names, only if you want the efficient way.
Example:
public class CanBeHurt{
private var label: String;
public CanBeHurt() extends MovieClip{
label= "hurtLabel";
}
public function hurt():void{
currentFrameLabel != "hurtLabel" ? gotoAndPlay("hurtLabel") : null;
}
}
You should check the current frame's label and then play the animation to avoid frame stucking. (The animation is always at the first frame)
For games, most of the developers use a function to control the animations, it's the handleAnimation function.
Example:
public class Example{
private var label:String = "someLabel1";
public Example() extends MovieClip{
//constructor code here
}
private function handleAnimation(){
if(condition){
label = "someLabel1";
}
if(condition2){
label = "someLabel2";
}
gotoAndPlay(label);
}
}
I usually don't extend movieclip for my code. I create some kind of class that accepts a MovieClip in constructor and stores a reference to this and then acts upon it on function calls.
When it comes to label names I usually place them as a static variable in the appropriate class.
This has worked well for me throughout the years :)

Get instance objects which are not on first frame

I'm using actionscript 3.0 and Flash Professional CS5.5
I ran into a problem with instance objects which are not on the first frame.
My first frame is a menu and my second frame contains a TLF text field.
I have given the text field an instance name, let's say "username_txt".
Now if I turn off the "Automatically Declare Stage Instances" and do this in my main class
public class MainClass extends MovieClip {
public var username_txt:TLFTextField;
public function MainClass() {
username_txt.text = "anything";
}
}
I will receive a run time error stating that I try to access the property of a null object. (I have import all the necessary classes so that is not the problem)
This does not happen when I put the text right in the first frame though.
Any help?
Thanks in advance
As the text field has not yet been instantiated since the frame has not yet been reached, I'm unsure there is an elegant way to perform this task.
Perhaps a better model would be to decouple your data model from your views.
Create a singleton class to store data:
package
{
public class ApplicationModel
{
/** Singleton instance. */
private static var instance:ApplicationModel = new ApplicationModel();
/** Return singleton instance. */
public static function getInstance():ApplicationModel
{
return instance;
}
/** Data Model */
public var username:String;
/** Constructor as singleton enforcer */
public function ApplicationModel()
{
if (instance)
throw new Error("ApplicationModel is a singleton and can only be accessed through ApplicationModel.getInstance()");
}
}
}
Then from within anywhere such as keyframes or classes, you can get the instance of the object, implemented either as:
ApplicationModel.getInstance().username = "test";
Or a better practice would be:
var applicationModel:ApplicationModel = ApplicationModel.getInstance();
applicationModel.username = "test";
As per your example:
public class MainClass extends MovieClip {
public var username_txt:TLFTextField;
public function MainClass() {
ApplicationModel.getInstance().username = "anything";
}
}
Then, on the frame you need to update your TLFTextField (frame script of frame 2), you set the text based upon the model:
username_txt.text = ApplicationModel.getInstance().username
Your view will always update when needed.

How to access stage in a class that is not a DisplayObject?

How do I access the stage in Actionscript 3 in a class which is not my main class and not a displayobject?
The easy way, you can keep it in a static var for example:
public class MyMain extends Sprite {
public static var STAGE:Stage;
public function MyMain() {
if (stage)
init();
else
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}
}
private function init(e:Event=null):void{
removeEventListener(Event.ADDED_TO_STAGE, init);
// store stage reference when stage ready
STAGE=stage;
}
}
and in your other class import the class that is holding the static var, of course the var have to be initialized before accessing it.
import MyMain;
public class Other {
public function useStage():void {
MyMain.STAGE...
}
}
the easiest way is to use a global object
http://github.com/inruntime/AS3-Global-Object
this page has examples of how to set and retrieve objects from any class.
Adobe failed to provide static access to the stage, leaving you no option but to implement it yourself.
This is an epic fail, since it's impossible to access the stage before your main document class instance constructor runs to stash the stage instance in some arbitrary static variable.
Since you'll have to initialize that arbitrary static variable every time you want static access to the stage, it's best to ensure you only have to do it once.
To ensure you'll only have to initialize it once, you'll have to make sure that EVERY STATIC METHOD you ever write points to that variable and doesn't try to access it before it's initialized.
Given all that... the most logical steps are:
1. File a Feature Request with Adobe NOW.
2. Create a "Global" or "Document" base class that initializes a static stage variable for you, and have all your document classes inherit from it. Extending MovieClip gives you the most flexibility:
package
{
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
public class Document extends MovieClip
{
public static var _stage:Stage = null;
public static function get sstage():Stage //added an extra s for "static" to differentiate the static property name from the instance property name "stage"; call it what you want
{
return _stage;
}
public function Document()
{
super();
if (stage != null)
initStage( null ); //explicitly pass null to indicate no listener was attached
else
addEventListener( Event.ADDED_TO_STAGE, initStage, false, 0, true ); //prefer weak references
}
private function initStage( e:Event ):void
{
_stage = stage;
if (e != null) //event listener will be non-null iff listener was added
removeEventListener( Event.ADDED_TO_STAGE, initStage, false );
}
}
}
You will not have to write this class more than once, as long as all your document classes extend the above defined "Document" class (and calls "super" in its constructor right away). By doing that, your document's constructor code and the rest of your project from that point forward will have static access to the stage via "Document.sstage". There is no way for a static context to have access to the stage before this initialization occurs in the main document class.
I suggest you adopt this kind of consistency very early on, because it will make it easier to fix if Adobe ever adds static access to Stage. It will simply be a matter of replacing "Document.sstage" with whatever Adobe provides.
You could use accessor and mutator class to set and retrieve the stage instance?

custom AS3 Loader class that uses a custom LoaderInfo class?

This is a question specifically about creating a custom Loader class as a followup to How do I modify existing AS3 events so that I can pass data?. If this wasn't the right way to go about this, let me know.
I have a custom Loader class and a custom LoaderInfo class. What I can't figure out is how to have the Loader class use the custom LoaderInfo class as it's contentLoaderInfo property.
package com.display {
import flash.display.Loader;
import com.display.CustomLoaderInfo;
public class CustomLoader extends Loader {
public var customData:*;
public function CustomLoader(passedData: *= null) {
customData = passedData;
}
}
}
^ Have to do something in there to make it have the new CustomLoaderInfo
package com.display {
import flash.display.LoaderInfo;
import flash.events.Event;
import com.events.CustomEvent;
public class CustomLoaderInfo extends LoaderInfo {
public var customData:*;
public function CustomLoaderInfo(passedData: *= null) {
customData = passedData;
}
override public function dispatchEvent(event:Event):Boolean {
var customEvent:CustomEvent = new CustomEvent(event.type, customData, event.bubbles, event.cancelable);
return super.dispatchEvent(customEvent);
}
}
}
^ That might work, but since I can't get CustomLoader to use it, I don't know yet.
I don't know how it fits into what you're doing, but you could not bother making an extended LoaderInfo at all. Loader.contentLoaderInfo has a property called loader that will return the Loader (or in this case CustomLoader) that was used. In theory you can just do this with the CustomLoader class that you have:
var loader: CustomLoader = new CustomLoader("Extra Data");
loader.load(new URLRequest("file.swf"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
function loaderComplete(event: Event) : void
{
var customData:* = (event.target.loader as CustomLoader).customData;
trace(customData); // Extra Data
}
You would have to override the function where the loaderinfo instance is created, and that might be in a private function. In the new function you could then instanciate your custom loaderInfo class instead, but you would also have to do anything else that the overwritten method does. You might run into some security sandbox problems with loadinfo as well. Overwriting a function like this is usually only posible when the function written with this in mind.
Alternatively you could use the prototype chain (read here) to change the loaderInfo class at runtime. But I wouldn't recommend it. It's not good practice in my opinion.
If you get this to work you still have the problem that your custom event will not work with any event that has properties not inheritet from the Event class (like the ProgressEvent that has progress related properties).
I would say you properly should look for another solution.
In the question you link to you talk about sending movieclip with your event. Have you thought about reaching the other way? When you recieve the event, you get a reference to the object that dispatched it (Event.target). Can you not use that reference to get the movieclip?
Perhaps you could explain your problem in more detail (maybe in a new question so you still keep this open), I am sure there is a better / easier way to solve it.
I needed something like this because I wanted to carry an index with the info ... ie - have something like event.target.index available after an event is triggered .. anyway i designed a class that contained the loader and the loader info... heres the class
public class LoaderBox extends Sprite {
public static var PROGRESS:String = "progress"
public static var COMPLETE:String = "complete"
public var id:int
public var index:int
public var loader:Loader
public var info:LoaderInfo
public var isOpen:Boolean
//
public var percent:Number = -1
public function load(path:String,idx:int=-1,nm:String=null){
loader = new Loader()
if (nm != null){
name = nm
}
index = idx
var req:URLRequest = new URLRequest(path)
info = loader.contentLoaderInfo
info.addEventListener(Event.COMPLETE,complete)
info.addEventListener(ProgressEvent.PROGRESS,progress)
isOpen = true
loader.load(req)
}
private function complete(e:Event):void{
isOpen = false
dispatchEvent(new Event(LoaderBox.COMPLETE))
}
private function progress(e:ProgressEvent):void{
if (e.target.bytesTotal>0){
percent = Math.floor(e.target.bytesLoaded/e.target.bytesTotal * 100)
}
dispatchEvent(new Event(LoaderBox.PROGRESS))
}
}
A potential issue with the flagged answer: if contentLoaderInfo spits out a IOError, you can't access the .loader property to access your custom loader class.
What I did is the following:
* in my custom loader class, create the following method:
public function requestError(event:Event):void {
dispatchEvent(event);
}
when adding a listener to contentLoaderInfo for the IOerror, point to the custom loader class's method:
_loaderCls.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, _loaderCls.requestError);
Then, add the same listener to your loader class and make it call any method you need. At that point, the event.target would be that of your custom loader class and you can add any extra info you need to that class:
_loaderCls.addEventListener(IOErrorEvent.IO_ERROR, requestError);
or you can do a better thing to pass data into the Loader class:
package com.display
{
import flash.display.Loader;
public class Loader extends flash.display.Loader
{
private var _obj:Object;
public function Loader():void
{
super();
}
public function get obj():Object
{
return _obj;
}
public function set obj(o:Object):void
{
_obj = o;
}
}
}

AS3 - Can I detect change of value of a variable using addEventListener?

Is it possible to use EventListener to Listen to a variable and detect when the value of that variable changes? Thanks.
This is quite easy to do if you wrap it all into a class. We will be using getter/setter methods. The setter method will dispatch and event whenever it is called.
(Note: Setters and Getters are treated like properties). You merely assign a value, as opposed to calling a method (e.g someVar = 5 instead of someVar(5); Even though setters / getters are functions/methods, they are treated like properties.
//The document class
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
public Class TestDocClass extends Sprite
{
private var _model:Model;
public function TestDocClass():void
{
_model = new Model();
_model.addEventListener(Model.VALUE_CHANGED, onModelChanged);
}
private function onModelChanged(e:Event):void
{
trace('The value changed');
}
}
}
//The model that holds the data (variables, etc) and dispatches events. Save in same folder as DOC Class;
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class Model extends EventDispatcher
{
public static const VALUE_CHANGED:String = 'value_changed';
private var _someVar:someVarType;
public function Model():void
{
trace('The model was instantiated.');
}
public function set someVariable(newVal:someVarType):void
{
_someVar = newVal;
this.dispatchEvent(new Event(Model.VALUE_CHANGED));
}
}
}
#BrianHodge: How do you actually use your example? How do you call the set function? How do you refer to it? Where do pass the variable to be changed..?
Let's say if I want to change the wrapped variable with a button click, for example.
I have to confess that I tried some other codes and example (getter/setter) type, with dispatchEvent or without,and I can't get over it! But your example seems to be exactly what I need, just can't make it work.
I get the The model was instantiated when I set the function as document class. That's all.
I found out, at last, for people like me who are loosing time with this dispatch thing!
In my case the _someVar variable has to be data typed as a String (same thing for fornewVal).
OnceTestDocClass is set as your document class; you refer to the Model instantiated like this:
_model.someVariable="new stuff";
I was trying to change the value like this:
_model.someVariable("new stuff");
You can add some trace actions in the Model class to have a clear demo in the output panel:
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class Model extends EventDispatcher
{
public static const VALUE_CHANGED:String = 'value_changed';
private var _someVar:String = "default";
public function Model():void
{
trace('The model was instantiated.');
}
public function set someVariable(newVal:String):void
{
trace ("before " + _someVar);
_someVar = newVal;
trace ("after " + _someVar);
this.dispatchEvent(new Event(Model.VALUE_CHANGED));
}
}
}
It's not much, but these things can cost some people a whole lot of time =)
You used to be able to do something similar in AS2 using Object.watch. I don't see a direct equivalent, but it looks like mx.binding.utils.ChangeWatcher will give you similar functionality for any variables that are bindable.
I don't know of a way to do it in AS3 for non-bindable variables, but if the variable you want to watch is bindable (or if you can modify it to be binadable) then ChangeWatcher might give you what you want.