Nonfunctioning "for loop" for addressing each variable of class - actionscript-3

I'm learning AS3 but have some antiquated background in programming (TP and Atari Basic). On this forum I learned to use a loop such as the one below to address each variable in an object class, in order to make a clone of the object (deep or shallow) or in my case to build the text for a tooltip. However mine doesn't work. Here's the loop, following is an explanation, any help you can give I'd appreciate greatly!
var tooltipText:String;
var i:String;
for (i in bsm) {
if (!(bsm[i] is String)) {
if (bsm[i] != 0) {
tooltipText = i + ": " + bsm[i];
tooltip.extendTooltip(tooltipText, 0xFFFFFF);
}
}
}
Please forgive the horrible variable names. 'i' is a String. 'bsm' is a non-null instance of class StatMod, which begins with
public class StatMod extends Object {
public static const ENCHANTMENTMODIFIER:String = "enchantmentModifier";
public var enchantmentType:String = "None";
public var enchantmentDescriptor:String = "None";
public var minDamage:Number = 0;
public var maxDamage:Number = 0;
public var attackSpeed:Number = 0.2;
The intended behavior is to go through each of the variables of StatMod (I'm not showing them all and I will add more later), and if the variable is a non-zero number, make a string ("attackSpeed: 0.2" for instance) and then add that string to the tooltip. The tooltip.extendTooltip function is working properly.
The observed behavior is basically the computer believing that there are no variables in bsm.
What can I say or do to convince the computer that there actually are variabels in bsm?

The behavior you're expecting is only the case when iterating over dynamically attached properties. For example, if you marked your class dynamic:
public dynamic class StatMod { }
Then added some values to it at runtime:
bsm.test = 5;
Your loop will find the property test with the value 5.
Some options you have to achieve what you want are:
Extend the Proxy class to define what properties are iterable via nextName and nextNameIndex.
Use describeType to generate a list of all the public properties.
Though a simpler method is to expose a list of the properties you want to iterate over and use that in your loop instead, something like:
public class StatMod {
// Existing properties etc.
private _properties:Vector.<String>;
public function get properties():Vector.<String> {
if (_properties === null) {
_properties = new <String>[
'enchantmentType',
'enchantmentDescription',
'minDamage',
'maxDamage',
'attackSpeed'
];
}
return _properties;
}
}
Then:
for (var i:int = 0; i < bsm.properties.length; i++) {
var prop:String = bsm.properties[i];
trace(prop, bsm[prop]);
}

Related

Sorting an array by a private variable

So... I have spent the last hour trying to figure out why the sort method for my array was not working properly, when I realized that the variable I was trying to sort by in my objects was not publicly available. I have accessed it by using a getter method, which has worked fine for all other purposes. My questions is: Is it possible to sort by a private variable somehow? Perhaps by using a getter method, but I don't know how that would work syntactically. Or do I just have to make my variable public?
On a slightly related note, is there some way to sort on a variable of an object in a vector using standard methods?
Here is an example using get function to sort
var k:Vector.<A> = new Vector.<A>();
k.push(new A(5));
k.push(new A(3));
k.push(new A(7));
k.push(new A(1));
k.push(new A(9));
k.sort(compareFunction);
private function compareFunction(obj1:Object, obj2:Object, properties:Array = null):int
{
var a1:A = obj1 as A;
var a2:A = obj2 as A;
if (a1.level > a2.level)
{
return 1;
}
else if (a1.level < a2.level)
{
return -1;
}
else
{
return 0;
}
}
class A {
private var _level:int;
public function get level():int
{
return _level;
}
public function A($level:int)
{
_level = $level
}
}

converting element by using AS operator

So im creating something that now is finished and i want not to create every time elements, but to Pool them (ObjectPooling)
The problem comes that my object from the pool doesnt have the variable from the class it mimics, or at least i understand it that way, cause it doesnt do what it should.
Can someone tell me does this
var myNewBox:Box = Pool_myBox.getSprite() as Box;
mean that all the proparties and parameters that the class Box() has will be given and can be used by the new variable myNewBox or it`s a little more tricky that this?
or in other words is var myNewBox:Box = new Box();
the same as
var myNewBox:Box = Pool_myBox.getSprite() as Box;
------------EDIT-----------
so i do private var Pool_myBox:SpritePool; in the Main Class .
and set Pool_myBox = new SpritePool(Bullet,20); so in the 1st moment it has generated 20 objects.
The whole SpritePool class is this
package {
import flash.display.DisplayObject;
public class SpritePool {
private var pool:Array;
private var counter:int;
private var classRef:Class;
public function SpritePool(type:Class, len:int) {
pool = new Array();
counter = len;
classRef = type;
var i:int = len;
while (--i > -1) {
pool[i] = new type();
}
}
public function getSprite():DisplayObject {
if (counter > 0) {
return pool[--counter];
} else {
increasePool(10);
return getSprite();
}
}
public function increasePool(amount:int):void {
counter += amount;
while( --amount > -1 )
pool.unshift ( new classRef() );
}
public function returnSprite(s:DisplayObject):void {
pool[counter++] = s;
//trace(pool.length)
}
}
}
Absolutely not. If your getSprite() method does not return a Box instance (or some descendant of it), it will not 'inherit' the properties of Box. as is not performing any kind of internal magic - it is simply casting and telling the compiler that you know what you are doing and that the object indeed is a XXX instance (fill in). You should use casting only when going from a more general type to a more specific type, let's assume this:
var child:Sprite = parent.getChildAt(0); //what does this return? A display object instance => compiler will throw an error as Sprite is not DisplayObject
/*
Implicit coercion of a value with static type flash.display:DisplayObject to a possibly unrelated type flash.display:Sprite.
*/
//so you cast it:
var child:Sprite = parent.getChildAt(0) as Sprite; //this won't throw anything cos you casted it correctly
Also note that:
myObj as MyObj
is the same as:
MyObj(myObj)
if Pool_myBox.getSprite returns only Box objects, then you don't need to cast. The getSprite function should be look something like:
public function getSprite():Box {
var recycled_box:Box = ... // get box from pool
return recycled_box;
}
var myNewBox = Pool_myBox.getSprite();
Then, myNewBox will look and act like a Box. Note that any initialization or processing that happened on previous Box instances must be undone when it's returned to the pool if you need a "fresh" instance of Box.
OK, given the pool class, it looks like it should work with casting. Note that your text says you're passing in "Bullet" as the Class, while your code seems to want Box's (I assume this is either a typo, or Bullet is a superclass of Box?). If it works on the first 20, but not after you start recycling, then check what may need to be undone (as above).
What behavior are you seeing that makes you think it's not returning the right Class?

Restoring custom class object array from SharedObject

I have an array of Widgets (a class I created) called "widgetArray" that I save into a shared object.
savedGame1.data.widgetArray = widgetArray;
When I go to load this data out and use the widgets I run into problems. The array is stored fine, but each widget is saved as an object. So I can't simply do:
widetArray = savedGame1.data.widgetArray;
because then if I try to use the widgets:
widgetArray[0].someWidgetFunction();
it does not work because flash thinks they are objects. I have tried typecasting the widgets when I load them, but it only produces a null object.
widgetArray[i] = widgetArray[i] as Widget;//widgetArray[i] becomes null
tempWidget:Widget = widgetArray[i] as Widget;//null as well
This question is asked in one of the forums from a friend, since there were no responses, i thought this is the right place for it...
How does anyone else deal with this?
Make two methods that can save and load a widget, the save method should take the data from the widget class and save it into the shared object the load class will take the data from the shared object and set the properties of the object.
Something like this :
public function save():Object
{
return {
"x":this.x,
"y":this.y,
"otherStuff":otherStuff
};
}
public function load(data:Object):void
{
this.x = data.x;
this.y = data.y;
this.otherStuff = data.otherStuff;
}
You can call the save method and store the results in an array and then store it in the shared object. You only need to save the data that is required to rebuild the widget class, not all the properties of the class.
Edit : Updated based on BlueRaja's comment.
As BlueRaja pointed out IExternalizable is meant to be used for this.
If you have a class like this :
public class MyClass implements IExternalizable
{
private var one:int = 1;
private var two:int = 2;
public function MyClass()
{
}
public function writeExternal(output:IDataOutput):void
{
output.writeInt(one);
output.writeInt(two);
}
public function readExternal(input:IDataInput):void
{
one = input.readInt();
two = input.readInt();
}
public function print():void
{
trace(one);
trace(two);
}
}
Then you can save it like this:
registerClassAlias("MyClass", MyClass);
var sharedObject:SharedObject = SharedObject.getLocal("so");
var myClass:MyClass = new MyClass();
sharedObject.data['storedObject'] = myClass;
sharedObject.flush();
Then to load it :
registerClassAlias("MyClass", MyClass);
var sharedObject:SharedObject = SharedObject.getLocal("so");
var loadedClass:MyClass = sharedObject.data['storedObject'] as MyClass;
loadedClass.print();
Hope that helps.
http://tush.wordpress.com/2007/07/08/actionscript-3-serializing-classes-using-registerclassalias/ ... This is also a genuine way to store the custom class objects in shared objects

How to instantiate an array of custom classes in Action Script 3.0

I'm new to AS3 and I'm getting this error while trying to implement OO style code.
Incorrect number of arguments. Expected no more than 0.
When I try to:
var countries:Country = new Country(10);
Normally this would work in Java or C++, so I'm not sure what's up!?
Here is my custom class.
package {
public class Country {
var cName:String = "noName";
public function Country() {
// constructor code
}
public function setName(n:String):void {
cName = n;
}
public function getName():String {
return cName;
}
}
}
You are passing 10 to the constructor, which is not what you want to do. To instantiate an array of instances, try something like this:
var countries:Array = []
var country:Country;
for (var i:uint = 0; i < 10; i++) {
country = new Country()
country.setName("Country_" + i);
countries.push(country)
}
your constructor function public function Country() {} not have an argument, but you give 10, must go wrong.
ActionScript's array not like c++, don't need element type <Country>
you want to save class in array is simple: var arr:Array = [new Country()]

Get all static variables in a class

I have this ObjectType class which is a class to help me do something like this:
object.type = ObjectType.TWO
//ObjectType.as
package
{
public class ObjectType
{
public static var ONE:String = "one";
public static var TWO:String = "two";
public static var THREE:String = "three";
public function ObjectType()
{
}
}
}
Let's suppose I'm creating a new class and I need a property named type. In that property set function I want to make sure that it's value is one of the ObjectType variables. How can I achieve this?
public function set type(value:String):void
{
for (var o:Object in ObjectType) {
if (value == o)
this._type = value;
} else {
//error
}
}
}
Not performance aware but without modifying anything you can use describeType function to check the static field and get the value back:
function valueInClass(clazz:Class, value:*):Boolean {
return describeType(clazz).variable.(clazz[#name.toString()] == value).length() != 0
}
public function set type(value:String):void
{
if (valueInClass(ObjectType, value)) {
this._type = value;
} else {
//error
}
}
I suppose the second code example you presented doesn't work...
I think it is because you're using the for in loop a little bit wrong.
for (var blah:String in somewhere){
// blah represents a KEY of the somewhere object
// to get the value of this key, use:
var theValue = somewhere[blah];
}
It's the for each loop that loops through the values. But for now I'll use the for in.
Also, it's not in ObjectType, but rather in the class' prototype, that is in ObjectType.prototype.
So, to fix this:
for (var o:* in ObjectType.prototype) {
if (value == ObjectType.prototype[o])
this._type = value;
} else {
//error
}
}
You can solve this using reflection.
A similar question was asked just a few days ago, you should be able to use the same solution, found here.
It should be noted that while the the accepted answer is right, it's also really slow. Not something that you want to do a lot. There are three simpler solutions.
One: Check the value itself:
public function set type(value:String):void
{
if( value != ObjectType.ONE && value != ObjectType.TWO && value != ObjectType.THREE )
return;
}
Obviously, the more constants you have the check the harder this becomes.
Two: Use ints as your constants
Change your ObjectType class to use ints:
public class ObjectType
{
public static var NONE:int = 0;
public static var ONE:int = 1;
public static var TWO:int = 2;
public static var THREE:int = 3;
public static var TOTAL:int = 4;
}
Notice the NONE and TOTAL in there? This makes it easy to check if your value is in the right range:
public function set type(value:int):void
{
if( value <= ObjectType.NONE || value >= ObjectType.TOTAL )
return;
}
You can add more values as needed and you just need to update TOTAL and it'll still work. This needs each value to be in order though.
Three: Use Enums
While Flash has no in-build class for enums, there's a lot of solutions available. Check our the Enum class from Scott Bilas: http://scottbilas.com/blog/ultimate-as3-fake-enums/
Using this as your base class your ObjectType class becomes:
public final class ObjectType extends Enum
{
{ initEnum( ObjectType ); } // static ctor
public static const ONE:ObjectType = new ObjectType;
public static const TWO:ObjectType = new ObjectType;
public static const THREE:ObjectType = new ObjectType;
}
And your check now becomes:
public function set type(value:ObjectType):void
{
...
}
Here, your setter now becomes type safe and will throw errors if anything other than an ObjectType is used.
It turns out that if using an ENUM type of check you should check for the constants property, not variables as showin in the example here:
ActionScript - Determine If Value is Class Constant