ActionScript Defining a Static Constant Array - actionscript-3

is it not possible to define a static const array? i would like to have an optional parameter to a function that is an array of colors,
private static const DEFAULT_COLORS:Array = new Array(0x000000, 0xFFFFFF);
public function myConstructor(colorsArray:Array = DEFAULT_COLORS)
{
}
i know i can use ...args but i actually wanting to supply the constructor with 2 separate arrays as option arguments.

Not possible, but you could this to simulate this behaviour:
private static const DEFAULT_COLORS:Array = new Array(0x000000, 0xFFFFFF);
public function myConstructor(colorsArray:Array = null)
{
colorsArray = colorsArray ? colorsArray : DEFAULT_COLORS;
}
This will not work if your function is coded in a way such that null could be a valid value (to signal some condition, for instance), but probably that's not the case here.
Edit
If you plan to write to colorsArray in myConstructor, it would be wise to make a copy of DEFAULT_COLORS here:
colorsArray = colorsArray ? colorsArray : DEFAULT_COLORS.slice();
The reference to the DEFAULT_COLORS Array is constant, but its contents are not, so you could accidentally change your default values.

Related

Function-Backed Action which creates a duplicates of an Object

Is it possible to write a TypeScript Function which programmatically creates copies of an object, without hardcoding references to the object properties being copied?
I'm trying to enable a user workflow where the user is empowered to create copies of a specified object, i.e. create and partially complete one object using a Form, create 10x duplicates of the new object, fill-in data for new objects.
I've succeeded in creating a Function-Backed Action which duplicates a designated object, but all references to the properties being copied are hardcoded, which is less than ideal for maintenance and is a relatively common request for our org.
Code example for desired Function:
#OntologyEditFunction()
public GenerticBatchCopyObjects(mySelectedObject: ObjectAPIName, numberNewObjects: Integer): void {
/** Set UUID primary key */
let object_pk = Uuid.random()
for (let i = 1; i <= numberNewObjects; i++) {
/** initialize new object */
const newObject = Objects.create().ObjectAPIName(object_pk + "_" + String(i))
/** copy all properties from selected object record to new object */
for property in mySelectedObject.properties:
if property != "name_of_primary_key_column":
newObject.property = mySelectedObject.property
}
}
There’s not really a nice way to achieve this currently and it’s maybe not advised. This is primarily because the list of properties that will be copied from an object are fixed at publish-/compile-time in any (nice) method I can see.
Partial type-safe solution for copying properties only
I've included the most generic version of this function I can construct below, but it has some limitations. More concretely, the copy function
does not copy links that aren't represented by FK properties (i.e. it only copies properties);
does not adapt and might break when there are new or removed properties; and
is not easily maintained as functionality can change depending on when it is compiled.
private isPrimaryKey(x: string): x is MyObjectType["primaryKey"]["apiName"] {
return x === MyObjectType.primaryKey.apiName;
}
#OntologyEditFunction()
public copy(obj: MyObjectType): void {
const new_obj = Objects.create().myObjectType(Uuid.random());
var prop : keyof typeof MyObjectType.properties;
for (prop in MyObjectType.properties) {
if (!this.isPrimaryKey(prop)) {
new_obj[prop] = obj[prop];
}
}
}
Explicit type-safe solution for copying everything
The approach below requires more manual adjustment if the ontology changes, but makes it explicit that the code must be changed in line with ontology changes. This approach also copies links.
It is also worth noting that this behaviour may not be desired behaviour because it doesn't recursively copy the linked objects and instead attempts to copy the links. Please test that this gives the desired results.
static PROPERTIES = ["myProperty"] as const;
static MULTILINKS = ["myMultiLink"] as const;
static SINGLELINKS = ["mySingleLink", "myOtherSingleLink"] as const;
#OntologyEditFunction()
public copy2(obj: MyObjectType): void {
const new_obj = Objects.create().myObjectType(Uuid.random());
MyFunctions.PROPERTIES.forEach(p => {
new_obj[p] = obj[p];
});
MyFunctions.MULTILINKS.forEach(p => {
obj[p].all().forEach(v => new_obj[p].add(v));
});
MyFunctions.SINGLELINKS.forEach(p => {
const v = obj[p].get();
if (v !== undefined) {
new_obj[p].set(v);
}
});
}
You may need to exclude some of the code if your object type does not have properties/multi-links/single-links to make the compiler happy.

Nonfunctioning "for loop" for addressing each variable of class

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]);
}

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

AS3 - Clone an object

I have a game with a variety of ship types. My Ship class has a static array holding one of each of the types in it. Whenever I make a new Ship (other than when initializing this array), I want to make it a clone of one of the existing Ship objects in my prototype array.
1 - How can I run through all the properties in one Ship object and assign them to a second Ship object?
2 - How can I see if a property is an object or a basic type like String or int? Some of the objects in my Ship class need to be cloned, and some are just references that need to stay the same.
One option, arguably the most agile, would be to define clone methods for each class that you need to clone, such as:
class Ship
{
public var prop1:Number;
public var otherClassInstance:OtherClass;
public function clone():Ship
{
var result:Ship = new Ship();
result.prop1 = this.prop1;
result.otherClassInstance = this.otherClassInstance.clone()
}
}
class OtherClass
{
public var prop1:Number;
public function clone():OtherClass
{
var result:OtherClass = new OtherClass();
result.prop1 = this.prop1;
}
}
Another option is to clone an object by using the ByteArray class like this example from the Adobe documentation:
function clone( source:Object ):*
{
var myBA:ByteArray = new ByteArray();
myBA.writeObject( source );
myBA.position = 0;
return( myBA.readObject() );
}
I've seen instances where this approach does not work for cloning instances of custom classes, specifically view classes like Sprites.
Another approach is to use describeType from the flash.utils package. With describeType you can iterate through the properties of an object.
Here is an example of using describeType to inspect the properties of an object that is a part of a utils lib I wrote.
As for checking the type of the property, you can use describeType or you can also use the is operator like this:
if( myObj is SomeClass )
{
}
if( myObj is OtherClass )
{
}
To run through all the properties of one ship object and assign them to a second:
shipobj1:Ship = new Ship();
//set values for all shipobj1 properties
shipobj2:Ship = new Ship();
for (item in shipobj2)
item = shipobj1[item];
Checking if a property value is an object you could use typeof. The limitation of this is that there are only 6 possible types returned: boolean, function, number, object, string, and xml. So for example if you need to know if a property is an array you can't really do that with typeof since that would actually return "object" since "array" isn't one of the 6 options, but if you're just concerned with identifying simple types like numbers and strings versus other stuff it should do the trick:
if(typeof item == "object")
// do whatever with object
else if(typeof item == "string")
// do whatever with string
//etc, etc.
EDIT: Replaced variable "var" with "item" since var is a reserved word.