Changing Child Index of MovieClips based on their Y Position? - arrays

I'm writing a game with Flash CS5/AS 3.0 that tries to simulate depth of field by drawing all the relevant Movie Clips based on their Y position in ascending order, i.e. things lower on the stage overlap things higher on the stage. A MovieClip with a Y position of 10 would therefore need to have a lower index compared to a MovieClip with a Y position of 20, so the second one gets drawn on top of the first.
I wrote a quick and dirty function just to test this. During the trace, I've noticed that the truck's index hits 0 when I go near the top of the stage, but if I go too far up it will completely disappear from the stage. Trace then starts generating this error:
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/getChildIndex()
at EICT::Game/ReorganizeDisplayIndexes()
at EICT::Game/loop()
theTruck is a MovieClip of the player controlled vehicle
Enemies, Cones, Rocks are all arrays which contain MovieClips
None of them have event listeners.
private function ReorganizeDisplayIndexes(): void
{
var drawableObjects:Array = new Array();
drawableObjects.push(theTruck);
drawableObjects = drawableObjects.concat(Enemies, Rocks, Bushes);
drawableObjects.sortOn("y", Array.DESCENDING | Array.NUMERIC);
drawableObjects.reverse();
trace(collisionLayer.getChildIndex(theTruck));
for (var a:int = collisionLayer.numChildren - 1; a >= 0; a--)
{
collisionLayer.removeChildAt(a);
}
for (var i:int = 0; i < drawableObjects.length; i++)
{
collisionLayer.addChild(drawableObjects[i]);
}
}

Hint: You don't need to remove the child first. When you use addChild() on an object, it is automatically re-added to the next highest depth.
That said, you'll just need to do something like this:
drawableObjects.sortOn("y");
for each(var i:DisplayObject in drawableObjects)
{
if(i.parent)
i.parent.addChild(i);
}

Use setChildIndex instead of removing and re-adding:
for (var a:int = collisionLayer.numChildren - 1; i >= 0; i--)
{
collisionLayer.setChildIndex(drawableObjects[i], i);
}
Also, it's a bit wasteful to first order the sort in descending and then reversing the array. Sort it ascending to begin with!

Related

as3 array of objects - movement with constant speed

Ok, so I have some experience with as3 and some of the basics. But this problem has been stumping me for so long. I tried to do a workaround based on what I currently know about as3. But somehow either i get an error message or it just doesn't do anything at all. Here is the code that i'm trying to solve.
var zombieCount:Array = new Array();
var helltime:Timer = new Timer(1500);
helltime.addEventListener(TimerEvent.TIMER, spawnzombies)
helltime.start();
function spawnzombies(happened:TimerEvent){
var zombie1:Zombie = new Zombie();
zombieCount.push(zombie1);
stage.addChild(zombieCount[zombieCount.length - 1]);
zombie1.x = 135 + (330*Math.random())
zombie1.y = -29
stage.addEventListener(Event.ENTER_FRAME, move_zombie)
function move_zombie(happened:Event){
for(var i:int; i < zombieCount.length; i++){
zombieCount[i].y = zombieCount[i].y + 1;
if(zombieCount[i].hitTestObject(border)){
stage.removeChild(zombieCount[i]);
zombieCount.shift();
trace(zombieCount.length);
}
}
}
}
While this may not be inclusive of everything wrong, here are at least a few of the issues I see.
Inline function issue:
Inside your timer tick handler (spawnZombies), you create an inline function called move_zombie and then add an enter frame handler that calls that function.
The issue here, is that every tick of the timer, will then create a whole new copy of that function, and add ANOTHER enter frame handler. This will create huge problems after a few timer ticks.
Break that move_zombie function OUT OF the spawn function:
eg:
helltime.addEventListener(TimerEvent.TIMER, spawnzombies)
helltime.start();
stage.addEventListener(Event.ENTER_FRAME, move_zombie);
function move_zombie(......
function spawnzombies(.....
Iteration issue:
In your for loop:
for(var i:int; i < zombieCount.length; i++){
zombieCount[i].y = zombieCount[i].y + 1;
if(zombieCount[i].hitTestObject(border)){
stage.removeChild(zombieCount[i]);
zombieCount.shift();
trace(zombieCount.length);
}
}
You are not initializing your i value. While this will default it to 0, it's still a good idea for readability to initialize it.
So your iterating forward from 0 to the end of the array. However, if your hit test succeeds, you then use the shift method of the array. This removes the first item of the array (irrespective of what value i is at the time). This will remove the wrong item, plus mess up what zombieCount[i] refers to (because the amount of items has now changed after doing shift, so the next iteration zombieCount[i] will be a reference to same item as the previous iteration).
Instead of what you're currently doing, use the splice method to remove, and iterate backwards so your index doesn't get out of whack.
for(var i:int=zombieCount.length-1;i >=0;i--){
zombieCount[i].y += 1; //move it down 1 pixel
if(zombieCount[i].hitTestObject(border)){
stage.removeChild(zombieCount[i]);
zombieCount.splice(i,1); //remove the item at the current index (do this instead of shift)
trace(zombieCount.length);
}
}

Array not resetting in Game

Hey guys so i am developing a Game and i have an Array that keeps track of my 5 movie clips that are added to the stage by the array. I have a Player movie clip as well so the Player movie clip is Hittesting with all the 5 diamond movie clips which are added to the stage like so:
private var nPoints:Number = 5;..........
public function addPointsToStage():void
{
for (var i = 0; i < nPoints; i++)
{
trace(aPointsArray.length);
points = new mcGainPoints();
stage.addChild(points);
points.x = startPoint.x + (xSpacing * i);
points.y = startPoint.y - (ySpacing * i);
aPointsArray.push(points);
}
}
Now when the Player comes in contact with all the 5 movie clips everything works fine and the output for the array looks like this:
0
1
2
3
4
Then it continues to the next level.
But say that my Player doesnt hit any of the 5 movie clips or only hits a couple of them, when the next level is started the output looks like this:
5
6
7
8
9
and when the next level is started weird things start happening like points start adding to the highscore by themselves.
I think the problem is that im not destroying the array holding the 5 movie clips correctly this is the code i use to start the next level and destroy the array:
if(player.hitTestObject(mcGoal_1))
{
//Remove all points from array/stage
for (var i:int = 0; i < aPointsArray.length; i++)
{
if (aPointsArray[i].parent)
{
parent.removeChild(aPointsArray[i]);
}
startNextLevel();
}
The mcGoal_1 is the object of the game so if the player hits the goal_1 then destroy all array objects on screen and start next level. This is the startnextlevel function.
private function startNextLevel():void
{
//add points to next stage
addPointsToStage();
}
So can you see why when the next level starts the array isnt reset back to 01234? I think thats why the game has that bug of randomly adding points. Please any help will be appreciated
//Remove all points from array/stage
for (var i:int = 0; i < aPointsArray.length; i++)
if (aPointsArray[i].parent)
parent.removeChild(aPointsArray[i]);
// just one statement, no braces
aPointsArray.length=0; // clear the array itself, not just
// points from display list!
startNextLevel(); // now start level
An incorrect order of correct statements leads to a disaster. Always check your code flow, what executes now and what then, and how many times.
It doesn't look like you're removing anything from the array.
You can use a combination of .indexOf() and .splice() to remove items from an array:
function removeMc(clip:MovieClip):void
{
if(clip.parent) clip.parent.removeChild(clip);
// Also remove from array.
var index:int = aPointsArray.indexOf(clip);
aPointsArray.splice(index, 1);
}
It might also be worth simply emptying the array when you startNextLevel():
private function startNextLevel():void
{
aPointsArray.length = 0;
//add points to next stage
addPointsToStage();
}
Just assign your array to null value. so that array will start from begining whenever you require. i hope the following code code would helps you.
var nPoints:Number = 5;
var points:poin;
var aPointsArray:Array;
var startPoint:Number = 0;
var xSpacing:Number = 20;
var ySpacing:Number = 20;
addPointsToStage();
function addPointsToStage():void
{
aPointsArray = null;
aPointsArray = new Array();
for (var i:int = 0; i < 6; i++)
{
trace(aPointsArray.length);
points = new poin();
addChild(points);
points.x = 100+ (xSpacing * i);
points.y = 200 - (ySpacing * i);
aPointsArray.push(points);
}
}

hitTestObject not hitTesting with all MovieClips

Hey guys having a little trouble this might be easier than i am making it out to be.
But the problem that i am having is when i hittest my mcPoints with my mcPlayer it is only interacting with 4 out of 5 of the movie clips that are added to the stage by a for loop.
I have been struggling with this for the past two days and cant seem to pin point the problem, everything seems set up perfectly but maybe you can help.
These are my Variables:
public var mcPoints:smallGainPoints;
private var nPoints:Number = 5;
private var aPointsArray:Array;
Here is how i set up the 5 mcPoints Movie Clips to be added to stage:
private function addPointsToStage():void
{
var startPoint:Point = new Point((stage.stageWidth / 2) - 100, stage.stageHeight / 2);
var spacing:Number = 50;
for (var i = 0; i < nPoints; i++)
{
trace(aPointsArray.length);
mcPoints = new smallGainPoints();
aPointsArray.push(mcPoints);
stage.addChild(mcPoints);
mcPoints.x = startPoint.x + (spacing * i);
mcPoints.y = startPoint.y;
}
}
So that adds the 5 points movie Clips to the stage which are aligned horizontally.
And finally here is the loop that listens for the HitTestObject to Initiate:
private function checkPlayerHitPoints():void
{
for (var i:int = 0; i < aPointsArray.length; i++)
{
//get current point in i loop
var currentPoints:smallGainPoints = aPointsArray[i];
//test if player is hitting current point
if(player.hitTestObject(currentPoints))
{
//Add points sound effects
var pointsSound:Sound = new pointsPickUpSound();
pointsSound.play();
//remove point on stage
currentPoints.destroyPoints()
//remove points from array
aPointsArray.splice(i, 1);
trace(aPointsArray.length);
//Add plus 5 text to current points position
mcPlus5 = new plusFiveText();
stage.addChild(mcPlus5);
mcPlus5.x = (currentPoints.x);
mcPlus5.y = (currentPoints.y);
//Update high score text
nScore += 5;
updateHighScore();
}
}
}
So i added traces both for when the movie clips are added and when they are hit here are the values i get:
0
1
2
3
4
Hit: 4
Hit: 3
Hit: 2
Hit: 1
Also i call the addPointsToStage(); in my constructor for more information.
So from the values im getting it seems that the last value "0" isn't being interacted with, any ideas why? Please anything would be of use. Thanks so much!
i'm not exactly sure what your code is suppose to be doing. But when you remove element from array in loop you are lose one item.
You array is
[1][2][3][4][5]
When i=1 you remove element and get
[1][3][4][5]
next interation i=2 which means you never test agains value 3.
You should decrement i whenever you remove element from an array.

Is it possible to reload or reset an array in as3?

I'm wondering if it is possible to reset / reload / reconstruct an array order?
I'm making this "Space Invaders" game and the enemy's need to restart to it's position when the game is being restarted. When I shoot down the enemy's and reset my game, the enemy's I've killed keep being gone.
So here's some of the code responsible:
var spiderArray:Array = new Array(enemyField.enemy1,enemyField.enemy2,
enemyField.enemy3,enemyField.enemy4,
enemyField.enemy5,enemyField.enemy6,
enemyField.enemy7,enemyField.enemy8,
enemyField.enemy9,enemyField.enemy10,
enemyField.enemy11,enemyField.enemy12,
enemyField.enemy13,enemyField.enemy14,
enemyField.enemy15,enemyField.enemy16,
enemyField.enemy17,enemyField.enemy18,
enemyField.enemy19,enemyField.enemy20,
enemyField.enemy21,enemyField.enemy22,
enemyField.enemy23,enemyField.enemy24,
enemyField.enemy25,enemyField.enemy26,
enemyField.enemy27,enemyField.enemy28,
enemyField.enemy29,enemyField.enemy30,
enemyField.enemy31,enemyField.enemy32,
enemyField.enemy33,enemyField.enemy34,
enemyField.enemy35,enemyField.enemy36,
enemyField.enemy37,enemyField.enemy38,
enemyField.enemy39,enemyField.enemy40,
enemyField.enemy41,enemyField.enemy42,
enemyField.enemy43,enemyField.enemy44,
enemyField.enemy45,enemyField.enemy46,
enemyField.enemy47,enemyField.enemy48,
enemyField.enemy49,enemyField.enemy50,
enemyField.enemy51,enemyField.enemy52,
enemyField.enemy53,enemyField.enemy54,
enemyField.enemy55,enemyField.enemy56,
enemyField.enemy57,enemyField.enemy58,
enemyField.enemy59,enemyField.enemy60,
enemyField.enemy61,enemyField.enemy62,
enemyField.enemy63,enemyField.enemy64,
enemyField.enemy65,enemyField.enemy66);
Now the place where the enemy's are being killed:
function enemyHitTest():void {
//for each of the three spiders
for(var i:int = 0; i < spiderArray.length; i++) {
//the each of the six bullets
for(var j:int = 0; j < 6; j++) {
//don't consider bullets that aren't in play:
if(bulletArray[j].y > SpelerMC.y) continue;
if(spiderArray[i].hitTestObject(bulletArray[j])) {
score += 10;
scoreTxt.text = score.toString();
trace("Invader " + i + " neergeschoten!");
spiderArray[i].parent.removeChild(spiderArray[i]);
bulletArray[j].x = j * 70 + 100;
bulletArray[j].y = 595;
}
}
}
Now I think I need to put some sort of theArray.pop(); or something, but don't know how to use it, but I need to place it in this function:
function startGame() {
trace("Start het spel opnieuw...");
gameTimer.addEventListener(TimerEvent.TIMER, onTick);
gameTimer.start();
enemyField.x = 400;
enemyField.y = 160;
SpelerMC.x = 83;
SpelerMC.y = 531;
}
Please help me! Have been searching for 5 hours already. Thanks in advance!
to remove element number i use array.splice(i, 1); ( http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#splice().com/en_US/FlashPlatform/reference/actionscript/3/Array.html#splice%28%29) but keep in mind that array.length will decrease
to reset the array just invoke spidersArray = new Array(enemyField.enemy1, etc) again
UPDATE
in in the enemyHitTest function i changed the removing condition to if(enemyArray[i].visible && enemyArray[i].hitTestObject(laserArray[j])) and enemyArray[i].parent.removeChild(enemyArray[i]); to enemyArray[i].visible = false;
and added
function respawnEnemies():void{
for(var i:int = 0; i < enemyArray.length; i++) {
enemyArray[i].visible = true;
}
}
to call it from startGame
full code here
upd 2
so the problem was not in resetting the array but in the fact that your enemyField and its' enemies were added to stage manually and removed programmatically so there was no code to call to bring them back
if all your instances should be preserved - e.g. you generate all actors at the beginning and later all of them are reused (at the new game) you can set an array of them and on each start game make a copy and remove "dead" enemies from the copied array
also better option is to use a vector e.g.
var enemies:Vector.<Enemy> = Vector.<Enemy>([]);//in brackets references to the instances of Enemy class
var inGame:Vector.<Enemy> = enemies.concat();
also 5hrs of research? please be patient and try harder,
best regards

Actionscript 3.0 Get all instances of a class?

I got a ton of movieclips in a class. Is there a more efficient way to apply a function to every instance in the class other than this?
var textArray:Array = [
interludes.interludeIntro.interludeBegin1,
interludes.interludeIntro.interludeBegin2,
interludes.interludeIntro.interludeBegin3,
interludes.interludeIntro.interludeBegin4,
interludes.interludeIntro.interludeBegin5,
interludes.interludeIntro.interludeBegin6,
interludes.interludeIntro.interludeBegin7,
//... ... ...
interludes.interludeIntro.interludeBegin15
];
for each (var interludeText:MovieClip in interludeBeginText)
{
interludeText.alpha = 0 //clear all text first
}
Also for some reason this doesn't work:
interludes.interludeIntro.alpha = 0;
It permanently turns that class invisible, even if I try to make specific instances visible later with:
interludes.interludeIntro.interludeBegin1.alpha = 1;
I have NO idea why the above doesn't work. I want to turn every single instance in the class interludeIntro invisible, but I want to turn specific instances visible later.
(btw I have no idea how to insert code on this website, pressing "code" doesn't do anything, so pardon the bad formatting)
I'm not really sure what you're asking, but what may be useful is that, in ActionScript you can refer to properties by name, like myObject["someProperty"].
Using that, you can iterate over properties if they follow some naming scheme, so for example:
for (var i:int = 1; i <= 15; i ++)
interludes.interludeIntro["interludeBegin" + i].alpha = 0;
That iterates over interludes.interludeIntro.interludeBegin1 through ...15 and sets their alpha properties to 0.
When you do that:
interludes.interludeIntro.alpha = 0;
you turn the movie clip and all its children invisible.
So later when you do that:
interludes.interludeIntro.interludeBegin1.alpha = 1;
You make the movie clip visible, but since its parent is still invisible, you don't see anything. The solution is to loop through the movie clips and make each of them invisible/visible.
// Keep the parent visible at all time
interludes.interludeIntro.alpha = 1;
for (var i:int = 0; i < textArray.length; i++) {
textArray[i].alpha = 0;
}
// Now this will work:
interludes.interludeIntro.interludeBegin1.alpha = 1;

Resources