AS3: How to simplify Action Script 3 Code? - actionscript-3

Here's a example that I've to use when I want to create a button with mouse-over effect:
this.buttonExample.buttonMode = true;
this.buttonExample.useHandCursor = true;
this.buttonExample.addEventListener(MouseEvent.CLICK,myaction);
I'm new to AS3 - is there any way, to simplify this code like this:
this.buttonExample.buttonMode = true;.useHandCursor = true;.addEventListener(MouseEvent.CLICK,myaction);
why does it not works ?

Its already as simple as it gets. Firstly
this.buttonExample.buttonMode = true;
this.buttonExample.useHandCursor = true;
this.buttonExample.addEventListener(MouseEvent.CLICK,myaction)
is much more readable than
this.buttonExample.buttonMode = true;.useHandCursor = true;.addEventListener(MouseEvent.CLICK,myaction);
Always go for readbility over anything else. And secondly,
this.buttonExample.buttonMode = true;
does not return an object so you can't interact with anything.

If you're using that pattern a lot, you can make a helper function:
public function setAsButton(button:Sprite, clickHandler:Function):void {
button.buttonMode = button.userHandCursor = true;
button.addEventListener(MouseEvent.CLICK, clickHandler);
}
Then call it somewhere:
setAsButton(this.buttonExample, myaction);

If you feel that typing this.buttonExample over and over again is too repetitive, simply assign that object to a variable and use that variable in the rest of the statements:
var b : Button = this.buttonExample;
b.buttonMode = true;
b.useHandCursor = true;
b.addEventListener(...);
As other's have mentioned, there's also the with statement, but it's use is discouraged since it makes the code harder to read, and may lead to weird results:
with (this.buttonExample) {
buttonMode = true;
useHandCursor = true;
addEventListener(...);
}
You can, of course, combine these suggestions with other tricks, like chaining assignments:
var b : Button = this.buttonExample;
b.buttonMode = b.useHandCursor = true;
b.addEventListener(...);
Be very careful to only chain assignments in this way if the assigned value is immutable (e.g. true, false, numbers and strings, but not arrays or most other objects), because the same object will be assigned to all variables on the left side. If the value is immutable this doesn't matter, but if it's mutable you can end up with weird results, like this in this example:
a = b = [ ];
a.push(1);
b.push(2);
trace(a); // outputs 1, 2
trace(b); // also outputs 1, 2
The reason for this result is that a and b both reference the same array, and since arrays are mutable it doesn't matter how you access the object, it will still be changed. a and b don't reference different arrays just because they are different variables.
You may think that you could do something like the following, but it will not work.
// this will NOT work
var b : Button = this.buttonExample;
(b.buttonMode = b.useHandCursor = true).addEventListener(...);
The reason why it works to say b.buttonMode = b.useHandCursor = true, but not to add .addEventListener(...) is that the value of an assignment expression (e.g. b.buttonMode = true) is the value assigned to the left hand side (e.g. true). If you add .addEventListener(...) to that you are essentially saying true.addEventListener(...), which clearly is not what you want. In other words
b.buttonMode = b.useHandCursor = false;
is equivalent to
b.useHandCursor = false;
b.buttonMode = b.useHandCursor;
Which should hopefully also make the caveats mentioned above plain.

you can use the with statement. however I'd not encourage you to do so, since it leads to a lot of ambiguity and unclearness.
also, you can have multiple assignments:
this.buttonExample.buttonMode = this.buttonExample.useHandCursor = true;
this sometimes is useful, but for the sake of readability, you shouldn't overuse it.
greetz
back2dos

Related

Making a simple flexible translate function in nodejs

In nodejs I have these (very long) translation files
gb.json (english)
{
"transHi":"Hello",
"transBye":"Goodbye"
}
de.json (german)
{
"transHi":"Gutentag",
"transBye":"Auf Wiedersehen"
}
I have a lot of controllers that all need these texts available in the many languages so I can call them when ever needed.
The obvious way would be something like this in my app.js:
global.gb = require('../global/language/gb.json');
global.de = require('../global/language/de.json');
And when I need a text I would call like:
myText = global.gb.transHi
myText = global.de.transHi
But!! the language is always determined by a variable
usersLanguage = "de"
myText = global.usersLanguage.transHi
And that wont work.
I also tried:
usersLanguage = "de"
myText = global.usersLanguage.transHi
Perhaps I could solve it with a function that has a long switch structure
var findText = (language,textkey) => {
switch(language) {
case "gb:
return gb.textkey
break;
case "de:
return de.textkey
break;
}
}
myText = translate(usersLanguage, "transHi");
But I cant seem to make that work either.
How would I do this in a simple and flexible way?
UPDATE: Is it possible to do this?
Any object property accessed via . can also be accessed using array index notation []. So,
var langObj = texts.gb;
is the same as
var langObj = texts["gb"];
which is also the same as
var lang = "gb";
var langObj = texts[lang];
Same for the textkey, using the .textkey you get the property called textkey, which probably doesn't exist. If you want a different property depending on the value of the variable textkey, do
var text = langObj[textkey];

Is it possible to make a collapsing variables without making individual functions?

I have a code that starts as a small amount of variables and makes more elements using those initial variables.
function new( x, y, width, height )
local object = {}
--border
object.border = { x = x, y = y, width = width, height = height }
--body
object.body = { x = x+1, y = y+1, width = width-2, height = height-2 }
--font
object.font = {}
object.font.size = (object.body.height+2)-(math.floor((object.body.height+2)/4)+1)
object.font.height = love.graphics.setNewFont( object.font.size ):getHeight()
--padding
object.padding = {}
object.padding.height = math.floor(object.border.height*(2/29))
object.padding.width = object.padding.height*3
--text
object.text = { input = '' }
object.text.centerHeight = math.ceil(object.body.y+((object.body.height-object.font.height)/2))
object.text.left = object.body.x+object.padding.width+object.padding.height
--backspacing
object.backspace = {key = false, rate = 3, time = 0, pausetime = 20, pause = true}
--config
object.config = { active = true, devmode = false, debug = false, id = gui.id(), type = 'textbox' }
gui.add(object)
return object.config.id
end
and when I modify something in the middle part, the whole thing becomes a mess because starting from the one i changed until the bottom ones value doesn't agree with each other
local x = gui.get(2)
x.body.height = 50
I'm looking if there's a way for these variables to be redefined, starting from them until the bottom, without: (a) making functions for each of the variables. and (b) editing the required parameters in the function.
and If there's none, are the an alternate way to do this efficiently?
EDIT:
the structure of the variables is as follow:
border->body->padding->font
what i needed is a way i can define any of them so that the one that follows also changes like:
object.body.x = 15
and it would collapse from that redefined variable until the bottom:
body->padding->font
i could just redefine them from the edited variable until the bottom like this:
--not the actual code, just an example of variables dependent on the variable above
object.body.x = 15
object.padding.width = object.body.x+1
object.font.size = object.padding.width+1
but that means I have to do the same when redefining the padding until the font which is extremely inefficient especially when I extended more elements.
example:
--padding->font
object.padding.width = 5
object.font.size = object.padding.width+1
I was bored and saw this question (again) along with a duplicate.
I started writing some code for fun, leading to this:
local function getNeededVars(tab,func)
local needed,this = {}
this = setmetatable({},{
__index = function(s,k)
-- See if the requested variable exists.
-- If it doesn't, we obviously complain.
-- If it does, we log it and return the value.
local var = tab.vars[k]
if not var then
error("Eh, "..k.." isn't registered (yet?)",5)
end needed[k] = true return tab.vals[k]
end;
}) func(this) return needed
end
local function updateStuff(self,key,done)
for k,v in pairs(self.levars) do
if v.needed and v.needed[key] then
if not done[v] then done[v] = true
self.vals[v.name] = v.func(self)
updateStuff(self,v.name,done)
end
end
end
end
local function createSubTable(self,key,tab)
return setmetatable({},{
__newindex = function(s,k,v)
tab[k] = v updateStuff(self,key,{})
end; __index = tab;
})
end
local dependenceMeta
dependenceMeta = {
__index = function(self,k)
-- Allow methods, because OOP
local method = dependenceMeta[k]
if method then return method end
local variable = self.vars[k]
if not variable then
error("Variable "..k.." not found",2)
end return self.vals[k]
end;
__newindex = function(self,k,v)
local variable = self.vars[k]
if not variable then
error("Use :Register() to add stuff",2)
elseif type(v) == "table" then
self.vals[k] = createSubTable(self,k,v)
return updateStuff(self,k,{})
end self.vals[k] = v updateStuff(self,k,{})
end
}
function dependenceMeta:Register(var,value)
local varobject = {func=value,name=var}
self.vars[var] = varobject
table.insert(self.levars,varobject)
if type(value) == "function" then
varobject.needed = getNeededVars(self,value)
self.vals[var] = value(self)
elseif type(value) == "table" then
self.vals[var] = createSubTable(self,var,value)
elseif value then
self.vals[var] = value
end
end
function dependenceMeta:RegisterAll(tab)
for k,v in pairs(tab) do
self:Register(k,v)
end
end
local function DependenceTable()
return setmetatable({
levars = {};
vars = {};
vals = {};
},dependenceMeta)
end
local test = DependenceTable()
test:Register("border",{
x=20; y=50;
height=200;
width=100;
})
test:Register("body",function(self)
return {x=self.border.x+1,y=self.border.y+1,
height=self.border.height-2,
width=self.border.width-2}
end)
test:Register("font",function(self)
local size = (self.body.height+2)-(math.floor((self.body.height+2)/4)+1);
return { size = size; -- Since we use it in the table constructor...
height = size-4; --love.graphics.setNewFont( self.font.size ):getHeight();
-- I don't run this on love, so can't use the above line. Should work though.
}
end)
test:Register("padding",function(self)
local height = math.floor(self.border.height*(2/29))
return { height = height; width = height*3 } -- again dependency
end)
test:Register("text",{input=""}) -- Need this initially to keep input
test:Register("text",function(self)
return { input = self.text.input;
centerHeight = math.ceil(self.body.y+((self.body.height-self.font.height)/2));
left = self.body.x+self.padding.width+self.padding.height;
}
end)
test:Register("backspace",{key = false, rate = 3, time = 0, pausetime = 20, pause = true})
-- Again, didn't use gui.id() on the line below because my lack of LÖVE
test:Register("config",{active=true,devmode=false,debug=false,id=123,type='textbox'})
print("border.x=20, test.text.left="..test.text.left)
test.border = {x=30; y=50; height=200; width=100;}
print("border.x=30, test.text.left="..test.text.left)
test.border.x = 40
print("border.x=40, test.text.left="..test.text.left)
It's a lot of code, but I liked writing it. It gives this nice output:
border.x=20, test.text.left=73
border.x=30, test.text.left=83
border.x=40, test.text.left=93
All properties only get recalculated when one of its dependencies is edited. I made it also work with subtables, which was a bit tricky, but at the end actually seems quite easy. You can edit (for example) the body field by setting it to a completely new table or by setting a field in the already existing table, as seen in the last few lines of the code snippet. When you assign it to a new table, it'll set a metatable on it. You can't use pairs (& co) neither, unless you use 5.2 and can use __pairs.
It might solve your problem. If not, I had fun writing it, so at least it'll always be something positive that I wrote this. (And you have to admit, that's some beautiful code. Well, the way it works, not the actual formatting)
Note: If you're gonna use it, uncomment the love.graphics and gui.id part, as I don't have LÖVE and I obviously had to test the code.
Here's a quick "summary" of my thing's API, as it might be confusing in the beginning:
local hmm = DependenceTable() -- Create a new one
print(hmm.field) -- Would error, "field" doesn't exist yet
-- Sets the property 'idk' to 123.
-- Everything except functions and tables are "primitive".
-- They're like constants, they never change unless you do it.
hmm:Register("idk",123)
-- If you want to actually set a regular table/function, you
-- can register a random value, then do hmm.idk = func/table
-- (the "constructor registering" only happens during :Register())
-- Sets the field to a constructor, which first gets validated.
-- During registering, the constructor is already called once.
-- Afterwards, it'll get called when it has to update.
-- (Whenever 'idk' changes, since 'field' depends on 'idk' here)
hmm:Register("field",function(self) return self.idk+1 end)
-- This errors because 'nonexistant' isn't reigstered yet
hmm:Register("error",function(self) return self.nonexistant end)
-- Basicly calls hmm:Register() twice with key/value as parameters
hmm:RegisterAll{
lower = function(self) return self.field - 5 end;
higher = function(self) return self.field + 5 end;
}
-- This sets the property 'idk' to 5.
-- Since 'field' depends on this property, it'll also update.
-- Since 'lower' and 'higher' depend on 'field', they too.
-- (It happens in order, so there should be no conflicts)
hmm.idk = 5
-- This prints 6 since 'idk' is 5 and 'field' is idk+1
print(hmm.field)
You could use setfenv (if Lua 5.1) to remove the need of 'self.FIELD'. With some environment magic you can have the constructor for 'field' (as an example) just be function() return idk+1 end.
You could make use of metatables, more specific, the __newindex field:
(Well, need to combine it with the __index field, but eh)
function new(x, y, width, height )
local object = {
font = {}, padding = {}, text = {input=''}, -- tables themself are static
-- also I assume text.input will change and has to stay the way it is
}
-- more static data here (yes yes, I know. The code is a bit ugly, but if it works fine...)
object.config = { active = true, devmode = false, debug = false, id = gui.id(), type = 'textbox' }
object.backspace = {key = false, rate = 3, time = 0, pausetime = 20, pause = true}
object.border = { x = x, y = y, width = width, height = height }
-- stuff that has to be calculated from the above variables goes below
local border = object.border
local function calculate()
--border
--body
object.body = { x = border.x+1, y = border.y+1, width = border.width-2, height = border.height-2 }
--font
object.font.size = height-(math.floor(height/4)+1)
object.font.height = love.graphics.setNewFont( object.font.size ):getHeight()
--padding
object.padding.height = math.floor(object.border.height*(2/29))
object.padding.width = object.padding.height*3
--text
object.text.centerHeight = math.ceil(object.body.y+((object.body.height-object.font.height)/2))
object.text.left = object.body.x+object.padding.width+object.padding.height
--backspacing
--config
end
calculate()
local proxy = setmetatable({},{
__index = object; -- proxy.abc returns object.abc (to get width, use proxy.border.width)
__newindex = function(s,k,v)
-- fires whenever 'proxy[k] = v' is done
-- I assume you'll only change x/y/width/height, as other properties are dynamic
-- Doing 'proxy.x = 123' is the same as 'object.border.x = 123' + recalculating
object.border[k] = v -- Actually apply the change
calculate() -- Recalculate the other properties that depends on the above
end;
})
gui.add(object)
return object.config.id
end
You can run code like proxy.x = 12 to edit the X property. All values will be recalculated. It's not the best, but your code a tiny bit annoying to improve. (But hey, if it works fine for you, it's good)
Note: You can only set x, y, width and height. You can get all properties the old way though, e.g. proxy.padding.width (Mind that proxy.x doesn't work. Use proxy.border.x)

Coding a triple-state Boolean in AS3

I know that a Boolean can have only 2 values, false and true. And I can code a Class that has a property that can have 3 states. But how can I code it in such a way so that, when setting or getting its state, I can refer to the object itself as opposed its property? eg:
var triBoolean:TriBoolean = 0;
// later...
triBoolean = 1;
The reason I want to do this is so I can have a value that toggles through three states and I'm assuming it would be quicker to refer directly to the object - but perhaps that's not right.
Or is it only primitive types that can be used in this way?
If you use an Object type you can do it, having: (null, true or false) tri-state, you will lose type safety, but still valid:
var triBoolean:Object = null;
// Then you can assign your triple states...
triBoolean = false; //valid
triBoolean = true; //valid
triBoolean = null; //valid

AS3 How to make a kind of array that index things based on a object? but not being strict like dictionary

How to make a kind of array that index things based on a object? but not being strict like dictionary.
What I mean:
var a:Object = {a:3};
var b:Object = {a:3};
var dict:Dictionary = new Dictionary();
dict[a] = 'value for a';
// now I want to get the value for the last assignment
var value = dict[b];
// value doesn't exits :s
How to make something like that. TO not be to heavy as a lot of data will be flowing there.
I have an idea to use the toString() method but I would have to make custom classes.. I would like something fast..
Why not make a special class that encapsulates an array, put methods in there to add and remove elements from the array, and then you could make a special method (maybe getValueByObject(), whatever makes sense). Then you could do:
var mySpecialArrayClass:MySpecialArrayClass = MySpecialArrayClass();
var a:Object = {a:3};
var b:Object = {a:3};
mySpecialArrayClass.addElement(a,'value for a');
var value = mySpecialArrayClass.getValueByObject(a);
I could probably cook up a simple example of such a class if you don't follow.
Update:
Would something like this help?
http://snipplr.com/view/6494/action-script-to-string-serialization-and-deserialization/
Update:
Could you use the === functionality? if you say
if ( object === object )
it compares the underlying memory address to see if two objects are the same reference...

Using one variable for different text fields or no in AS3?

I was wondering which way it would be better to do the stuff bellow (in order to increase performance)..
var _tfShopCoins:TextField = _mcShop.tfCoins;
_tfShopCoins.mouseEnabled = false;
_tfShopCoins.text = "";
var _tfShopMoney:TextField = _mcShop.tfMoney;
_tfShopMoney.mouseEnabled = false;
_tfShopMoney.text = "";
or
var _tfText:TextField = _mcShop.tfCoins;
_tfText.mouseEnabled = false;
_tfText.text = "";
_tfText = _mcShop.tfMoney;
_tfText.mouseEnabled = false;
_tfText.text = "";
My guess is that it's the second one as there I declare only 1 variable.
Go with the first one. I personally would never reassign a variable like you did in the second example because its less readable and the performance increase would be next to nothing.
I don't think combining them into one variable will work the way you've written it out here, since you're reassigning the _tfText variable to something else (to itself, in this case).
If you have multiple text fields with the same behavior, you could put them in an array and iterate over the items -- it might save you a few lines of code, but that's about it.
In your second example, did you mean to type "_tfText.mouseEnabled..." etc.?
In any case, neither of these approaches will have a measurable impact unless you're doing it millions of times, and you're better off directly addressing the text fields in the first place:
_mcShop.tfMoney.mouseEnabled = false;
_mcShop.tfMoney.text = "";
etc.