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.
Related
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.
UPDATED ANSWERED: Found a WORK AROUND. I changed my array to an array of mc names rather than strings so it works now. Nevertheless, curious if this question could be answered.
I have a random array of string names. I sort them out randomly. But I need to add
an existing movieclip based the string name.
Here is what I have that doesn't work.
public function addToStage()
{
Happy = sorted.sort( randomize );
trace("First is: " +Happy[0]); /// Works!
addChild(Happy[0]); // Does not work
}
ERROR I GET
TypeError: Error #1034: Type Coercion failed: cannot convert "Apple" to flash.display.DisplayObject.
at Main/addToStage()[C:etc..\Main.as:74]
at Main/init()[C: etc...\Main.as:64]
at Main()[C: etc... \Main.as:25]
Assuming the strings in the array are names of MovieClip vars then it's as simple as this:
this.addChild(this[Happy[0]])
That effectively looks for a variable with the name equivalent to the string in the array, so this["hello"] will look for this.hello
If the strings aren't names of movieclip vars then you would need to "map" the movieclips to the strings using a dictionary or Object like so:
var dictionary:Dictionary = new Dictionary;
dictionary["hello"] = movieClipVariable;
this.addChild(dictionary["hello"]);
Let me know if this isn't what you meant and I'll have another look
Arrays are dynamic, types are lost on children of the array, the are stored as Object.
to add the to the display list you must cast them to a DisplayObject or class that extends a DisplayObject:
addChild(Happy[0] as DisplayObject); // should work
you have to do type cast a from object to displayobject.
private var tempMC:MovieClip;
public function addToStage()
{
Happy = sorted.sort( randomize );
trace("First is: " +Happy[0]); /// Works!
tempMC = Happy[0] as MovieClip;
addChild(tempMC); // it should work
}
So I have this superclass grid class, and a subclass of the grid class named GrassTile1, GrassTile2, etc... all of the instance of the subclasses are stored in an array. How am I suppose to convert the instance of subclass to its superclass referencing to the array?
private var backgroundGrid = []; //the array which the grids are stored in, in the main class.
public class Grid extends MovieClip
{
protected var node :PathfindNode; //the variable I wish to access, from an instance of subclass.
public function Grid(){
node = new PathfindNode();
}
}
public class GrassTile1 extends Grid { //every subclass of Grid will extends Grid
public function GrassTile1() {
// constructor code
}
}
function getBackgroundGrid(i:int,j:int):Grid{ //in the main class
return Grid(backgroundGrid[i][j]); // this line gives me an error
}
TypeError: Error #1034: Type Coercion failed: cannot convert GrassTile1#2905d5f1 to Grid.
I've tried accessing backgroundGrid[i][j].node and other ways to work around that I could think of and failed. Any Idea?
Try :
return backgroundGrid[i][j] as Grid;
Personally, Grid seems like a bad class name to use. I think Tile makes more sense, as that GrassTile1 is not a grid as I logically understand a grid. A grid might contain a collection of tiles, so doesn't sound logical to use that as a class name for tiles.
Also, where is the line where you actually call the getBackgroundGrid method ? You should try casting there, as opposed to in that method. I believe that will solve the problem.
I can't verify the line throwing the error, so we are assuming that it's the return statement. But, it could be on the other side where you are calling getBackgroundGrid.
UPDATE : I have tried a .fla using what you are describing and it works just fine, I get no error. Which is why I'm thinking we are missing something here and maybe the definition of the class is not being used. Can you put a trace in your constructors to verify what you expect is actually happening ?
In ActionScript 3, there are some classes that will represent a value rather than the class itself. It's hard to explain properly what I mean, so take this example:
var str:String = "something";
var mc:MovieClip = new MovieClip();
trace(str); // something
trace(mc); // [object MovieClip]
You'll notice that the first trace outputs a value, rather than [object String]. Ontop of this, I can still make use of methods of String, like this:
var ar:Array = str.split('s');
Even though in a way you could almost read the above as:
"something".split('s');
I have a class AvLevelData that has some methods that deal with level data (which is essentially a String). At the moment there is a property data:String which represents the core level data.
The question I have is - can I replicate the behaviour of String in that when I trace or assign an instance of AvLevelData, the result is actually the String data.
For example, at the moment I need to go:
var levelData:AvLevelData = new AvLevelData();
trace(levelData.data);
To get the data. I instead want to be able to simply do the following:
var levelData:AvLevelData = new AvLevelData();
trace(levelData); // some level data string
Is this possible?
If you wan't your object to trace out your own fabricated string then you must implement a toString() function on your AvLevelData class.
In your example above, the MovieClip trace outputs: [Object MovieClip]; this comes from the default toString() implementation for Object (found on Object.prototype) . Note, you cannot override toString() as it only exists on the prototype of Object (remnants of the AS2/Javascript world), all you need to do is provide your own implementation with the same name. For instance:
public function toString():String {
return "MyCustomObjectString";
}
Some of the most basic types - String, int, Number, uint, Boolean, to name a few - are not classes / objects per se, they are primitives. In some languages there is a wrapper class available for some of these so they can be treated like objects, though Flash doesn't do this so much from my experience.
Probably the best way to answer your question is to make a toString() method for your AvLevelData class:
public function toString():String {
return data;
}
Any time you treat a class as a string (such as by putting it in trace()), flash (and many other languages) try to call toString() on the object. Typically this results in a string that's not helpful. But if you define your own toString() method, you can control what string gets output.
Another option is to simply do:
trace(AvLevelData.data);
Since that variable is a string, it should trace just fine.
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.