Generalizing instance name in ActionScript3 - actionscript-3

Imagine there is a situation : I have 100 movieclips with instance names : MC1a, MC2a, MC3a .. MC100a
and i want all of them to be invisible is there any other way than :
MC1a.visible = false; ... MC100a.visible = false;
because in this way the code gets very heavy and i thinks it's not the right way. so i was thinking is there any possible way to be something like that :
MC*a.visible = false;
all the movieclips that contains 'MC' in the begging and 'a' and the ending to disappear ? maybe something with array ?

If the parent of all these movieclips is called container you can do :
container["MC"+i+"a"].visible=false
This is due to the script nature of ActionScript.
For your particular case you can do
for(i=0;i<100;i++){
container["MC"+i+"a"].visible=false;
}
If you don't have all number between 0 and 100 you can do something like this :
for each(MovieClip mc in container){
name=mc.name;
if(name.substring(0,2)=="MC" && name.substring(-1)=="a"){
mc.visible=false;
}
}
(This is non tested pseudocode written on the fly)

There are several ways you can achieve this. First - make a Sprite container, which will be their common parent, then alter its visibility. This is not a flexible way, for example, if your movie clips are located on two different areas of stage, and need to interact somehow, you might be unable to put them all under a single parent in your display list. The second way is to make an array out of those 100 movie clips at the time of their instantiation (if possible, of course), then you iterate through the array and assign their visibility in a loop.
Basically, if you have some objects that should form a structure, consider linking them somehow first, then altering their visibility or other parameters all together. Should you need to move them all at once, or hide, the container approach will be better. Should you need them to perform similar, but not exactly same actions (say you have monsters as movie clips, and you need them to move together, but each with their own direction and speed), you should have an array.
Another thing to consider, if there is a movie clip that has a name like "MCbig_a", that is, complies with your condition, but does not exactly belong to the group of MCs you desire to make invisible, you will have to take precautions about such occurrences.

Assuming that al children are added in the same container called myContainer
var container:MovieClip = myContainer;
var i:uint = container.numChildren;
while (i--)
{
var child:* = container.getChildAt(i);
child.visible = false;
}

Related

Make all children do something...?

I'm wondering if there is an easy way to make all children simultaneously do something. In particular, I want everything on my stage to shake back and forth like an earthquake. It's a bit hard to single out everything on stage and add in the proper code because at a given time I don't always know the amount of children on stage.
Also, I'm not sure if it makes a difference, but sometimes when I addChild something, I don't go out of my way to add it to the stage...Like for a few buttons I'll do this.addChild(mybutton). I don't know if there's a way to access everything that has been addChilded even though I may not have added them directly to the stage? I'm pretty sure numChildren returns the value of the number of objects on screen, but I could be wrong...
I was thinking of doing something like
Inside my loop
if (numChildren >= 10 && nukeShakeTimer.currentCount == 0)
{
nukeShakeTimer.start();
}
if (nukeShakeTimer.currentCount > 0)
{
NukeShake();
}
NukeShake
public function NukeShake():void
{
numberChildren = numChildren;
trace(numberChildren);
while (numberChildren > 0)
{
var tempObject:Object = getChildAt(numberChildren)
if (nukeShakeTimer.currentCount % 2 == 1)
{
tempObject.x += 10;
}
if (nukeShakeTimer.currentCount % 2 == 0)
{
tempObject.x -= 10;
}
numberChildren -= 1;
}
if (nukeShakeTimer.currentCount > 30)
{
nukeShakeTimer.reset();
}
}
When I try this though, I get a runtime error for the line var tempObject:Object = getChildAt(numberChildren) and the error reads RangeError: Error #2006: The supplied index is out of bounds.
Also I feel like it may be possible to do this operation much faster without using a while loop, but maybe not. Help please, thanks!
If you want to address every child on the stage then you'll need to loop through them. As Cherniv says, changing your getChildAt call to use numberChildren-1 should resolve your error.
It shouldn't matter whether or not everything is directly on the stage or not. Looping through the stage children will get you whatever container you added the objects to. Moving that container will move its children too. (Though you'll have to do more work if you need to move those children independently).
But...
In your specific case, it looks like you just want to shake everything on the screen together, in the same direction at the same rate. In this case, I would probably just add all my objects to a single container Sprite on the stage. Then, when you want to do a shake effect, you only need to move that one container object and everything moves together.

Creating a nicely coded level select screen

Currently developing my first full flash game, and I'm now implementing multiple levels, which means I have to make a level select screen. I have no problem with actually accomplishing this, but the only way I know how to do this would be to make a different level select screen available every time somebody passed a new level (ie, after passing level 2, now there are 2 buttons available, for level 1 and 2 instead of just level 1).
Of course, this is highly inefficient. I want to be able to do this for all my levels in one go. I already know in advance that I will be having 9 levels, so a 3x3 grid system of buttons is possible (maybe using 2 for loops, for x and y position?).
Navigating between level select and other pages is no problem, I should be able to do that. My real problem is creating the actual level select screen to display different amounts of clickable buttons depending on how far one has progressed in the game. I mean I'm guessing I just create some boolean values for the user, and once a level is passed I change those booleans to true, then link the boolean to creating a button instead of a static text field. Then would I just position each button individually on the level select screen? If somebody could shed some light ( and by light I mean code =p ) about how to accomplish making this type of level select screen, it would be greatly appreciated, particularly the part about adding the buttons to the stage vs. the textfields.
Also, is it a waste to create 9 different buttons? It would be nice to just create 1 button and then just change the text field on them to display the level number. Can I accomplish this using a dynamic text field and just altering the text in that text field?
Basically yes, you can do whatever you see fit, just prepare for all of this. This is mostly architecture question than actual programming question. About progress, you can either track the highest available level and only display those that are available, or display level progress (say one level was complete with 1 star, the other with 2 stars, and you make buttons with stars on them), etc. Also, you might look at say Angry Birds level selector, it has 15 buttons, why could you not make 9?
About how to add a custom button to stage - first, it's better if you wrap the whole level selector into a Sprite, write AS3 code for it having level buttons placed at specific coordinates, a close button (aka "no I want to review other options before selecting level" one), other data like a text field with level name, etc etc, whatever you see fit. An example (with only level buttons, mostly in pseudocode):
public class LevelSelector extends Sprite {
private static const thumbnailClasses:Array=[Level01Bitmap,Level02Bitmap,...];
// embed these
private var gridXOffset:Number=60;
private var gridYOffset:Number=60;
private var gridXDimension:int=3; // how many buttons in row
private var gridXBase:Number=30;
private var gridYBase:Number=10;
// other initialized data
private var buttons:Vector.<LevelButton>; // buttons stored here
public function LevelSelector() {
buttons=new Vector.<LevelButton>();
// other initialization
for (var i:int=0;i<thumbnailClasses.length;i++) {
var b:LevelButton=new LevelButton();
b.x=(i%gridXDimension)*gridXOffset+gridXBase;
b.y=Math.floor(i/gridXDimension)*gridYOffset+gridYBase;
b.picture=(new thumbnailClasses[i]()).bitmapData;
// assuming thumbnails are embedded bitmap class names
addChild(b);
b.enabled=levelIsAvailable(i); // query level availability
b.stars=getLevelPerformance(i); // query how many stars to draw
b.addEventListener(MouseEvent.CLICK,buttonPressed); // listen for mouse
buttons.push(b);
}
}
private function buttonPressed(e:MouseEvent):void {
var level:int=buttons.indexOf(e.target);
var b:LevelButton=e.target; // if need to do something with the button
// query correctness and navigate to level screen
}
}
It sounds like you need to break the problem down into smaller steps. I'm not sure if you're using the Flash timeline or external .as Classes. I'd recommend using Classes as you can be much more flexible about what you display on screen. If you're not used to using Classes, the following probably won't make much sense...
In your main Document.as Class you could keep track of which levels the player has completed (perhaps in an Array of Boolean values corresponding to the level numbers). When a LevelSelect page is needed, make a new LevelSelect page and pass the _levelsCompleted Array as a parameter. The passed array can be used by the LevelSelect Class instance to determine how many LevelButtons are needed. Loop through the passed Boolean array and if an index value is true, add a LevelButton. Each LevelButton could be passed its level number (the loop counter) which could be used in its TextField to identify it (remember Array indexes are zero indexed, so level '1' might actually be '0').
If this doesn't make any sense, let me know.

nested collision starling as3

I am using starling and tweenMax frameworks in my project.
The trouble I am running into is this:
For the purpose of animating along different paths, I am using tweenmax.
There is one _leaderEnemy that animates along a path and I am pushing several other _shooterEnemy (they are of the same class) into it.
public function createEnemies(enemyNo:int, path:Array, offset:int):void
{
for(var i:uint=1;i<=enemyNo;i++){
if (i==1){
_leaderCount++;
_leaderEnemy = new ShooterEnemy();
_leaderEnemy.x=600;
_leaderEnemy.y=300;
_leaderEnemy.name="_shooterEnemy"+_leaderCount;
this.addChild(_leaderEnemy);
leaderEnemyArray.push(_leaderEnemy);
}
else
{
_leaderCount++;
_shooterEnemy= new ShooterEnemy();
_shooterEnemy.x=0;
_shooterEnemy.y=(offset*(i-1));
_shooterEnemy.name="_shooterEnemy"+_leaderCount;
trace("no: "+_shooterEnemy.name);
leaderEnemyArray.push(_shooterEnemy);
_leaderEnemy.addChild(_shooterEnemy);
}
}
Now I want to check for collision using starling between each of the _leaderEnemy and the _shooterEnemy inside it with _shooterHero.
Running this array successfully checks collision with the whole group i.e. _leaderEnemy but not the individual ones inside it.
Technically, I should be able to do this just by:
var Track:Object;
for(var i:uint=0;i<leaderEnemyArray.length;i++) {
Track=leaderEnemyArray[i];
if (Track.bounds.intersects(_shooterHero.bounds)){
Track.rotation=deg2rad(70);
}
}
It may be something stupid I am doing. But I have not been able to solve this.
Any help on this would be appreciated.
Some other questions I have:
Can I check for collision with _shooterHero from inside the _shooterEnemy's class?
I have tried:
if (this.bounds.intersects(stage.getChildByName("_hero"))){
}
although it did not work.
Can I check for collision of one Enemy with everything on stage, so I can assign individual functions for his each contact?
For example: hit with hero: die; hit with another enemy: turn around
If you need more info, I will be happy to provide it.
Thank You.
Can I check for collision of one Enemy with everything on stage, so I can assign individual functions for his each contact? For example: hit with hero: die; hit with another enemy: turn around
Of course you can. You should! This is approximatively how collision engines works.
Running this array successfully checks collision with the whole group i.e. _leaderEnemy but not the individual ones inside it.
Nope. It won't work. Because the bounds property use the parent coordinates.
Meaning you can intersect each child display (of same container) with each other.
But you shouldn't intersect a child display of A with a child display of B.
Except if you convert every bounds coordinates (which are local) to global.
http://doc.starling-framework.org/core/starling/display/DisplayObject.html#localToGlobal()
Using Track.bounds.intersects is one way of checking collosions. Another would be to use hitTest method of starling.displayDisplayObject
You could also check the distance of your hero and other enemies calculated by a pythagorean theorem type calculation.
Are you checking enemies against each other? If you have many enemies you might want to look into "flocking" algorithms. Keith Petes covers this subject nicely in his book Advanced Actionscript Animation.

Masking Many Objects With 1 Mask Using `getChildAt(i).mask` Not Working

I am attempting to apply a mask to all objects on the stage except for a couple. There are a lot of different objects, and the amount of them will change in the future, so I want the masking to be done dynamically.
I wrote this code:
var i;
for (i = 0; i < this.numChildren; i++) {
if (this.getChildAt(i).name!="stage_kelp_bg" && this.getChildAt(i).name!="magnifier_mask") {
this.getChildAt(i).mask = this.magnifier_mask;
}
}
The above code is inside the document class's constructor method. Simply stating something like:
this.stage_kelp.mask = this.magnifier_mask;
works flawlessly, but only for that one object. Any idea what's wrong?
No errors are thrown, the objects just simply don't get masked.
Further research shows me that I cannot apply 1 mask to multiple objects. I have to have a mask for each object, or put all the objects into one container and mask that container.
Apparently you can use a layer to mask multiple objects on the timeline, but you can't do it programmatically without adding all the objects to one container. Unfortunately I can't do this without re-coding the entire application, so I will be using the timeline to mask things.
I would suggest you to better move all the movieclips to be masked in a single movieclip. This would be easier, if it's feasible in your case.
How about for each
for (var mc:movieClicp in this){
mc.mask=mask_}

Flex AS3 : Find and remove elements from container of certain class

How can I remove only every image found in a Bordercontainer which also holds Textinputs and Buttons ?
i tried:
for(var i:int=0;i<container.numElements;i++){
if(container.getElementAt(i) is Image){
container.removeElementAt(i);}
}
But as expected this loop does not fully work since the numElements changes which means that not all Images get removed.
I know there is some simple trick to this...but I cant think of it right now...Please help
As commenters have suggested, it seems like looping backwards would be the way to do it. I'd try something like this:
var totalElements : int = container.numElements;
for(var i:int=totalElements-1;i>=0;i--){
if(container.getElementAt(i) is Image){
container.removeElementAt(i);
}
}
By storing the numElements in a variable before starting the loop, you can be sure that the value will not change while processing the loop. Since your going backwards, you don't have to worry about the child index changing.
A second option would be to queue up the image instances in one loop and remove them in a second loop using the removeElement method. I suspect the two loop method will have significantly worse performance.
Looping backwards would be 1 way to do this.
Another would be
for(var i:int=0; i<container.numElements; i++){
if(container.getElementAt(i) is Image){
container.removeElementAt(i);
i--; //This nullifies the effect of removing an element
}
}