Dynamic Y Position of TextField through iterations of a loop - actionscript-3

I have a function called dataLoaded that has multiple loops through a nested array full of ingredient items. The aim is to insert each item from the array into a TextField which forms part of a scrolling list. My array structure is like so:
public var so:SharedObject = SharedObject.getLocal("mySharedObject");
public var meat_items_array:Array = new Array(so.data.meat1, so.data.meat2, so.data.meat3);
public var carb_items_array:Array = new Array(so.data.carbs1, so.data.carbs2, so.data.carbs3);
public var other_items_array:Array = new Array(so.data.other1, so.data.other2, so.data.other3);
public var mealtype_items_array:Array = new Array(so.data.mealtype1, so.data.mealtype2, so.data.mealtype3);
public var cuisine_items_array:Array = new Array(so.data.cuisine1, so.data.cuisine2, so.data.cuisine3);
public var veg_items_array:Array = new Array(so.data.veg1, so.data.veg2, so.data.veg3);
public var master_array:Array = new Array(meat_items_array, carb_items_array, other_items_array, mealtype_items_array, cuisine_items_array, veg_items_array);
Here is my dataLoaded function:
private function dataLoaded():void
{
for ( var masterCounter:int = 0; masterCounter < master_array.length; masterCounter++ )
{
for (var nestedCounter:int=0;nestedCounter<master_array[masterCounter].length;nestedCounter++)
{
var txt:String = master_array[masterCounter][nestedCounter];
if (txt==null) continue;
populateItem(txt,nestedCounter);
}
}
}
Originally all of the aesthetic positioning of items in the scrolling list below were done within the dataLoaded function but I have now decided to move all of these things into a new function called populateItems which is called during every iteration of the inner for loop in dataLoaded.
This all works well, except for the fact that the line _item.y = nestedCount * _itemPosition; no longer works as it should (originally items would be placed sequentially in a list, with the y value being calculated by multiplying a constant (_itemPosition) by the value of the counter for each iteration of the inner for loop). Now, 3 items are inputted into the list at once, and in the next iteration of the loop, another 3 items are placed right on top of the previous 3 items, obscuring them.
How do I change my below function so that my _itemPosition is more dynamic through the iterations of the dataLoaded function?
function populateItem(txt:String, nestedCount:Number):void {
_item = new Item();
_item.item_btn_over.alpha = 0;
_itemTextField = new TextField();
_itemTextField.x = _textFieldXPosition;
_itemTextField.y = _textFieldYPosition
_itemTextField.text = txt;
//adds textfield to displaylist//
_item.addChild(_itemTextField);
//vertical positioning//
_item.y = nestedCount * _itemPosition; // ????
//adds items to container displaylist//
_container.addChild(_item);
}

I'm not entirely clear as to what your trying to do, but assuming this code only runs once, this should do what you want:
_item.y = _container.height + padding; //this will always put the next item at the bottom of the container after the previous one (padding can be any number you want)
Here is how this works, let's say you have 3 objects that you are sequentially adding to container in a loop and you're giving each item the y value of container.height. Let's also assume each item has a height of 10:
Loop 1:
Container height is 0 (since there are presently no items in it)
So item1's y is 0
after container.addChild(item1) container
height becomes 10 (the height of item1)
Loop 2:
item2's y is set to container.height, which is presently 10
after container.addChild(item2) container height becomes 20
Loop 3:
item3's y is set to container.height, which is presently 20
after container.addChild(item3) container height becomes 30
If your code is being run multiple times (so _container already has objects in it), then simply run a function like this whenever you need to rearrange:
function orderItem():void {
var padding:int = 5; //5 pixel gap between items
var tmpY:Number = 0;
var tmpItem:DisplayObject;
for(var i:int=0;i<_container.numChildren;i++){
tmpItem = _container.getChildAt(i);
tmpItem.y = tmpY;
tmpY += tmpItem.height + padding;
}
}
This goes through every child of _container and stacks the items vertically.
As an aside, it would be much cleaner to take all this:
_item.item_btn_over.alpha = 0;
_itemTextField = new TextField();
_itemTextField.x = _textFieldXPosition;
_itemTextField.y = _textFieldYPosition
_itemTextField.text = txt;
//adds textfield to displaylist//
_item.addChild(_itemTextField);
and put it in the constructor of Item, then pass in txt as a constructor argument

Related

Flash drop and catch game

I am doing a flash mini game that a character need to catch item drop down from top.
The problems i am facing are using timer for those 6 items in an array randomly chose and drop down with random x-axis.
var PointFood:Array = [Melon1,Honey1,Worm1,Clock1,Poison1,Sling1];
//this is the array of items to drop
time = new Timer(60);
//No idea the timer is with frame or second
time.addEventListener(TimerEvent.TIMER,GenerateItem);
time.start();
function GenerateItem(event:TimerEvent):void
{
axis = Math.round(Math.random()*709.7)+44.45;
//this is the random x axis with my scene width
PointFood[i].x = axis;
PointFood[i].y = 0;
if(PointFood[i].y < 600.95)
{
PointFood[i].y += 10;
}
}
//here assign the x and y axis
function ItemType(e:Event):Number
{
i = Math.round(Math.random()*5);
return i;
}
//this is the random for item in array
However the x-axis will be keep changing once the calculation done. Even the items that already exist on screen, their x-axis will also keep changing with the calculation.
Any Solution for this?
There are different ways of dealing with this.
I assume Melon1, Honey1, ... are instances of your own classes.
Add a public property positioned to each of them, like:
public var positioned:Boolean=false;
At the moment your ItemType function just returns a random integer based on the number of objects inside the PointFood array.
Let's integrate a check if the random object is already positioned using it's freshly added positioned property:
function ItemType(e:Event):Number
{
var found:Boolean;
do
{
found = true;
i = Math.round(Math.random() * 5);
if (PointFood[i].positioned)
{
found = false;
}
} while (!found);
return i;
}
Finally modify the GenerateItem function to take the positioned property into account - thus just randomize the position if it's false:
function GenerateItem(event:TimerEvent):void
{
if (PointFood[i].positioned == false)
{
axis = Math.round(Math.random() * 709.7) + 44.45;
//this is the random x axis with my scene width
PointFood[i].positioned = true;
PointFood[i].x = axis;
PointFood[i].y = 0;
}
if (PointFood[i].y < 600.95)
{
PointFood[i].y += 10;
}
}
As a side note:
time = new Timer(60);
means your timer fires every 60 milliseconds - is that the expected behaviour?
Also there might be a little logic problem with your code. As the name GenerateItem implies, I guess this function should just spawn a new object and initialize it's position. Unfortunately it looks like you're abusing this function to do your main game loop as well. I'd recommend splitting this up into two separate functions.

How to Add objects without them hitting each other

Im doing a test/game and i have bumped in a problem I cant figure out.
I am trying to add Objects (in my case Bricks) to the stage ,but adding in such a way that they never hit one other, and when there is`t any space left on the stage ,to stop adding them and to display lets say "no more space".
The stage is 500x500px and the "block" is 75px to 30px ,but I need this to be able to do the same with other objects with different width and height.
I would be very thankful to an solution for this.:)
The creating of the blocks is done in an AS.
There is a movieClip exported for AS with the name Block
package {
import flash.display.MovieClip;
import flash.events.Event;
public class MainClass extends MovieClip {
private var _blockTime:Number = 0;
private var _blockLimit:Number = 20;
private var Number_:int =0;
private var _blockHolder:MovieClip = new MovieClip();
public function MainClass() {
addEventListener(Event.ENTER_FRAME ,onEveryFrame);
this.addChild(_blockHolder)
}
private function onEveryFrame(ev:Event):void{
makeBlocks();
}
private function makeBlocks():void{
_blockTime++;
if(_blockTime >= _blockLimit){
var _block:Block = new Block();
_block.y = Blocks_YX_Positioning()
_block.x = Blocks_YX_Positioning()
_blockHolder.addChild(_block);
_blockTime = 0;
Number_++
}
}
//code so the block is staing on the stage
private function Blocks_YX_Positioning():int{
var _block_YX:int = Math.random()*500
if (_block_YX < 0 ) {
_block_YX = 50;
}
if (_block_YX > 450 ) {
_block_YX = 450;
}
return _block_YX;
}
}
}
It's as simple as implementing a 2 dimensional Array representing a grid. You don't need any collision detection.
This i how it goes. first you create an Array that represents the grid.
var arrayGrid = [];
// define number of elements in grid
var columns = Math.floor(500 / 70);
var rows = Math.floor(500 / 30);
// create the references: 1,2,3,4 etc.
for( var i = 0; i < columns * rows; i++ ) {
array[i] = i;
}
As you can see, we are putting a number in every element of the array for every element on the imaginary grid. If the grid was 5 x 5, then it would have 25 elements.
Then you make a function like putBrick that puts your bricks in the stage randomly using a number from the array. I'm using 12 as the random number. At the end of the function remove the element of the array we used as a reference. At the start of the function check if the array is empty.
if( arrayGrid.length > 0 ){
var someRandomNumber = 12;
var currentCol = someRandomNumber % columns;
var currentRow = Math.floor(someRandomNumber / columns);
brick.x = currentCol * brick.width;
brick.y = currentRow * brick.height;
array.splice(someRandomNumber,1);
} else {
trace("we are done here!");
}
You will have to tweak a few things, and find the random number, but that's the core logic.

Applying an event listener to all instances on the stage of an item, and deleting the specific item

I have a list of items in a scrolling list, I would like the user to click on a specific item to delete it - I need to apply an eventListener to all instances of that item, so that whatever item is clicked, it is deleted
This is the first function:
private function dataLoaded(event:Event):void {
// this holds the loaded xml data //
data = new XML(event.target.data);
//items properties call - add other calls to master properties later on//
items = data.item;
// parsing of each ingredient//
for (var i = 0; i < items.length(); i++) {
// instantiation of mcItem (the stage for each item)
_item = new Item();
// sets //over// layer to invisible / transparent //
_item.item_btn_over.alpha = 0;
// creates the var itemTextField //
_itemTextField = new TextField();
// _itemTextField visual attributes //
_itemTextField.x = _textFieldXPosition + _textFieldPaddingLeft;
_itemTextField.y = _textFieldYPosition;
_itemTextField.selectable = true;
_itemTextField.wordWrap = true;
_itemTextField.width = _textFieldWidth;
_itemTextField.height = _textFieldHeight;
_itemTextField.embedFonts = true;
_defaultFormat.color = 0x111112;
_defaultFormat.font = _arialRounded.fontName;
_defaultFormat.size = 18;
_itemTextField.defaultTextFormat = _defaultFormat;
_itemTextField.text = items[i].toString();
//adds textfield to displaylist//
_item.addChild(_itemTextField);
//vertical positioning//
_item.y = i * _itemPosition;
_item.buttonMode = true;
_item.mouseChildren = false;
//adds items to container displaylist//
_container.addChild(_item);
}
// Input Mask//
_mask = new Shape();
_mask.graphics.beginFill(0xFF0000);
_mask.graphics.drawRect(0, 0, _maskWidth, _maskHeight);
_mask.graphics.endFill();
// Positioning of input mask//
// horizontal centering of input mask//
_mask.x = stage.stageWidth / 2 - _container.width / 2;
_mask.y = _paddingTop;
// adds the mask onto the stage//
addChild(_mask);
// assigns the above mask to the container //
_container.mask = _mask;
// Positioning of container with the mask//
// horizontal centering of container //
_container.x = stage.stageWidth / 2 - _container.width / 2;
// vertical position of container //
_container.y = _paddingTop;
//Container background stylings//
_background = new Shape();
_background.graphics.beginFill(0xFFFFFF);
_background.graphics.drawRect(0, 0, _container.width, _container.height);
_background.graphics.endFill();
_container.addChildAt(_background, 0);
//End of container background stylings//
_item.parent.addEventListener( MouseEvent.CLICK, itemClicked );
_container.addEventListener(MouseEvent.MOUSE_OVER, movingOver);
_container.addEventListener(MouseEvent.MOUSE_OUT, movingOut);
}
And here is my itemClicked function:
function itemClicked(event:MouseEvent):void {
_item.parent.removeChild(_item);
}
Unfortunately, my previous efforts have only managed to delete the last item in the list. How do I apply a listener to all instances, but in the listener function only delete the item that has been clicked on?
Your problem is that you have what I am assuming is a class member variable called _item, and as you loop through, you are reusing that variable to create your new item. So, _item is only ever a reference to the last item you created.
So when you use it in your itemClickHandler -- it ALWAYS references the last item you created.
Here's an example of what you should be doing :
// this is just example code for your dataLoaded function showing the correct concept
for (var i = 0; i < items.length(); i++)
{
var newItem:Item = new Item;
// do whatever you need to newItem
newItem.addEventListener(MouseEvent.CLICKED, itemClicked);
}
function itemClicked(event:MouseEvent):void
{
var curItem:Item = event.currentTarget as Item;
curItem.parent.removeChild(curItem);
}
Although you could do this as well :
// this is just example code for your dataLoaded function showing the correct concept
for (var i = 0; i < items.length(); i++)
{
var newItem:Item = new Item;
// do whatever you need to newItem
}
_container.addEventListener(MouseEvent.CLICKED, itemClicked);
function itemClicked(event:MouseEvent):void
{
// you could also have a check here to see if it is indeed an Item
var curItem:Item = event.target as Item;
curItem.parent.removeChild(curItem);
}
This second approach has the advantage of only 1 listener, and therefore you'll only need to remove 1 when you are done with the list.
You can access the clicked item in the event listener as evt.target
Try:
function itemClicked(event:MouseEvent):void {
if(evt.target is Item) {
var item:Item = Item(evt.target);
item.parent.removeChild(item);
}
}

as3 accessing Children from dynamicly added moiveclip

I am adding moiveClips and putting their position accordingly. I am placing all the dynamically added clips in one new movie clip and then centering that on stage. As a test, I see I can control the alpha in the main container but I can not access the children. How can I assess each flagButton separately? There would be several in this loop example below.
var flagButton:MovieClip;
var flagArray:Array = new Array;
var flagDisplayButtons:MovieClip = new MovieClip();
function displayFlagButtons()
{
for( var i = 0; flagArray.length > i; i++)
{
var flagButton:MovieClip = new roundButton();
flagButton.x = (flagButton.width + 10) * i;
flagButton.y = stage.stageHeight - flagButton.height;
addChild(flagButton);
flagDisplayButtons.addChild(flagButton);
}
addChild(flagDisplayButtons);
// Works
flagDisplayButtons.x = stage.stageWidth/2 - flagDisplayButtons.width/2;
// Does not see flagButton
flagDisplayButtons.flagButton.alpha = .5;
}
GETS ERROR:
TypeError: Error #1010: A term is undefined and has no properties.
In your example code, you have created a bunch of button instances, and one by one you have added them to the display list of flagDisplayButtons. They are on that display list in the order that you placed them.
first instance is at index 0
second instance is at index 1
third instance is at index 2
and so on.
So if you want to access the third button you added, you could do this :
var flagButton:RoundButton = flagDisplayButtons.getChildAt(2) as RoundButton;
Now that you have created variable to reference that button, you can do any of these :
flagButton.alpha = .5;
flagButton.x = 100;
flagButton.y = 200;
You need to use getChildAt or getChildByName to get the children of a DisplayObjectContainer
IE:
flagDisplayButtons.getChildAt(0).alpha = 0.5;

AS3 - removing childs in parent container doesn't reposition childs correctly

I made this plugin wich positions all child objects of a container in a grid formation. Now I added a clickhandler to the childs in the container, and when I click one of them, it is removed. When I remove a full row (from top to bottom) on the right hand side, all goes well, but when I remove a full row on the left hand side, the position of all the items in the container stay on their place but the container itself will be moved to x = 0 and y = 0. What I want is that all childs in the container are moved to x:0, y:0 as one group.
some pictures on what I get and what I want:
1) What I get:
2) What I get when I remove a full row on the left:
3) What I want:
The code I use:
private function clickHandler(event:MouseEvent):void {
var name:String = event.currentTarget.name;
if(container.getChildByName(name) != null)
container.removeChild(container.getChildByName(name));
trace(name, "container.width: ", container.width);
trace(name, "container.width: ", container.height);
trace(name, "container.x: ", container.x);
container.graphics.clear();
container.graphics.beginFill(0x2C2C2C);
container.graphics.drawRect(container.x ,container.y, container.width, container.height);
container.graphics.endFill();
}
Anyone got an idea on how to fix this? :)
EDIT: Code for creating the grid:
private function generateGrid(rows:int, cols:int, spacing:int):void
{
for (var py:int = 0; py <rows; py++)
{
for (var px:int = 0; px <cols; px++)
{
if(childs.length > 0)
{
var child:* = childs.shift();
child.x = (child.width + spacing) * px;
child.y = (child.height + spacing) * py;
} else {
break;
}
}
}
}
There's not really anything to 'fix', you just haven't written any code to move those objects, so they're not moving. You're redrawing the background of the container based on the new width, but you haven't actually moved any of the objects inside that container.
There are a lot of ways you could do this. The simplest solution that comes to mind would be something like this:
- loop through all the children of the container and find the lowest x position.
- loop through again and subtract that lowest x value from the x position of each child.
(You probably want to do this for the y position too, so they'll move up when you remove the top row.)
You would run that entire process each time a child is removed. If the lowest position is 0, meaning there is an object in the leftmost position, then nothing moves, if there is a blank row, the lowest x will be greater than 0, and everything will move over by that amount.
Disclaimer:
It would probably be 'better' to not muddle the graphical views with the data structure so much, but that's the best suggestion I can offer without seeing the rest of your code.
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Grid extends Sprite{
private var _squ:Square;
private var _cont:Sprite;
public function Grid() {
// constructor code
_cont = new Sprite();
addChild(_cont);
for( var i:uint = 0;i< 20; i++){
_squ = new Square(50,50,2,Math.random() * 0xFFFFFF);
_cont.addChild(_squ);
_squ.name = "square_"+i;
_squ.x = 100 + 52 * Math.round(i%5);
_squ.y = 50 + 52 * Math.floor(i/5);
_squ.buttonMode = true;
}
this.addEventListener(MouseEvent.CLICK, onClickAction);
}
public function gridAlign(cont:Sprite):void{
for( var i:uint = 0;i< cont.numChildren; i++){
_cont.getChildAt(i).x = 100 + 52 * Math.round(i%5);
_cont.getChildAt(i).y = 50 + 52 * Math.floor(i/5);
}
}
private function onClickAction(e:MouseEvent):void{
_cont.removeChild(_cont.getChildByName(e.target.name));
this.gridAlign(_cont);
}
}
}
try this.