Dynamic amount of '.parent' - actionscript-3

I have 2 object. In different depths of MovieClip. I would like to know if object A parent is same with object B parent. I wanted to dynamically add '.parent' to object A/B till it both reaches the same level (having the same parent object). How can I do that?
My idea was to have something like
objectA = objectA + ".parent"
and make it loop till it reaches the target. But this is not the correct way to add in more layers of '.parent'. Anyone know how it should be coded?

You can use contains method
public function contains(child:DisplayObject):Boolean
Determines whether the specified display object is a child of the DisplayObjectContainer instance or the instance itself. The search includes the entire display list including this DisplayObjectContainer instance, grandchildren, great-grandchildren, and so on.
function haveCommonParent(a:DisplayObject, b:DisplayObject):Boolean
{
for(var p:DisplayObjectContainer = a.parent; p != null; p = p.parent)
{
if(p.contains(b))
return true;
}
return false;
}
Might be slow for huge display lists.
Update: get the common parent, if any. This will return a Stage object if both are on stage.
function getCommonParent(a:DisplayObject, b:DisplayObject):DisplayObjectContainer
{
for(var p:DisplayObjectContainer = a.parent; p != null; p = p.parent)
{
if(p.contains(b))
return p;
}
return null;
}

var mc:MovieClip;
while (mc.parent != null && mc.parent != targetParent)
{
mc = mc.parent;
}

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.

AS3: How to access children of children of children?

Is there a better (quicker, shorter or neater) way to access a child sprite (object) 3 layers or more deep? If the children have all been given names and you know the names. Is there a better way to do it than the code I made below?
var mySprite:DisplayObjectContainer = layerASprite.getChildByName("layerA") as DisplayObjectContainer;
mySprite = mySprite.getChildByName("layerB") as DisplayObjectContainer;
mySprite.getChildByName("layerC").y = 200;
If they are unique you can create a "global" registry in a form of static class variables/methods:
package
{
public class Registry
{
static private var hash:Object = new Object;
static public function register(name:String, target:DisplayObject):void
{
hash[name] = target;
}
static public function access(name:String):DisplayObject
{
return hash[name];
}
}
}
Usage:
// Any MovieClip, frame 1 or class constructor.
import Registry;
Registry.register("deepChild", this);
// From any other place.
import Registry;
Registry.access("deepChild").y = 200;
Alternately you can use a method that digs children of children by a single string argument:
function offSpring(path:String):DisplayObject
{
var aSplit:Array = path.split(".");
var result:DisplayObject = this;
while (aSplit.length)
{
var aParent:DisplayObjectContainer = result as DisplayObjectContainer;
if (!aParent) return null;
result = aParent.getChildByName(aSplit.shift());
}
return result;
}
Usage:
offSpring("layerA.layerB.layerC").y = 200;
As I'm not a big fan of static properties so I would also propose recursive search:
public function findChild(d:DisplayObject, n:String):DisplayObject {
var dc:DisplayObjectContainer = d as DisplayObjectContainer; if (!dc) return null;
for (var i:int = 0; i < dc.numChildren; i++){
var ch:DisplayObject = dc.getChildAt(i);
if (ch.name == n || (ch = findChild(ch, n))) return ch;
}return null;
}
and than you can simply type this:
var d:DisplayObject = findChild(stage, "childName");
to find first child with childName name anywhere on stage.
I just wrote it and tested it once, but I hope it fine.
Advantages:
You don't need to make any additional steps to work with this method. You don't even need to name containers of child you search for.
You can start search at any DisplayObjectContainer you want.
If you decide at some point that you need you need to move your child form container A to container B no change in code is needed if it's still part of the same branch and has unique name.
Disadvantages:
It could be expensive, especially if you have extensive branch.
You need to make sure name of your child is unique across searched branch.
More sophisticated version
As searched children could usually be direct child of given container or be on closer level you could possibly search for child one level at time though it's bit tricky. For example my alpha version:
/**
* Perform parallel/sprial recursive search in this container to find child with given name.
* This means that this function will first check names of all child of this container and and then go to deeper level.
* In other words, no element will be tested on level x+1 if not all elements were tested on level x.
* This is true for all levels until whole tree is exhausted. This method is using token argument for such functionallity.
* #param n name of child element to be found.
* #param t token passed internally for sipral search. You should not specify this if you want the function to search a whole tree.
* The token has only single value which is basically a desired level at which child should be searched for.
* Level 1 means this function will only check its own childrens, level 2 means that only childs of childs of this container will be checked and so one.
* However note that if you specify a token with some level, only that single level will be searched.
* On the other hand if given token is null, this will check childs on level 1, then level 2, 3... and it will countinue until deepest level has been reached.
* #return nearest child with specified name or null if no child with given name found.
*/
public function findChild(n:String, t:SearchToken = null, ind:String = ""):SGLElement {
ind += " ";
var r:Boolean = (t) ? false : true; //is this call root of search.
t = (t) ? t.offL( -1) : new SearchToken(0); //create new token if not given or decrement current token value.
//trace(ind + "F", this.name, ":", t);
//if (!t) t = new SearchToken(0);
//--t.l;
var cl:SearchToken = new SearchToken(t.l); //current search level.
var exc:int = 0; //exhausted childrens.
if(t.l == 0){//search own children
for (var i:int = 0; i < _childs.length; i++) { //trace(ind + " c", _childs[i].name);
if (_childs[i].name == n) return _childs[i]; }
if (r) ++cl.l; else return null;
}
while( cl.l > 0){
if (exc >= _childs.length) { t.l = -1; return null;}
for (i = 0; i < _childs.length; i++) {
//trace(ind + "ch", t,":", i, _childs[i].name, _childs[i]);
if (!(_childs[i] as SGLElementContainer)) continue;
//trace(ind + "t", t, i);
t.l = cl.l;
var e:SGLElement = SGLElementContainer(_childs[i]).findChild(n, t, ind);
//++cl.l;
if (e) return e;
else if (t.l < 0) exc++;
}
//trace(ind + "END_LEVEL>>>>", t);
if (!r) return null;
//t.l = cl.l;
++cl.l;
}
return null;
}
token class
package adnss.common.utils
{
public class SearchToken
{
/**Current level**/
public var l:int;
public function SearchToken(levelValue:int) {l = levelValue;}
public function toString():String {return String(l);}
/**Set level value and return this token instance.**/
public function setL(v:int):SearchToken { l = v; return this; }
/**Add given offset value to level value and return this token instance.**/
public function offL(v:int):SearchToken { l += v; return this;}
}
}
I note I don't know what is technical name for such search so I gave it my own name and this method is not used for display list so you would need to adapt it. It's bit hard to explain this but if you have some question about it feel free to ask.

How to give differents directions of bullets on one array?

im very new on javascript, i was doing a ship gaming that you have to kill some asteroids, and when you take some differents of "objects" on the screen we expand our number of bullets. Okey, going to the point i could get 3 bullets on the screen when you take diffirents objects, but now i want to give 2 of that 3 bullets of the array different directions. When i tried, i have the problem that i give the 3 bullets the same direction, i know why but im for at least 5hrs trying to fix this and i cant.
Im programming on Flash Builder 4.7 with different classes, i ll give the code of the array who is in the main, and the bullet class so as the hero class too.
Main Array
public function evUpdateBullet():void//here execute update of my class Bullets
{
var i:int;
for(i=myBullets.length-1; i >= 0; i--)
{
if(myBullets != null) //to be ?
{
if(myBullets[i].isDestroyed) //is destroyed Bullets?
{
myBullets[i] = null;
myBullets.splice(i, 1); //deleted elements.
}else
{
myBullets[i].evUpdate();
}
}
}
}
here i push the array and create the bullet, remember myBullets is the name of the array.
public function evShoot(posX:int, posY:int):void//here create the bullet and push in the array
{
attack1 = new Bullet;
attack1.Spawn(posX, posY);
myBullets.push(attack1);
}
here i show the Hero code, where i define the position of the bullet is going to spawn on the screen.
if (isPressing_Shoot && !isDestroyed)// Here execute the event shoot without power
{
Main.instace.evShoot(model.x, model.y);
isPressing_Shoot = false;
canShoot = false;
}
evDestroyed();
}
here is the code from Bullet class
first the spawn
public function Spawn(posX:int, posY:int):void
{
isDestroyed = false;//first parameter of my bullet
model = new MCbullet;
Main.layer1.addChild(model);//painting the hero in the stage
model.x = posX;//position in the stage wiht the hero
model.y = posY;
model.tigger.visible = false;
}
then the Update
public function evUpdate():void//here conect with update general
{
if (model != null)//to be?
{
model.y -= 12;//move of my bullet
//model.x -= 12;
if (model.y <= 0 )
{
evDestroyed();
}
}
}
in this update i set the movement of y, so i can shoot vertically, but.. when i try to add an x.move, i do for the all array, so i want to know how i can give different move, for differents bullets of the same array.
Iterate through the array elements. There are a few ways to do this, but the one I'm most accustomed to using would be the for loop. It looks like this:
// loop through myBullets array to update
// each x and y position dependent on unique
// property value _xSpeed and _ySpeed.
for (var i:int = 0; i < myBullets.length; i++)
{
myBullets[i].x += myBullets[i]._xSpeed;
myBullets[i].y += myBullets[i]._ySpeed;
}
Obviously, you will need the _xSpeed and _ySpeed properties of the array elements to be set to dynamic values. You would first need to give the bullet class these properties and then set their values when you instantiate the bullets. That might look something like this:
function makeBullet():void{
var b:Bullet = new Bullet();
b.x = hero.x;
b.y = hero.y;
b._xSpeed = hero._xSpeed; // or put here whatever makes sense for assigning the right value in your application
And in your bullet class constructor, before the function but inside the class brackets, add the property:
var _xSpeed:Number = 0;
var _ySpeed:Number = 0;
Basically this is allowing each bullet to hold it's own special property that is independent of any other instance of the class.
I hope that helps.

How to handle event for nonvisual objects in Flex

I am trying to perform two way binding e.g I have a button (out of many controls), on its selection, I am showing the values of its diff properties(like height, width etc) in some textinput. This one way process works fine.
But the reverse process doesn't work. i.e When I select some button, and try to change its dimension by entering some value in height, width textinputs, the dimension are not changed.
How to know which button was selected by me? How events needs to be handled here ?
private void Form1_Load(object sender, System.EventArgs e)
{
//Create some data and bind it to the grid
dt1 = GetData(1000, 3);
this.UltraGrid1.DataSource = dt1;
//Set the grid's CreationFilter to a new instance of the NumbersInRowSelectors class.
this.UltraGrid1.CreationFilter = new NumbersInRowSelectors();
}
private void UltraGrid1_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)
{
//Hide the default images that are drawn in the RowSelectors, like the pencil and asterisk, etc.
e.Layout.Override.RowSelectorAppearance.ImageAlpha = Infragistics.Win.Alpha.Transparent;
//Center the text in the RowSelectors.
e.Layout.Override.RowSelectorAppearance.TextHAlign = Infragistics.Win.HAlign.Center;
e.Layout.Override.RowSelectorAppearance.TextVAlign = Infragistics.Win.VAlign.Middle;
//There is no wy to change the width of the RowSelectors.
//Use a smaller font, so that 3-digit numbers will fit.
e.Layout.Override.RowSelectorAppearance.FontData.Name = "Small Fonts";
e.Layout.Override.RowSelectorAppearance.FontData.SizeInPoints = 6;
}
//The NumbersInRowSelectors class. This class Implements a CreationFilter and
//adds a TextUIElement to each RowSelector which displays the row number of
//the row.
public class NumbersInRowSelectors:Infragistics.Win.IUIElementCreationFilter
{
#region Implementation of IUIElementCreationFilter
public void AfterCreateChildElements(Infragistics.Win.UIElement parent)
{
//Don't need to do anything here
}
public bool BeforeCreateChildElements(Infragistics.Win.UIElement parent)
{
//Declare some variables
Infragistics.Win.TextUIElement objTextUIElement;
Infragistics.Win.UltraWinGrid.RowSelectorUIElement objRowSelectorUIElement;
Infragistics.Win.UltraWinGrid.UltraGridRow objRow;
int RowNumber;
//Check to see if the parent is a RowSelectorUIElement. If not,
//we don't need to do anything
if (parent is Infragistics.Win.UltraWinGrid.RowSelectorUIElement)
{
//Get the Row from the RowSelectorsUIElement
objRowSelectorUIElement = (Infragistics.Win.UltraWinGrid.RowSelectorUIElement)parent;
objRow = (Infragistics.Win.UltraWinGrid.UltraGridRow)objRowSelectorUIElement.GetContext(typeof(Infragistics.Win.UltraWinGrid.UltraGridRow));
//Get the Index of the Row, so we can use it as a row number.
RowNumber = objRow.Index;
//Check to see if the TextUIElement is already created. Since
//The RowSelectorsUIElement never has children by default, we
//can just check the count.
if (parent.ChildElements.Count == 0)
{
//Create a new TextUIElement and parent it to the RowSelectorUIElement
objTextUIElement = new Infragistics.Win.TextUIElement(parent, RowNumber.ToString());
parent.ChildElements.Add(objTextUIElement);
}
else
{
//There's already a TextUIElement here, so just set the Text
objTextUIElement = (Infragistics.Win.TextUIElement)parent.ChildElements[0];
objTextUIElement.Text = RowNumber.ToString();
}
//Position the TextUIElement into the RowSelectorUIElement
objTextUIElement.Rect = parent.RectInsideBorders;
//Return True let the grid know we handled this event.
//This doesn't really do anything, since the grid
//does not create any child elements for this object, anyway.
return true;
}
//Return false to let the grid know we did not handle the event.
//This doesn't really do anything, since the grid
//does not create any child elements for this object, anyway.
return false;
}
#endregion
}
}
Create a "currently selected item" member in the class where the button and the text edit are declared.
In the button selection event listener assign the event target to this member. Then use it in the text edit event listener.
For example:
// It's a declaration of the member variable
private var m_current_btn:Button = null;
// It's an event listener for your button
private function on_selection_change(event:Event):void
{
m_current_btn = event.target as Button;
// button_x and button_y are two text edits
button_x.text = m_current_button.x.toString();
button_y.text = m_current_button.y.toString();
}
// Event listener to track changes in the coordinate text inputs
private function on_coordinate_textedit_change(event:Event):void
{
if (m_current_btn != null)
{
m_current_btn.x = parseInt(button_x.text);
m_current_btn.y = parseInt(button_y.text);
}
}

I need a way out

public class NCell
{
private var _player: NPlayer;
public function set player(value: NPlayer):void
{
this._player = value;
}
public function get player():NPlayer {return this._player}
}
public class NPlayer
{
private var _cell: NCell;
public function set cell(value: NCell):void
{
if (this._cell != null)
{
this._cell.player = null;
}
this._cell = value;
if (this._cell != null)
{
this._cell.player = this;
this.position = this.cell.position;
}
}
}
So, I have a field of NCells (size 20x15) and several players on it. To move player I write
player.cell = field[4][4];
After it player knows his cell (and position) and cell has a link to player. Thus I have everything to calculate available moves. Sometimes there are situations when player has "cell", but "player" in the cell == null. It's bad. It becomes so because when I calculation moves I store players position then move players and then continue searching and when game points become 0 I restore players positions and continue searching. E.g. I have c1 == field[3][4], c2 == field[4][4], c3 == field[5][4], p1.cell == c1, p2.cell == c2. p2 moves to c3 and then p1 moves to c1. Then I restore position:
//c2.player == p1
p2.cell = c2;//now c2.player == c2 but p1.cell == c2
p1.cell = c1;//and here c2.player == null
and it doesn't depends of restoring sequence. How to avoid link wiping?
Well, I am not so sure the way you store your data is the best. If you really want this kind of linkage, you may want to consider making a cell capable of holding multiple players. This would resolve your current problem and would be more flexible, later on.
Basically, you problem can be solved like this:
public class NPlayer
{
private var _cell: NCell;
public function set cell(value: NCell):void
{
if (this._cell != null && this._cell.player == this) //only the owner may remove a link!
{
this._cell.player = null;
}
this._cell = value;
if (this._cell != null)
{
this._cell.player = this;
//this.position = this.cell.position; <-- no need for that, see below
}
}
}
Unless you access NPlayer::position a gazillion times per frame, there is no point in storing it. This only introduces a potential source of errors. The property should be calculated, by simply returning this.cell.position or a meaningful default value in case the player has no cell (or raising an exception, if that is not supposed to happen).
greetz
back2dos