libgdx - scene2d.ui label height - libgdx

Why is this happening?
TextBounds tBounds1 = game.font.getWrappedBounds("blah\nblah\nblah", 300);
System.out.println(""+tBounds1.height); // outputs 79.0001
TextBounds tBounds2 = game.font.getWrappedBounds("blah", 300);
System.out.println(""+tBounds1.height); // outputs 20.00002
So the variable tBounds1 has changed simply by calling getWrappedBounds() on another variable. How is this even...?

Is seems that tBounds1 and tBounds2 are pointing to the same object.
If you try to check them, it is happening that areSame below it is true
boolean areSame = tBounds1 == tBounds2;
so tBounds1 and tBounds2 are pointing to the same object. The getWrappedBounds method is called by:
public TextBounds getWrappedBounds (CharSequence str, float wrapWidth) {
return getWrappedBounds(str, wrapWidth, cache.getBounds());
}
Not the cache.getBounds. The cache is created at start when creating the BitmapFont
private final BitmapFontCache cache;
so the cache is a property of the current font and therefore when something is changed there, it is also propagated.
the getWrappedBounds method calls also the other method:
public TextBounds getWrappedBounds (CharSequence str, float wrapWidth, TextBounds textBounds) {
// Just removed the lines here
textBounds.width = maxWidth;
textBounds.height = data.capHeight + (numLines - 1) * data.lineHeight;
return **textBounds**;
}
This method at the end is changing the BitmapFontCache cache object.
So if you want to calculate the height for 2 different strings, you can assign it to primitive types:
float height1 = font.getWrappedBounds("blah\nblah\nblah", 300);
float height2 = font.getWrappedBounds("blah", 300);
or if you need a full BitmapFont.TextBounds object, this:
BitmapFont.TextBounds tBounds1 = new BitmapFont.TextBounds(font.getWrappedBounds("blah\nblah\nblah", 300));
BitmapFont.TextBounds tBounds2 = new BitmapFont.TextBounds(font.getWrappedBounds("blah", 300));
In this way you wound ensure that tBounds1 and tBounds2 are pointing to different objects.

Related

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?

Unable to save class objects to a SharedObject FIle

Well basically as the title implies, I can't save my array to the shared object.
I have an array which contains different "soldiers" with different characteristics(Health,Armor,Weapon,Position,Exp,Level) etc etc and was wondering how I would go about saving it. When I reload the swf I get this trace (",,,") but before I reload it I get a correct array reading.
This is my code if it helps:
//Saving game
function saveGame(E:MouseEvent){
var so:SharedObject = SharedObject.getLocal("saveFile"); //Instantiating the shared object
so.data.savedUnitArray = towerDefenceMain.unitArray;// is the array that stores the Soldiers
trace(so.data.savedUnitArray); //returns correct trace
so.flush();//Saving the operation
}
//Loading the data back
var so:SharedObject = SharedObject.getLocal("saveFile");
if(so.data.savedUnitArray != undefined){
unitArray = so.data.savedUnitArray;
trace(unitArray); //returns (",,,,")
}
In order to save a custom object, you either have to make all its properties public and accessible, AND have no references to DisplayObjects, or implement IExternalizable and define writeExternal() and readExternal() methods. Note that if your object is read from elsewhere, it first is initialized via zero parameter call to its constructor, and then the instance's readExternal() is invoked.
The manual on IExternalizable
An example:
public class Tower2 extends Obstacle implements gameRunnable,IExternalizable {
// HUGE set of statistics skipped, all methods skipped
public function writeExternal(output:IDataOutput):void {
output.writeInt(gemType);
output.writeInt(gemGrade);
output.writeBoolean(affectsFlying); // as some gems might be elongated, saving this
output.writeInt(_targetMode); // placeholder for targetting
output.writeInt(kills);
// hehe, what else to write in here? Everything else is derivable
}
public function readExternal(input:IDataInput):void {
var gt:int = input.readInt();
var gg:int = input.readInt();
MakeGem(gt, gg); // this is the function that initializes everything that's the tower
raised = true; // will place manually if ever
affectsFlying = input.readBoolean();
gt = input.readInt();
SetTargetting(gt);
kills = input.readInt(); // kills
updateDamage(); // this updates damage respective to kills counter
}
So, for your Soldiers you only need to save essential data, and re-create everything else once you load your set of soldiers from the shared object.

AS3 XMLListCollection data sorting on multiple fields and crashing when value changed

My XML:
<destinations>
<destination>
<fav>1</fav>
<cheapest>140</cheapest>
</destination>
<destination>
<fav>0</fav>
<cheapest>150</cheapest>
</destination>
</destinations>
I am creating XMLListCollection for my spark List component.
var dataprovider:XMLListCollection = new XMLListCollection(xml.destination);
I am trying to sort this XMLListCollection using fav and cheapest element.
var sort:Sort = new Sort();
sort.fields = [new SortField("fav" , true , true)];
sort.fields.push(new SortField("cheapest" , false , true));
dataprovider.sort = sort;
dataprovider.refresh();
Everything works fine till I update value of fav:
xml.destination.(id == String(destId))[0].fav = 0;
XML structure looks exactly the same after the update but I get thrown error from my itemrenderer object :
override public function set data( value:Object ) : void {
dest_name.text = value.text;
}
Error stating that value is null. How can value be null in the first place? I get no error when I remove fav from sort fields or update cheapest element instead.
Does anyone have any idea about this anomaly?
You have to take into account that your itemrenderers are recycled, for example, if order of items in your collection changes(when you change value of sort field). When renderers are recycled, null can be passed to set data function.
That means your function
override public function set data( value:Object ) : void {
dest_name.text = value.text;
}
shall be changed like that:
override public function set data( value:Object ) : void {
if(value){
dest_name.text = value.text;
}
}
You should always keep this in mind when implementing item renderers.

Why does as3crypto say "Invalid padding value"?

What is the cause of the error "Invalid padding value" in as3crypto?
Error: PKCS#5:unpad: Invalid padding value. expected [153], found [25]
at com.hurlant.crypto.symmetric::PKCS5/unpad()
at com.hurlant.crypto.symmetric::CTRMode/decrypt()
at com.hurlant.crypto.symmetric::SimpleIVMode/decrypt()
at com.mycompany.myproject::Application$/decrypt()
... (followed by the rest of my application stack)
I think I've previously solved this by making sure that the encrypted data is prepended with the intialization vector (IV), by using the SimpleIVMode wrapper class. In this case, I'm already doing that, though.
I'm not using the Crypto class because minimizing download size is important.
Any ideas?
My abstraction code (in Application class):
protected static var cipher:ICipher =
new SimpleIVMode(
new CTRMode(
new AESKey( Hex.toArray("53c12a8eb8612733ec817290580c3d") // not actual key
))
);
public static function encrypt(d:ByteArray):ByteArray {
d.position = 0;
cipher.encrypt(d);
d.position = 0;
return d;
}
public static function decrypt(d:ByteArray):ByteArray {
d.position = 0;
cipher.decrypt(d); // THIS LINE THROWS
d.position = 0;
return d;
}
All that d.position = 0 stuff is my paranoia.
The encryption code:
// we first have to serialize the object to a ByteArray, then encrypt that data.
var encryptedValue:ByteArray = new ByteArray();
encryptedValue.writeObject(objectToEncrypt);
encryptedValue.position = 0; // paranoia?
Application.encrypt(encryptedValue);
so.setProperty(key, encryptedValue); // save it in my SharedObject
Now the code that causes the error:
var data:ByteArray = so.data[key]; // get the byte array out of storage.
trace(data.length); // Check that it's real... I get 553 bytes
Application.decrypt(data); // THIS LINE THROWS
i think you should extend memory size of your SharedObject, like
so.flush(1000000000000000);

ActionScript Defining a Static Constant Array

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.