Overriding public method in dynamically loaded class with AS3 and getDefinitionByName() - actionscript-3

I have two SWFs: main.swf and external.swf. main.swf needs to access some methods in external.swf, so it loads external.swf into itself and uses getDefinitionByName("package.Class") to access the class and one of its methods:
var ExternalClass = getDefinitionByName("package.Class") as Class;
var ClassInstance = new ExternalClass();
var NeededFunction:Function = ClassInstance["NeededFunction"] as Function;
var response:String = NeededFunction(param);
Now, I need to extend the functionality of NeededFunction (which is a public method)... I know it's possible to override public methods, but how would I go about this with a dynamically loaded class?
I was thinking I could do something like this, but it doesn't work:
var ClassInstance["NeededFunction"] = function(param1:uint):String {
var newString = "Your number is: "+param1.toString(); //New functionality
return newString;
}

Another way to deal with this could be to have the classes in a package that's accessible by both SWFs. Just add the classes' root folder to your Actionscript path .
Instead of getting a class by using getDefinitionByName , you simply import it. As for overriding , you can create a Class that overrides one of the classes , or you can create an Interface.
import com.yourlocation.ExternalClass;
var external:ExternalClass = new ExternalClass();

Using FlashDevelop this is pretty simple to fix.
Right click your included swc from the Project list.
Choose options then "include library (complete library)".
..you can now use getDefinitionByName to get a unreferenced class from your swc file.

Related

Flash loading first external swf loaded

I am making an application to test art from a game I volunteered for. Right now the example I am posting will only touch the armors but the loading process is the same throughout the program. I have a movieclip ready to hold the loaded file but it adds it to the container via the class. It works how it should however my issue is that if you use another file with the same classes then it will default to the first file loaded. Even i use loaderr.unloadAndStop() and remove everything from the stage, it will always load the first file that corresponds to the class I am loading by. Since the armor pieces are loaded by class it makes it a hassle to test multiple changes to an armor file without changing the classes on each export. Here is an example of the code that is being used and I am curious if there is any way that I can improve this. `
public class Test extends MovieClip
{
public var mcChar:Display;
public var btnTest:SimpleButton;
public var btnTest2:SimpleButton;
public var ldr:Loader = new Loader();
public var strSkinLinkage:String;
public var strGender:String;
public function Test()
{
btnTest.addEventListener(MouseEvent.CLICK, TestP);
btnTest2.addEventListener(MouseEvent.CLICK, TestP2);
}
public function TestP(e:MouseEvent)
{
mcChar = new Display();
stage.addChild(mcChar);
mcChar.x = 789.6;
mcChar.y = 604.75;
mcChar.width = 667.15;
mcChar.height = 478.55;
strSkinLinkage = "CNC";
strGender = "M"
this.ldr.load(new URLRequest("CNC.SWF"), new LoaderContext(false, ApplicationDomain.currentDomain));
this.ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
public function TestP2(e:MouseEvent)
{
mcChar = new Display();
stage.addChild(mcChar);
mcChar.x = 789.6;
mcChar.y = 604.75;
mcChar.width = 667.15;
mcChar.height = 478.55;
strSkinLinkage = "CNC";
strGender = "M"
this.ldr.load(new URLRequest("CNC2.SWF"), new LoaderContext(false, ApplicationDomain.currentDomain));
this.ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
public function onLoadSkinComplete(e:Event):*
{
var AssetClass:Class;
try
{
AssetClass = (getDefinitionByName(((strSkinLinkage + strGender) + "Head")) as Class);
mcChar.head.addChildAt(new (AssetClass)(), 0);
}
catch(err:Error)
{
AssetClass = (getDefinitionByName(("mcHead" + strGender)) as Class);
mcChar.head.addChildAt(new (AssetClass)(), 0);
};
AssetClass = (getDefinitionByName(((strSkinLinkage + strGender) + "Chest")) as Class);
chest.addChild(ldr.content (AssetClass)());
mcChar.chest.addChild(new (chest)());
this.ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
}
`
I don't think its well formatted on this site but this is the core code. I have separate removal functions and my imports are all there. Like I said I cant seem to get it to format correctly. This is my test scenario and isn't my full dynamic tester where I can choose the file. Any help in figuring out how to use the most recent file is appreciated. Also for some background I am more of a self taught novice in as3.
When it gets to loading and unloading assets in AS3, there are several things to learn.
ApplicationDomain is a container for class definitions. The getDefinitionByName(...) method is basically the same as calling the ApplicationDomain.getDefinition(...) on the current ApplicationDomain (or maybe on the main ApplicationDomain, I never tried to do it in the loaded content). As the side result, you cannot have two classes with the same names inside the same ApplicationDomain (or rather you can, but one of them is inaccessible, who knows).
When you load another SWF which falls into the "same domain" category (same www domain, or same/nested local folder), AS3 automatically mixes all the definitions from the loaded SWF into the main ApplicationDomain. If you are willing to have some advanced control over loading/unloading stuff, or/and there are "skin" libraries that have similar sets of classes, you need to put the loaded files into separate ApplicationDomains or their definitions will collide and the result will be unpredictable (yet obviously not satisfactory).
The Loader.load(...) method has a second argument that allows you to do so:
// If there are no mandatory constructor arguments,
// you are free to omit the () brackets. I like doing so.
var aLoader:Loader = new Loader;
var aRequest:URLRequest = new URLRequest("mylibrary.swf");
// Documentation states that passing no argument here is
// the same as passing ApplicationDomain.currentDomain.
var childDomain:ApplicationDomain = new ApplicationDomain;
var aContext:LoaderContext = new LoaderContext(false, childDomain);
aLoader.load(aRequest, aContext);
Thus, when external SWF library is loaded, you can obtain its classes/definitions as following:
var aClass:Class;
// If you get things from the loaded SWF's Library
// then it is Sprite or MovieClip for the most cases.
var anAsset:Sprite;
aClass = aLoader.contentLoaderInfo.applicationDomain.getDefinition("MyAssetClass") as Class;
anAsset = new aClass;
When you do not longer need some of the loaded libraries, you call the Loader.unloadAndStop(...) method on the relevant Loader instance. Combined with the loading SWF into separate ApplicationDomain you can be sure that all of the loaded content (graphics, classes, sounds) is unloaded, destroyed and removed (that one I actually checked):
// Passing "true" also forces the Garbage Collector
// to actually do its job for a change.
aLoader.unloadAndStop(true);

Can I still create Global variables in AS3

Following the answer here, I have created a file called MyGlobals.as and placed some global variables and functions so that I can access it from anywhere within my project just like AS3 buil-in functions such as trace() method.
This is MyGlobals.as which is located in the src folder (top level folder)
package {
public var MessageQueue:Array = new Array();
public var main:Main;
public var BOOKING_STATUS_DATA:Object;
public function postMessage(msg:Object):void {
MessageQueue.push(msg);
}
public function processMessage():void {
var msg:Object = MessageQueue.pop();
if (msg) {
switch (msg.type) {
}
}
}
Looks like my IDE (FD4) is also recognizing all these functions and variables and also highlighting the varibles and functions just like any other built-in global functions. However, I am getting compilation errors "Accessing possibly undefined variable xxx". The code is as simple as trace(MessageQueue) inside my Main (or another classe).
I am wondering if there was any change Adboe has done recently that it can't be done now or am I missing something? I am not sure if I need to give any special instructions to FD to include this MyGlobals.as?
I am using FD4, Flex SKD 3.1, FP12.0
I am aware of the best practices which suggests to avoid using this type of method for creating global variables but I really need it for my project for my comfort which I feel best way (right now) when compared to take any other path which involves daunting task of code refactoring. I just want do something which can be done in AS3 which I guess is not a hack.
I've done some playing around; it looks like you can only define one (1) property or method at package level per .as file. It must be the same name (case-sensitive) as the .as file it is contained in.
So no, nothing has changed since the older Flash Versions.
In your case that would mean you need five separate ActionScript files along the lines of:
MessageQueue.as:
package
{
public var MessageQueue:Array;
}
main.as:
package
{
public var main:Main;
}
...etc. As you can see this is very cumbersome, another downside to the many others when using this approach. I suggest using the singleton pattern in this scenario instead.
package{
public class Singleton{
private static var _instance:Singleton=null;
private var _score:Number=0;
public function Singleton(e:SingletonEnforcer){
trace(‘new instance of singleton created’);
}
public static function getInstance():Singleton{
if(_instance==null){
_instance=new Singleton(new SingletonEnforcer());
}
return _instance;
}
public function get score():Number{
return _score;
}
public function set score(newScore:Number):void{
_score=newScore;
}
}
}
then iin your any as3 class if you import the singleton class
import Singleton
thn where u need to update the global var_score
use for example
var s:Singleton=Singleton.getInstance();
s.score=50;
trace(s.score);
same thing to display the 50 from another class
var wawa:Singleton=Singleton.getInstance();
trace(wawa.score)

As3 instances from imported swf not recognized

I have made something like a level in flash editor. made some classes inside the editor that inherited from the classes in my project. next I export my swf to later be loaded by my main code.
the thing is that once I have the swf loaded I try doing some traces to check if the instances are the correct class.
trace(map.getChildAt(i)+" D "+(map.getChildAt(i) as PointerImage));
witch outputs this: [object PointerBall] D null.
PointerBall(from flash) inherites from PointerImage(from my main code).
now if I trace this
trace((new PointerBall())+" Y "+(new PointerBall() is PointerImage));
witch outputs this: [object PointerBall] Y true
so the problem is only with instances imported from the swf.
This worked for me when I tried it. Assuming your referenced .swf is in the same application domain as the parent. The key is to have the classes that you're referencing be in a specific package. Classes that aren't in packages are in their own namespace, in both the loaded .swf and the parent. So PointerImage in your loaded .swf is not the same PointerImage that exists in your parent. It all has to do with namespace.
Just move these classes into a package folder named game and rename the package and you should be set.
package game{
import game.PointerImage
public class PointerBall extends PointerImage{
//Class code
}
}
This is what I'm working with:
function onLoad(evt:Event):void{
var c:MovieClip = evt.target.content as MovieClip;
addChild(c);
var t:BigTest = c.getChildAt(0) as BigTest;
trace(t); //[object LitteTest]
}
You have to use a LoaderContext and set the domain you want to use for either overriding the current class definitions or overriding the loaded swf ones:
var context:LoaderContext = new LoaderContext();
context.securityDomain = SecurityDomain.currentDomain;
context.applicationDomain = ApplicationDomain.currentDomain;
var ldr:Loader = new Loader();
ldr.load(urlRequest, context);
Working with class inheritance is ok but easier is working with Interface.

AS3 dynamic variable creation in DC

Can variables be created dynamically without declaration when we write as Document class in AS3?
For example, from a library I'm importing sound files. Some 20 sound files.
If the code is in fla itself, we can assign in for loop like:
this["SOUND"+increasingNumber]
But in documentClass this is not working , since this refers the class here not the stage.
Any method to create variables?
When imported into your library, right click the sound file and go to its properties. Click the actionscript tab and check 'export for actionscript'. Give it a class name which you can then use in your document class to instantiate that sound.
If you named it Sound1:
var sound:Sound = new Sound1();
sound.play();
more detailed info here
[Edit to loxxy's reply] above shows how to create the variables in the document class.
To dynamically create all the sound variables, I'd recommend using an array, like so:
Suppose you named all your sounds in your library Sound1 to Sound20
import flash.utils.getDefinitionByName;
var sounds:Array = [];
var soundClass:Class;
for(var i:int = 1; i<21; i++){
soundClass = getDefinitionByName("Sound" + i) as Class;
sounds.push(new soundClass());
}
In fla when you add code, you add it into a framescript.
A framescript is a block of code repeated at a regular interval (framerate).
You can achieve that using addFrameScript like this.
However a better approach would be to not mix up framescript & the regular class methods.
You can access the 'stage' from the code but only after the added_to_stage event to be sure.
addEventListener(Event.ADDED_TO_STAGE, init);
function init(e:Event):void{
// Access 'stage' here
}

Instantiate an Fxg on runtime using getDefinitionByName in Flex

i have a little problem with getDefinitionByName.
My purpose is to instantiate an FXG object(Number10.fxg) in a document mxml on runtime.
The name of the Class is in a string variable that is used by getDefinitionByName
to return the name of the class to insantiate. The code doesn't work even if doesn't send an error message. The code is as follows:
import assets.Number10;
import flash.utils.getDefinitionByName;
import mx.core.IVisualElement;
private function onClick(event:MouseEvent):void
{
var value:String = "Number10";
var ClassDefinition:Class = getDefinitionByName(value) as Class;
var ten:IVisualElement = new ClassDefinition() as IVisualElement;
this.contentGroup.addElement(ten);
}
I tried also with... var ten:IVisualElement = new ClassDefinition();
but nothing. It Doesn't work!
Please, Help me!
First of all, i refer to the adobe documentation pages that covering the topic so telegraphic. Here it is:
Option includes class [...]
Description Links one or more classes to the resulting application SWF file, whether or not those classes are required at compile time.
To link an entire SWC file rather than individual classes, use the include-libraries option.
Ok.In Flash Builder i go to the Additional compiler arguments where there is just this option
-locale en_US
So i add my option under this
-includes class = assets.Number10
or
-includes class assets.Number10
or
-includes class Number10
When the application runs i get the Error #2032.
I think that the option declaretion is wrong. I do not have a good reference for using option.
So...Help me!
How can i declare the Number10 class or the assets package with the other fxg object using the includes class option?
Ok! I find the solution...
Is to put a reference to Number10 class somewhere in the code, for instance:
import assets.Number10;
import flash.utils.getDefinitionByName;
import spark.core.SpriteVisualElement;
//case1
var myNumber:Number10;
//or
//case2
Number10;
private function onClick(event:MouseEvent):void
{
var value:String = "assets.Number10";
var ClassDefinition:Class = getDefinitionByName(value) as Class;
var ten:SpriteVisualElement = new ClassDefinition() as SpriteVisualElement;
this.contentGroup.addElement(ten);
}
and the code works :-)
This is a problem that comes from the way that Flex compiles its code. Flex compiles its code so that if a class is not used, it will keep this class off the final compiled program.
But the problems are not over yet! If i have hundreds of Fxg objects that could be instantiate, declaring all classes is little difficult and tedious.
So, how i can delclare in one time all classes of a package?
You can add classes to SWCs and SWFs using the include and includeClasses compiler options. Using these, you don't have to reference the classes in the code. Consult the documentation for proper usage.
Be sure to use the fully qualifed class name.
Also, the approach of casting your FXG class as an IVisualElement is new to me. I thought you had to use real classes in casting and the sort. Try using a SpriteVisualElement.
private function onClick(event:MouseEvent):void
{
var value:String = "assets.Number10";
var ClassDefinition:Class = getDefinitionByName(value) as Class;
var ten:IVisualElement = new ClassDefinition() as SpriteVisualElement.;
this.contentGroup.addElement(ten);
}