as3 array string to movieClip - actionscript-3

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
}

Related

AS3: How to Deep Copy an Object

i have an object that i need to copy to my SharedObject data.
The problem is that the data property of shared object is read-only, so i can't clone my 'source' object and assign it to the shared object data, i have to make a copy of them in this way:
var so: SharedObject = SharedObject.getLocal("appData");
copyObject(sourceObj, so.data);
so.flush();
and the copy method:
public static function copyObject(sourceObject:Object, destinationObject:Object):void{
// this would be the code that i need
}
Also have in mind that my object has properties that are objects, so it has inside n leves of objects. That is why i can't simply make a for each and assign all properties on the first level, so what i need is to make a DEEP copy, probably recursive. I tried for hours to make this copyObject method with no success. Also i've searched on the internet but i didn't find any object copy that suits me.
Can someone please help me with this method? I would really apreciate it!
Thank you for your help!
The solution is to write your object to a byte array, encoded it to a string(optional - you can probably save the byte array as well, haven't looked it up) and save it to your shared object.
This function will take an object and turn it into a string
public static function serializeToString(value:Object):String{
if(value==null){
throw new Error("null isn't a legal serialization candidate");
}
var bytes:ByteArray = new ByteArray();
bytes.writeObject(value);
bytes.position = 0;
return Base64.encodeByteArray(bytes);
}
This one will get your object back from a string.
public static function readObjectFromStringBytes(value:String):Object{
var result:ByteArray = Base64.decodeToByteArray( value) as ByteArray;
result.position = 0;
return result.readObject();
}
The Base 64 encoding class you can find here https://github.com/juancgarcia/screenshotify/blob/master/Downloadify-652377f/src/com/dynamicflash/util/Base64.as.
You need to implement IExternalizable on all objects you want to store this way. The implementation includes making writeExternal method called against a ByteArray when you do writeObject(), and readExternal methods, that's called against a newly created instance, so your class should write the necessary metadata in order to make your object deep-cloned, including writing property objects.
Manual on IExternalizable
And on a side note, you should not store one object in the entire so.data, you'd better assign a field in so.data and stuff your object copy in there.
For complex objects I would use RegisterClassAlias:
import flash.net.registerClassAlias;
registerClassAlias("YourClassName", YourClassName);
var so:SharedObject = SharedObject.getLocal("objectName");
so.data.yourData = YourClassName.instance;
so.flush();
For simple Object type with deep level of simple data (primitives including arrays) I would simply use JSON.stringify() and JSON.parse() when reading back the data.

Type coercion fails when grabbing from an array

Say I have a class Person with a private array called children, containing Person objects. It has a method getChildren():Array { return this.children; }.
If I do trace(p.getChildren()[0]) (where p is an instance of Person), I can successfully print out whatever the first child is in the array. However, if I try to cast var firstChild:Person = p.getChildren()[0], I get an error saying Type Coercion failed: cannot convert []#a027b81 to classes.Person.
What is going wrong?
When you do: var firstChild:Person = p.getChildren()[0] your not actually casting. Your just trying to stuff an Array into an object you've defined as a Person and that's why you receive the error.
To cast, you need to do one of the following:
var firstChild:Person = Person(p.getChildren()[0]); //this will error if the cast fails
var firstChild:Person = p.getChildren()[0] as Person; //this will return null if the cast fails
A better approach however, may be to use a Vector - which in AS3 is like an array but all the members have to be of the specified type. So something like this:
private var children_:Vector.<Person>;
public function getChildren():Vector.<Person>{ return this.children_; }
Then you could just do:
var firstChild:Person = p.getChildren()[0]
Because each member of the Vector is already defined as a Person object.
Also, you may want to consider using a getter method instead of getChildren.
public function get children():Vector.<Person> { return this.children_;}
Then you access it like a property (but can't set it).

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.

AS3 - Returning a property of a class rather than the class itself

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.

Actionscript 3.0 String With Format?

How can i format a string with supplied variables in AS3?
//vars
var myNumber:Number = 12;
var myString:String = "Months";
var myObject:MovieClip = year;
//string
myString.txt = "One (?) consists of (?) consecutive (?)", string(myObject), string(myNumber), myString;
so in the string above, i would like myString to display "One year consists of 12 consecutive Months", but i'm new to AS3 and do not know how to properly format a string.
i'm sure that i'll have to cast the number variable into a string, string(myNumber), but i don't know if casting a movie clip variable to a string, string(myMovieClip), will return the name of the movie clip or produce an error. i'm willing to bet on the later.
The answers to this similar question suggest using the Formatter class or StringUtil.substitute().
The latter looks the simplest; in your case you would use it like this:
var str:String = "One {0} consists of {1} consecutive {2}";
var newString:String = StringUtil.substitute(str, myObject, myNumber, myString);
substitute() should automatically cast its arguments to String, but I'm not sure if, as in your code, you can cast a MovieClip (myObject) as a String.
Another good option, especially if you've used printf in other programming languages, is this third-party printf-as3 function.
Casting objects to strings
The method toString() is defined on the Object class. So all objects have this method defined for them. Calling myObject.toString() will therefore usually give you what you're looking for. Certain objects define additional methods, such as date.getHours(), which return string descriptions of the object in a different format from that supplied by getString().
For native types such as int, you can cast using String(myInt).
Concatenating strings together
You can then add together the different parts of a string as follows:
var myString:String = "There are " + String(24) + " hours in a day."
Hope that helps,
Dave
The shorter way I'd do it is something like:
var finalString:String = "One " + myObject + " consists of " + myNumber + " " + myString;
A single or double quote initiates a string literal. If you use the + symbol to append something to a string literal it's going to automatically call toString() on that object.
myObject will return [Object MovieClip], though. What you want to do is create a custom class that extends MovieClip and then override the toString() protected method to return whatever string you want it to spit out.
Hope that helps!