How can i use object from a lazy loaded swf file if the class definition needs to be changed? - actionscript-3

I am converting all embed statements in my site with lazy loading. The code which was previously like this:
[Embed(source="/newswf.swf", symbol="kungfu")]
public static var Kungfu:Class;
has now been converted to this form:
private var _loader:Loader = new Loader();
public static var abcd:Class = null;
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,onProgressHandler);
_loader.load(new URLRequest("newswf.swf"));
private function onLoadComplete(evt:*):void
{
abcd = evt.target.applicationDomain.getDefinition("kungfu") as Class;
dispatchEvent(new MyEvent(MyEvent.LOADING_DONE));
}
The functions which make use of abcd will be called on recieving MyEvent.LOADING_DONE event.
Now, my problem is, when a class makes use of symbol and has a class definition, I am not able to implement it using the above method because the constructor will be called immediately and won't listen to the onLoadComplete event listener.
[Embed(source="/newswf.swf", symbol="judo")]
public class Judo extends MovieClip
{
public function Judo()
{
super(...);
}
}
When i put the code in the constructor in a separate function and calling it in onLoadComplete method, I get an error because super method had initially been used in the constructor and it cannot be used outside of a constructor.
Can someone tell me a way to do lazy loading in this case?
Thanks in advance :)

I'm not sure if it is possible to extend the class definition after loading because I've never tried, but have you tried simply casting the loaded object and then not calling super() again? That is, inside the loader function type:
obj:Judo = Judo(LoaderInfo(e.target).content)
This article may be helpful: http://www.parorrey.com/blog/flash-development/as3-loading-external-swf-into-movieclip-using-loader-class-in-flash-actionscript3/
That said, I probably wouldn't structure the code in this way and just avoid the situation you're describing with a different structure. Like, one approach would be instead of making the loaded object into a Judo object I would initialize a separate Judo object and then pass it the loaded object. The old "has-a" vs. "is-a" distinction.
Another approach that accomplishes the same thing would be for the containing class to not do the loading and simply create a new Judo object, passing the filename into the constructor. Then the Judo object does the loading.

Related

Call to a possibly undefined method '' through a reference with static type Class

I made small .fla file in Flash Professional, and I have added .as (ActionScript File) in Flash Professional, and I have added something like code below to .as (ActionScript file), but the error appears and I am trying to figure it out, but can't, so I decided to post it in here instead.
package
{
import flash.display.MovieClip;
public class Bag extends MovieClip
{
static var firstBag:String;
public static function set setFirstBag(value:String):void
{
firstBag = value;
}
public static function get getFirstBag():String
{
return firstBag;
}
}
}
and I called it like this:
button1.addEventListener(MouseEvent.CLICK, onClickFirstButton);
function onClickFirstButton(e:MouseEvent):void
{
Bag.setFirstBag("First slot in the bag has been filled up!");
}
But I have received this following error:
Call to a possibly undefined method setFirstBag through a reference
with static type Class.
What could I do wrong?
The .as file and .fla file are on the same folder.
if I changed the Bag class to static. The error will be like this:
The static attribute may be used only on definitions inside a class.
Your answer much appreciated!
Thank you!
You're useing get like it is a mettod, but thay are accessors for properties so intead of:
Bag.setFirstBag("First slot in the bag has been filled up!");
use
Bag.setFirstBag ="First slot in the bag has been filled up!";
A few additional thoughts...
While syntactically valid, the definition and naming of your getter and setter is confusing and atypical, which I think contributed to your confusion about the behavior. You've actually defined two separate properties, one is write-only ("setFirstBag") and one is read-only ("getFirstBag"). Usually you define a getter/setter as the same property (ex "firstBag"), and without any "get" or "set" in the property name, since that is what the getter/setter is defining for you. Example:
private static var _firstBag:String;
public static function get firstBag():String {
return _firstBag:
}
public static function set firstBag(value:String):void {
_firstBag = value;
}
// usage
Bag.firstBag = "stuff";
trace(Bag.firstBag); // "stuff"
Also, you may very well have a good reason to use a getter/setter here, or you might just prefer it, but from the code you posted you could just define a public static var to do the same thing. (If you did, refactoring into a getter/setter if you needed some side-effect logic would be trivial, since the public API remains the same.)

When using the 'Class' datatype, how can I specify the type so I only accept subclass of a specific class?

I've got a method that accepts a parameter of type Class, and I want to only accept classes that extend SuperClass. Right now, all I can figure out to do is this, which does a run-time check on an instance:
public function careless(SomeClass:Class):void {
var instance:SomeClass = new SomeClass();
if (instance as SuperClass) {
// great, i guess
} else {
// damn, wish i'd have known this at compile time
}
}
Is there any way to do something like this, so I can be assured that a Class instance extends some super class?
public function careful(SomeClass:[Class extends SuperClass]):void {
var instance:SuperClass = new SomeClass();
// all is good
}
If you are going to instantiate it anyway, why not accept an object instead which allows you to type it to :SuperClass?
careless(SomeClass);
//vs.
careless(new SomeClass);
Not too much of a problem there as far as your code goes.
There are a few differences though:
The object has to be created, because an object is required. If your function does not instantiate the class under some circumstances, this can be a problem. Additional logic to pass either an object or null can bloat the function call.
If you cannot call the constructor outside that function, it won't
work either.
All that is solved by the factory pattern. Pass a factory as the parameter that produces SuperClass objects.
function careful(factory:SuperClassFactory)
Your requirements:
I want to only accept classes that extend SuperClass
and
I need to pass in a Class so that it can be instantiated many times
by other objects later
Can be met by passing in an instance of the class you need, and using the Object.constructor() method.
public function careful(someInstance:SuperClass):void {
//you probably want to store classRef in a member variable
var classRef: Class = someInstance.constructor();
//the following is guaranteed to cast correctly,
//since someInstance will always be a descendant of SuperClass
var myInst:SuperClass = new classRef() as SuperClass;
}
More reading here.
You can't do that in ActionScript 3. In languages like C# you can do something like (forgive me if the syntax is off):
public void Careless<T>() where T : SuperClass
But AS3 does not have 'generics'. Unfortunately the only way I know how to do what you want is the way you have already done.
A pattern that might be more suitable for your use case might be something like:
class SuperClass
{
public static function careless():void
{
var instance:SuperClass = new SuperClass();
// ...
}
}
The only way to have static type checking in ActionScript 3 is to provide an instance of a class.
It is possible but it's expensive. You can use on a Class (not instance) the:
flash.utils.describeType
You then get an XML with a bunch of information including inheritance for that class. Like I said it's an expensive process and probably creating an instance and checking it will be in most cases faster.

How to assign a custom object to bytesArray? As3

I have a TActor class and a function to_bytes() inside it that should compress it to a bytes array as in this example: http://jacksondunstan.com/articles/1642
public function to_bytes():ByteArray
{
registerClassAlias("TActor",TActor);
var bytes:ByteArray=new ByteArray();
bytes.writeObject(this as TActor);
bytes.position=0;
trace(bytes.readObject());
bytes.position=0;
trace(bytes.readObject() as TActor);
return bytes;
}
However, the first trace prints undefined and the second one null instead of [object TActor].
What do I do wrong?
It's important to note that the this keyword returns the current instance of the object. What you are currently doing is attempting to pass the this instance to writeObject, which will only work if there is an instance of TActor instantiated. So it would work in this scenario:
In some class where you instantiate TActor:
var tactor:TActor = new TActor();
tactor.to_bytes();
Then it should serialize correctly.
Also as we discovered in the comments, TActor is of type MovieClip, currently you cannot use writeObject() on Objects of type MovieClip. More specifically any object that is a dynamic class cannot be used in writeObject. Changing it to Sprite solved this particular case.

Flash AS3 Dyanmic Text keeps giving an error 1119

So I have a method that takes in a String and then is suppose to set the dynamic textbox on a button to said String.
public function setText(caption:String) {
this.btext.text = caption;
}
I really don't understand why this method is producing a 1119 error.
Access of a possibly undefined property btext through a reference with static type Button.as
The instance name of the Dynamic Textbox is btext and I have tried deleting the textbox and making a new one however this still produces a 1119 error. I also read on another stack question that trying this['btext'].text = caption; which gave me plenty of runtime errors.
Basically what am I doing wrong?
Thank you for any help.
EDIT
Here is the code I am using, and I create an instance of button add it to the stage and store it in an array with this code.
Code to create button
this.buttonArray.push(this.addChild(weaponButton));
Button.as
package {
import flash.display.MovieClip;
import flash.filters.*;
public class Button extends MovieClip {
public function Button() {
}
public function setPosition(xpos:int, ypos:int) {
this.x = xpos;
this.y = ypos;
}
public function setScale(xScale:Number, yScale:Number) {
this.scaleX = xScale;
this.scaleY = yScale;
}
public function addDropShadow():Array {
var dropShadow:DropShadowFilter = new DropShadowFilter(2,45,0, 1,4,4,1,1,true);
return [dropShadow];
}
public function removeDropShadow():Array {
return null;
}
public function setText(caption:String) {
this.btext.text = caption;
}
}
}
As you have stated btext is an instance name of an object. Here is where I assume btext is an object you created in your library.
In your class you are doing 2 things wrong. So lets examine your method.
public function setText(caption:String) {
this.btext.text = caption;
}
The first thing wrong is you are using "this". "this" is a reference to the current instance of the class you are in. And you are saying btext is a property on said instance. Which as I am assuming it is not because you defined btext as an object in your library. This will give you the property is undefined error you are gettting.
Now the second issue at hand is you are about to ask "OK how do I reference btext in my class then". What you need to know is that only objects added to the display list IE:stage can access objects via the stage.
You can do this 3 ways.
The first way is to pass a reference to the button into the class and store it as a property of the class.
The second way is to add your class to stage and in the class listen to the addedToStage event. At that time you can then access the object.
MovieClip(root)["btext"].text
The first 2 methods are not good practice since btext is not apart of the class and a general rule of thumb would be to encapsulate your class.
To make this work what you could do is have your class assign the value to a property in your class then fire an event and make the parent of this class listen to that event then just grab the value and assign.
Here is some suggested reading
I think the variable btext doesn't exist at all, or is it inherited from Movieclip?

How to reference objects in AS3?

I've learned one way to do that, but I want to improve my knowledge. For simplicity I'm not going to use import neither extends in the code below.
1
public class Main
{
public function Main()
{
new MyCustomObject(stage);
}
}
2
public class MyCustomObject
{
public var referenceStage:Stage = new Stage();
public function MyCustomObject(xxx:Stage)
{
this.referenceStage = xxx;
referenceStage.addChild(this);
}
}
I've learned it reading a tutorial over internet, but I want to know where I can find more samples on how to reference objects in AS3. For future codes, I want to add hitTest and the like.
Thanks !
The best place is the ActionScript 3 Reference from Adobe: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/index.html
Here is the specific section on objects: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Object.html
if you absolutely want to pass a stage reference through an argument to a constructor, you can do so about how you have it laid out (although get rid of the new Stage() call, which won't do anything).
that said, .stage is a property available to all display objects that are in the display list (meaning: the have been added via addChild or addChildAt).
you're probably getting that error trying to reference a .stage property of an object before it's been added to the display list. this is a common error, and can be handled by waiting to reference the .stage property until it has been added, usually using addEventListener(Event.ADDED_TO_STAGE...
so instead of
public class MyObject extends Sprite {
public function MyObject():void{
this.x = this.stage.stageWidth/2;
}
}
you'd use something like this
public class MyObject extends Sprite {
public function MyObject():void{
this.addEventListener(Event.ADDED_TO_STAGE, this.addedHandler, false, 0, true);
}
private function addedHandler(e:Event):void{
this.x = this.stage.stageWidth/2;
}
}
HTH
In your example, you don't need do call new Stage() in your CustomObject
public var referenceStage:Stage;
is enough
A hitting function may be found here http://troygilbert.com/2007/06/pixel-perfect-collision-detection-in-actionscript3/
Possible solutions are:
Instead of passing the stage object, you can also pass the main object and calling functions in the main object for the custom object
Maintain an array in the MainObject with which you want do collisions test.
Implementing an Interface (extend an object) with a function which do the hit test agains the array in the MainObject (for example went the EntreFrame Event is fired)
Custom Events are the solution for communicating with the main object loosely
Passing a reference to an object in the constructor is a classic OOP pattern