Actionscript Child within parent within parent - actionscript-3

I am trying to make a calculator where a user can select from a list of items. If a user clicks say "ITEM1", it should add the item to a "CONTAINER_MC". The problem i have is all my data is set inside an array containing names and prices like the code below.
var menuNames:Array = [
"Item1",
"Item2",
"Item3",
"Item4",
"item5",
"item6"
];
//price array
var menuPrices:Array = [
"0.99",
"1.99",
"2.99",
"5.99",
"6.99",
"10.99"
];
Now i have a sprite which creates a menu for each of these items by the use of a movie clip containing 2 input fields which i setup like the code below.
var menuSprite:Sprite = new Sprite();
var totalItems:Number = menuNames.length;
var item:menuItem; //new item field
var btn:add_btn;
for(var i = 0; i < totalItems; i++) {
item = new menuItem();
btn = new add_btn();
menuSprite.addChild(item);
item.addChild(btn);
item.x = 0;
item.y = i * 80;
btn.y = 45;
item.itemName.text = menuNames[i];
item.itemPrice.text = "$" + menuPrices[i];
}
addChild(menuSprite);
This all works fine so far, the problem is that i have a button inside my item and i need to add even listeners for these buttons, the problem is how to target these buttons. Since these buttons are added through the for loop they are not given instance names so notice how i targetted the input fields stored within the "item", i used itemName but how would i do it to the buttons stored inside item.
Thank you, really appreciate any help possible.

Something like this:
var menuSprite:Sprite = new Sprite();
var totalItems:Number = menuNames.length;
var item:menuItem; //new item field
var btn:add_btn;
for(var i = 0; i < totalItems; i++) {
item = new menuItem();
btn = new add_btn();
btn.addEventListener(MouseEvent.CLICK,clickHndlr);
menuSprite.addChild(item);
item.addChild(btn);
item.x = 0;
item.y = i * 80;
btn.y = 45;
item.itemName.text = menuNames[i];
item.itemPrice.text = "$" + menuPrices[i];
}
addChild(menuSprite);
public function clickHndlr(event:MouseEvent):void
{
((event.currentTarget as add_btn).parent as menuItem).itemName.text="Any of changes";
}
But also I'd like to change arrays to Dictionary instance, like this:
var menuData=new Dictionary();
//filling our Dictionary instead of weird arrays
for(var i:int=0;i<10;i++)
menuData["Item"+i]=Math.round(Math.random()*100);
var menuSprite:Sprite = new Sprite();
var item:menuItem; //new item field
var btn:add_btn;
for(var menuName:Object in menuData)
{
item = new menuItem();
btn = new add_btn();
btn.addEventListener(MouseEvent.CLICK,clickHndlr);
menuSprite.addChild(item);
item.addChild(btn);
item.x = 0;
item.y = i * 80;
btn.y = 45;
var menuPrice:int=menuData[menuName] as Number;
item.itemName.text = menuName as String;
item.itemPrice.text = "$" + menuPrice.toString();
}
addChild(menuSprite);
public function clickHndlr(event:MouseEvent):void
{
((event.currentTarget as add_btn).parent as menuItem).itemName.text="Any of changes";
}
And if to be very honest, I'll put away Flash, and prefer yo use Flex+Catalyst to create great code with great UI.
Ask more if you need more information.
Regards
Eugene

Related

Randomising array coordinates with no duplicates AS3

very new to this kind of thing so please bear with me.
So basically I have 4 buttons that I have put to the stage using the Actions panel. They all have set coordinates so every time I run my game, they are in the same place.
Now here's my issue, I want these buttons to randomise their positions using those coordinates, but I often get duplicates, meaning one button is on top of another.
Here is my code so far
var coordArray : Array = [
new Point(44,420),
new Point(270,420),
new Point(44,550),
new Point(270,550),
];
var pointRange:Number = 4;
var randomPoint:int = Math.random()*pointRange;
answerButtons.x = coordArray[randomPoint].x;
answerButtons.y = coordArray[randomPoint].y;
var pointRange_2:Number = 4;
var randomPoint_2:int = Math.random()*pointRange_2;
answerButtons_2.x = coordArray[randomPoint_2].x;
answerButtons_2.y = coordArray[randomPoint_2].y;
var pointRange_3:Number = 4;
var randomPoint_3:int = Math.random()*pointRange_3;
answerButtons_3.x = coordArray[randomPoint_3].x;
answerButtons_3.y = coordArray[randomPoint_3].y;
var pointRange_4:Number = 4;
var randomPoint_4:int = Math.random()*pointRange_4;
answerButtons_4.x = coordArray[randomPoint_4].x;
answerButtons_4.y = coordArray[randomPoint_4].y;
I have googled this profusely and I keep getting answers to do with the splice and shift methods. Is this the type of thing I need? I'm assuming I need a function that removes a point from the array after it is used.
Cheers.
Just remove the coordinate from the list when you pick it randomly using splice:
var coordinates:Vector.<Point> = new <Point>[
new Point(44, 420),
new Point(270, 420),
new Point(44, 550),
new Point(270, 550)
];
function positionAtRandomCoordinate(object:DisplayObject):void {
var index:int = Math.random() * coordinates.length;
var coordinate:Point = coordinates.splice(index, 1)[0];
object.x = coordinate.x;
object.y = coordinate.y;
}
positionAtRandomCoordinate(answerButtons);
positionAtRandomCoordinate(answerButtons_2);
positionAtRandomCoordinate(answerButtons_3);
positionAtRandomCoordinate(answerButtons_4);

How to create a series of class instances in a for loop, as3

In my library I have a bunch of classes named tip1, tip2, tip3, tip4...and so on. Is it possible to create one instance of each on the stage using a for loop? I tried this but it didn't seem to work.
var tips:int = 12;
for(var i:int = 1; i<=tips; i++){
var tipName:String = "tip"+i
var tip:MovieClip = new tipName();
tip.name = "tip" + i
tip.x = stage.width;
tip.y = 0;
addChild(tip);
}
Any help would be appreciated. Thanks!
You were missing the "getDefinitionByName" part.
// Up top
import flash.utils.getDefinitionByName;
// Down below
var tips:int = 12;
for (var i:int = 1; i < tips; ++i ) {
var myClass:Class = getDefinitionByName('tip' + i) as Class;
var tip:Object = new myClass();
tip.name = "tip" + i;
....
}
Instead of
var tip:MovieClip = new tipName();
Try (written from memory)
var clazz:Class = getDefinitionByName(tipName) as Class;
var tip:MovieClip = new clazz();
Also, you generally want to use stage.stageWidth instead of stage.width, since the latter will return the stage bounding box width (which might not be the same as the area the swf file covers).

AS3 removing dynamically created child movieclips

I'm fairly new to AS3. Anyways, I'm try to remove a dynamically created child movieclip when clicked on. When a dirt block is clicked on, which is a child movieclip of 'world' I want to remove it.
I've tried various ways of removing it using removeChild. I've also tried moving the function inside/outside of the for loop that creates the movieclips.
var blockCount:Number = 0;
var blockArray:Array = [];
var world:MovieClip = new World();
world.x = 50;
world.y = 50;
world.name = "world";
addChild(world);
for(var i:Number=1;i<=100;i++){
blockCount++;
var tempGrassBlock:MovieClip = new GrassBlock();
tempGrassBlock.x = i*16;
tempGrassBlock.y = 256;
tempGrassBlock.name = "b"+blockCount;
world.addChild(tempGrassBlock);
tempGrassBlock.addEventListener(MouseEvent.CLICK, removeBlock);
function removeBlock(event:Event){
world.removeChild(getChildByName(event.target.name));
}
}
Thanks for the help.
Try this
function removeBlock(event:Event){
world.removeChild(event.currentTarget as DisplayObject);
}
No function definition should be inside a for. I changed that in your code and rewrited a little below:
var blockCount:Number = 0;
var blockArray:Array = [];
var world:MovieClip = new World();
world.x = 50;
world.y = 50;
world.name = "world";
addChild(world);
for(var i:Number=1;i<=100;i++){
blockCount++;
var tempGrassBlock:MovieClip = new GrassBlock();
tempGrassBlock.x = i*16;
tempGrassBlock.y = 256;
tempGrassBlock.name = "b"+blockCount;
world.addChild(tempGrassBlock);
tempGrassBlock.addEventListener(MouseEvent.CLICK, removeBlock);
}
function removeBlock(event:MouseEvent){
trace("Is click really working? This target name is " + event.currentTarget.name);
world.removeChild(event.currentTarget));
}

Closure problem? - passing current value of a variable

I'm trying to pass the current value of a variable when an a dynamically generated navigation 'node' is clicked. This needs to just be an integer, but it always results in the last node's value.. have tried some different methods to pass the value, a custom event listener, a setter, but I suspect it's a closure problem.. help would be appreciated ;-)
function callGrid():void {
for (var i:Number = 0; i < my_total; i++) {
var gridnode_url = my_grid[i].#gridnode;
var news_category= my_grid[i].#category;
var newstitle = my_grid[i].#newstitle;
var news_content = my_grid[i]..news_content;
var news_image = my_grid[i]..news_image;
var gridnode_loader = new Loader();
container_mc.addChild(gridnode_loader);
container_mc.mouseChildren = false;
gridnode_loader.load(new URLRequest(gridnode_url));
gridnode_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gridLoaded);
gridnode_loader.name = i;
text_container_mc = new MovieClip();
text_container_mc.x = 0;
text_container_mc.mouseEnabled = false;
var textY = text_container_mc.y = (my_gridnode_height+18)*y_counter;
addChild(text_container_mc);
var tf:TextSplash=new TextSplash(newstitle,10,0,4 );
container_mc.addChild(tf);
tf.mouseEnabled = false;
tf.height = my_gridnode_height;
text_container_mc.addChild(tf);
var text_container_mc_tween = new Tween(text_container_mc, "alpha", Strong.easeIn, 0,1,0.1, true);
gridnode_loader.x = (my_gridnode_width+5) * x_counter;
gridnode_loader.y = (my_gridnode_height+15) * y_counter;
if (x_counter+1 < columns) {
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
}
}
function gridLoaded(e:Event):void {
var i:uint;
var my_gridnode:Loader = Loader(e.target.loader);
container_mc.addChild(my_gridnode);
_xmlnewstarget = my_gridnode.name;
//||||||||||||||||||||||||||||||||||||||||
//when a particular grid node is clicked I need to send the current _xmlnewstarget value to the LoadNewsContent function...
//||||||||||||| ||||||||||||||||||||||||
my_tweens[Number(my_gridnode.name)]=new Tween(my_gridnode, "alpha", Strong.easeIn, 0,1,0.1, true);
my_gridnode.contentLoaderInfo.removeEventListener(Event.COMPLETE, gridLoaded);
my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
}
function loadNewsContent(e:MouseEvent):void {
createNewsContainer();
getXMLNewsTarget();
news_category = my_grid[_xmlnewstarget].#category;
var tfnews_category:TextSplash=new TextSplash(news_category,20,16,32,false,false,0xffffff );
tfnews_category.mouseEnabled = false;
newstitle = my_grid[_xmlnewstarget].#newstitle;
var tftitle:TextSplash=new TextSplash(newstitle,20,70,24,false,false,0x333333 );
news_container_mc.addChild(tftitle);
tftitle.mouseEnabled = false;
news_content = my_grid[_xmlnewstarget]..news_content;
var tfnews_content:TextSplash=new TextSplash(news_content,20,110,20,true,true,0x333333,330);
news_container_mc.addChild(tfnews_content);
tfnews_content.mouseEnabled = false;
news_image = my_grid[_xmlnewstarget].#news_image;
loadNewsImage();
addChild(tfnews_category);
addChild(tftitle);
addChild(tfnews_content);
var news_container_mc_tween = new Tween(news_container_mc, "alpha", Strong.easeIn, 0,1,0.3, true);
news_container_mc_tween.addEventListener(Event.INIT, newsContentLoaded);
}
I'm not going to try to read your code (try to work on your formatting, even if it's just indenting), but I'll provide a simplified example:
for (var i = 0; i < my_total; i++) {
var closure = function() {
// use i here
}
}
As you say, when closure is called it will contain the last value of i (which in this case would be my_total). Do this instead:
for (var i = 0; i < my_total; i++) {
(function(i) {
var closure = function() {
// use i here
}
})(i);
}
This creates another function inside the loop which "captures" the current value of i so that your closure can refer to that value.
See also How does the (function() {})() construct work and why do people use it? for further similar examples.
Umm, as mentioned above, the code is a bit dense, but I think you might have a bit of type conversion problem between string and integers, is the "last value" always 0? try making these changes and let me know how you get on.
// replace this gridnode_loader.name = i;
gridnode_loader.name = i.toString();
// explictly type this as an int
_xmlnewstarget = parseInt(my_gridnode.name);
// replace this: my_tweens[Number(my_gridnode.name)] = new Tween(......
my_tweens[parseInt(my_gridnode.name)] = new Tween();
Oh and I think it goes without saying that you should massively refactor this code block once you've got it working.
Edit: after further study I think you need this
//replace this: my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
var anonHandler:Function = function(e:MouseEvent):void
{
loadNewsContent(_xmlnewstarget);
};
my_gridnode.addEventListener(MouseEvent.CLICK, anonHandler);
Where your loadNewsContent has changed arguements from (e:MouseEvent) to (id:String)
Firstly, you do not need to call addChild for the same loader twice (once in callGrid) and then in (gridLoaded). Then you can try putting inside loadNewsContent: news_category = my_grid[int(e.target.name)].#category;instead of news_category = my_grid[_xmlnewstarget].#category; As _xmlnewstarget seems to be bigger scope, which is why it is getting updated every time a load operation completes.

Creating multiple TextInput fields in for loop

I need to loop through an array and for each element create a textfield. My problem is how to create a new identifier for each new TextInput
this is my code;
var count:Number = 0;
for (var i:String in columnsData)
{
var myTI:TextInput = new TextInput();
myTI.width = 70;
myTI.height = 25;
myTI.text = columnsData[i];
myTI.name = "myTI" + count;
addChild(myTI);
count++;
}
all this does however is overwrite the previously created TextInput field, any ideas?
Try this:
var count:uint = 0,
textInputs:Array /* of TextInputs */ = [];
for(var i:String in columnsData){
textInputs[count] = new TextInput();
// Customize textInput[count] instead of myTI
addChild(textInputs[count]);
count++;
}
Outside of this loop, you should be able to look inside the textInputs array for references to each of your new TextInputs. Note that, inside the loop, you probably want to change the x/y coordinates for each TextInput so they don't overlap.