ActionScritp 3 removeChild in loop - actionscript-3

I have two arrays, fireballs and gombas (enemies).
When I create fireball and gomba, I add them into arrays using push();
I shoot fireballs and move gombas using foor loop
for (var f:int = 0; f < fireballs.length; f++)
{
// move fireballs
}
I also remove fireballs if it goes too far, for example
if (myFireball.x + 1000 < player.x)
{
if (myFireball.parent)
{
myFireball.parent.removeChild(myFireball);
fireballs.splice(myFireball, 1);
}
}
I have no problem removing fireballs, but if fireball hitTestObject gomba, I want to remove both, fireball and gomba, ant thats my problem.
I tried on this way and I get error, a term is undefined and has no properties
for (i = 0; i < fireballs.length; i++)
{
for (var m = 0; m < gombas.length; m++)
{
if (fireballs[i].hitTestObject(gombas[m]))
{
if (fireballs[i].parent)
{
fireballs[i].parent.removeChild(fireballs[i]);
// same for gombas
}
}
}
}
If I use same loop but just check if fireballs and gombas are visible, if fireballs hit gombas I set visible to false and it works ok. Why it wont work with removeChild.

As #itcouldevenbeaboat says, when looping through an array and removing objects, it's better to loop backwards; that way, you won't have index issues.
For example, with the array ["one","two","three"], if you looped through and removed like so:
for( var i:int = 0; i < myArray.length; i++ )
myArray.splice( i, 1 );
What's happening, is this:
i starts at 0, which points to "one"
we remove the object at position 0 from myArray
myArray is now ["two","three"]
at the end of the loop, i is incremented, and becomes 1
we start the next iteraction, and i is 1, which points to "three"
In this example, we've skipped over the "two" String, because the splice has modified the array we're iterating. If we went backwards:
for( var i:int = myArray.length - 1; i >= 0; i-- )
myArray.splice( i, 1 );
Then we get this:
i starts at 2, which points to "three"
we remove the object at position 2 from myArray
myArray is now ["one","two"]
at the end of the loop, i is decremented, and becomes 1
we start the next iteraction, and i is 1, which points to "two"
Thus we remove all the objects from myArray, no problem.
For your particular example, as you're removing objects from the two arrays, you should iterate backwards, which should give you something like:
outer: for ( var i:int = fireballs.length - 1; i >= 0; i-- )
{
for ( var m:int = gombas.length - 1; m >= 0; m-- )
{
if (fireballs[i].hitTestObject(gombas[m]))
{
// remove our fireball and gombas graphics
if( fireballs[i].parent != null )
fireballs[i].parent.removeChild( fireballs[i] );
if( gombas[m].parent != null )
gombas[m].parent.removeChild( gombas[i] );
// remove the fireball and gombas from the array; this is assuming
// that once a fireball hits a gomba, they both disappear
gombas.splice( m, 1 );
fireballs.splice( i, 1 );
// break to the outer loop; the fireballs one, as we don't need
// to go any further in the inner one
break outer;
}
}
}
Note the outer label on the fireballs loop; this lets us jump out once we detect a collision between a fireball and a gomba.
If you want a fireball to hit multiple gombas, then just don't remove the graphics or splice the array.

Related

How to remove element from array A that are also in array B in Actionscript 3.0?

Suppose I have array A and array B which contains object. How do I "clean" elements from array A that are also exist in B. That is, I need to remove all element in A that are also in B.
From my understanding, you're trying to remove all similar items from array A that are present in B?
for (var i:uint = 0; i < arrayB.length; i++) {
arrayA.splice(arrayA.indexOf(arrayB[i]), 1);
}
Do note this only removes ONE of each item in arrayA. If you have multiple of the same instances in arrayA that are present in arrayB, only one will be removed. For multiples of the same instances, use this instead:
for (var i:uint = 0; i < arrayB.length; i++) {
while(arrayA.indexOf(arrayB[i]) >= 0) {
arrayA.splice(arrayA.indexOf(arrayB[i]), 1);
}
}
This effectively removes every element of Array B that happen to occur in Array A;
for each (var anItem:Object in B)
{
var anIndex:int;
do
{
anIndex = A.indexOf(anItem);
A.splice(anIndex, 1);
}
while (anIndex > -1);
}

How to walk a game character using mouse event in Adobe Animate CC AS3?

This question is related to my previous post, “TypeError: Error #1010: A term is undefined and has no properties” in AS3 because as I mentioned there, I'm creating an Android Game for our thesis. Now, I have a spritesheet of a character in the link: sprite character, I'm using this in the game. I'm researching on how to walk a character, I found one at a website, it actually works but unfortunately, it fails because the character didn't actually walk. I have no idea on what code will be place there. Either I will walk a character by clicking mouse or I will create a button then click on it to walk a character. What would be the code can I use for that? Any help will be appreciated.
P.S. In my previous post, I'm creating a code from timeline but now I transfer it to Actionscript file because of some errors.
EDIT:
Here's my code of the character:
forward.addEventListener(MouseEvent.CLICK, ppap);
function ppap(event:MouseEvent):void{
gril.x += mouseX;
gril.y += mouseY;
gril.gotoAndStop('i');
gameloop();
}
function gameloop(): void {
for (var o = 0; o > 5; o++) {
if (linya.hitTestObject(gril)) {
o++;
gotoAndStop(2);
scorer.visible = true;
timer.visible = true;
}
}
}
And the line: gril.gotoAndStop('a'); where the character is standing.
The gril is the instance name of a character. When it reaches to linya, the question will appear. Thanks!
Let's walk through your broken game loop
function gameloop(): void {
for (var o = 0; o > 5; o++) { //sets o to 0, loops as long as o > 5 (which it isn't, since we just set it to 0;
if (linya.hitTestObject(gril)) {
o++; //this also adds 1 to o
gotoAndStop(2);
scorer.visible = true;
timer.visible = true;
}
// if this part ever executed, it would add 1 to o
}
}
Do you see the problem? This for loop will not execute even once since 0 < 5
Instead it should be
function gameloop(): void {
for (var i = 0; i < 5; i++) {
if (linya.hitTestObject(gril)) {
gotoAndStop(2);
scorer.visible = true;
timer.visible = true;
break;
}
}
}
So here we have a functional (but pointless) for loop. It will work, but the first time through the loop it is going to result in the exact same thing as the second and third and fourth and fifth because changing the variable value by 1 isn't actually changing anything at all. You just telling the program to check the collision state 5 times. Well it does this 5 times before anything else can change. It checks it 5 times every game loop. Well I promise you nothing is moving while that for loop is running so why check it 5 times? I suggest stepping back and getting some help from your teacher or something because this seems way off. Sorry.
Right again #NealDavis and thank You for the comment!!!
I wrote my comment too quickly
Ascending loop from 0->4 (5 items):
for (var i:uint = 0; i < 5; i++){
trace("ascending = " + i);
}
Output:
ascending = 0
ascending = 1
ascending = 2
ascending = 3
ascending = 4
Descending loop from 4->0 (5 items):
for (var j:int = 4; j>=0; j--){
// j must be an int in this case, second mistake!
trace("descending = " + j)
};
Output:
descending = 4
descending = 3
descending = 2
descending = 1
descending = 0
My mistake. SRY
This is well explained in Looping reference
And in ActionScript 3 fundamentals: Loops reference
Shame on me!!! ;)
I'm a noob! :D
So I deleted my comment ;)
WOW, I'm so sorry about this mistake!!!
// You may also create Vectors to store the values.
var ascending:Vector.<uint> = new Vector.<uint>;
var descending:Vector.<uint> = new Vector.<uint>;
for (var k:uint = 0; k < 5; k++){
ascending.push(k+1);
}
trace("ascending Vector.<uint> = " + ascending);
// Output : ascending Vector.<uint> = 1,2,3,4,5
for (var l:int = 4; l >= 0; l--){
descending.push(l+1);
}
trace("descending Vector.<uint> = " + descending);
// Output : descending Vector.<uint> = 5,4,3,2,1
Or in an ascending loop :
trace("\nascending Vector.<uint> loop : ")
for(var m:String in ascending){
trace(m + " = " + ascending[m]);
}
Output :
ascending Vector.<uint> loop :
0 = 1
1 = 2
2 = 3
3 = 4
4 = 5
Or in a descending loop :
trace("descending Vector.<uint> loop : ")
for(var n:String in descending){
trace(n + " = " + descending[n]);
}
Output:
descending Vector.<uint> loop :
0 = 5
1 = 4
2 = 3
3 = 2
4 = 1

supplied DisplayObject must be a child of the caller fix

Im confused as to why my codes saying this,
My game creates a procedural generated map and the map is split into chuncks. The map works fine, i can switch areas and stuff no problem but when i delete a tree tile and re-add a grass tile then try to switch areas it tells me "The supplied DisplayObject must be a child of the caller." Iv gottn and fixed and slighty understand this problem, but i feel as if it IS a child of the calleer. idk :c
How my code is set up it creates a world class on my level class, then in that world a worldTiles sprite is created to place the tiles of the world into. This is where the tiles are originally added and deleted
This is where im pretty sure my problem is, the fucntion that deletes a tree tile and adds a grass tile
protected function mouseOnTile()
{
for (var i:int; i < world.tilesInWorld.length; i++)
{
if (mouse.hitTestObject(world.tilesInWorld[i]))
{
trace(world.tilesInWorld[i].Name);
if (world.tilesInWorld[i].Name == "tree")
{
var tx:int = world.tilesInWorld[i].x;
var ty:int = world.tilesInWorld[i].y;
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
world.tile = world.tilePool.getSprite();
world.tile.width = world.TILE_SIZE;
world.tile.height = world.TILE_SIZE;
world.tile.x = tx;
world.tile.y = ty;
world.tilesInWorld.push(world.tile);
world.worldTiles.addChild(world.tile);
}
}
}
}
Im sure it has something to do with how im re-adding the grass tile into the worldTiles, but im confused on how else i could do it?
This is the function that deletes the tiles when you walk to a different screen section
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
for (var i:int = tilesInWorld.length - 1; i >= 0; i--)
{
worldTiles.removeChild(tilesInWorld[i]);
switch (tilesInWorld[i].Name)
{
case "water" :
waterPool.returnSprite(tilesInWorld[i]);
break;
case "shallow" :
shallowPool.returnSprite(tilesInWorld[i]);
break;
case "shell" :
shellPool.returnSprite(tilesInWorld[i]);
break;
case "sand" :
sandPool.returnSprite(tilesInWorld[i]);
break;
case "tree" :
treePool.returnSprite(tilesInWorld[i]);
break;
case "grass" :
tilePool.returnSprite(tilesInWorld[i]);
break;
case "rock" :
rockPool.returnSprite(tilesInWorld[i]);
break;
case "stone" :
stonePool.returnSprite(tilesInWorld[i]);
break;
}
}
tilesInWorld.length = 0;//empty array
generateTile();
}
}
This is where the tiles are generated onto the screen after being deleted
public function generateTile()
{
var Xcalc:int = (X + (800 / TILE_SIZE) / GlobalCode.MAP_SCALE);
var Ycalc:int = (Y + (600 / TILE_SIZE) / GlobalCode.MAP_SCALE);
for (var i:int = X; i < Xcalc; i++)
{
for (var j:int = Y; j < Ycalc; j++)
{
hm = heightmap[i][j];
if ((hm >= 0.84))
{
tile = waterPool.getSprite();
}
else if (((hm >= 0.8) && hm < 0.84))
{
tile = shallowPool.getSprite();
}
else if (((hm >= 0.79) && hm < 0.799))
{
tile = shellPool.getSprite();
}
else if (((hm >= 0.7) && hm < 0.8))
{
tile = sandPool.getSprite();
}
else if (((hm >= 0.35) && hm < 0.4))
{
tile = treePool.getSprite();
}
else if (((hm >= 0.2) && hm < 0.7))
{
tile = tilePool.getSprite();
}
else if (((hm >= 0.09) && hm < 0.2))
{
tile = stonePool.getSprite();
}
else
{
tile = rockPool.getSprite();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % 800);
tile.y = TILE_SIZE * (j % 600);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}
I believe your problem is here:
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
...
}
You are lopping forward through the array, using removeChild on an element in the array, and using "pop" which removes the last item of the array, not the item that was actually removed. You eventually will hit an item that was already removed. Additionally, your i pointer changes each time you pop, which means the loop will never hit every item and is fundamentally flawed.
The DisplayObject used as the argument in dO.removeChild() must be defined, non-null, and a child of dO (i.e. added with dO.addChild(). If it does not meet all of those requirements, it will error out.
To fix this, use splice() instead of pop() (which will allow you to remove a specific element in an array) and go backwards through the array (which will handle the i pointer issues)
for (var i:int = world.tilesInWorld.length - 1; i >= 0; --i)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
...
}
You can also loop forward through the array, but you need to modify the pointer. This is slower and more error prone than going backwards, but can work just as well (when I say slower, we're talking microseconds difference unless you are doing massive computations).
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
--i;
...
}
Additionally, and this is just a syntax/readability thing, you should never rely on a datatype's default value. int will default to 0, but you should still declare it as var i:int = 0 to make it easy to change in the future, standardized and easy to read, and so that you could easily change it to a Number, which has a much, much higher max value than int but defaults to NaN.
This does not make any sense. Since you are removing all element from the array you can jusr forget about pop or slice and do
world.tilesInWorld.length = 0;
after the loop.
Since you are removing all object from world.worldTiles don't bother removing them one by one and do:
world.worldTiles.removeChildren()
after the loop.
Finally you are left with a simple loop where you only do:
world.treePool.returnSprite(world.tilesInWorld[i]);
It's a case (in your case and in the answer given) where you both try very hard to make a simple code as complicated as possible.

AS3 - Check results from an array and add to a counter

I am trying to make a video quiz and when you click it adds a value of 1 to an array. The array size goes up to [9] and I am trying to read from the array so that if there is a value of 1 in the array between [0] and [9] it will add 1 to the counter.
I have got it working for just the first value in the array so I know it works, but I am not sure about how to make it read from all of the array to check for values of 1.
Heres my code so far:
if (clickTimes[0] == 1)
{
counter2++;
}
Better yet, use a for each...in loop. It's really no different from a regular for loop, but it does provide some synactic sugar.
for each (var i:int in clickTimes)
{
if (i == 1)
{
counter2++;
}
}
For the beauty of it...
counter2 = clickTimes.join('').replace(/0/g, '').length;
It puts all your values in one string, remove the zeros and counts the characters left.
var clickTimes:Array = [1,1,1,0,0,0,1,1,0] // 5 1's and 4 0's
var counter2:int = 0
clickTimes.forEach(function(obj:*){
if (obj == 1){
counter2++;
}
})
trace(counter2) // traces 5
What about using a loop?
for (var i:int = 0; i < clickTimes.length; ++i)
{
if (clickTimes[i] == 1)
counter2++;
}
This would increment counter2, for each element that has a value of 1, in the clickTimes array.
You do not need if. Just the following:
var clickTimes:Array = [1,1,1,0,0,0,1,1,0] // 5 1's and 4 0's
var counter2:int = 0;
for each (var i:int in clickTimes)
{
counter2 += i;
}
trace(counter2); //5

AS3: Vector item isn't spliced

Hello I am creating a system with a gun that shoots bullet.
The update function is processed this way:
var b:Bullet;
var l:uint = bulletList.length;
var i:uint;
for (i = 0; i < l; i++) {
b = bulletList[i];
b.sprite.x += b.vx;
b.sprite.y += b.vy;
if (b.sprite.x > 1200 || b.sprite.x < -100 || b.sprite.y < -1000) {
deleteBullet(b);
bulletList.splice(i,1);
}
}
public function deleteBullet(b:Bullet) {
b.sprite = null;
b = null;
}
When I shoot a bullet and it goes of the edge it generates an error, and sometimes it creates a new one but that one doesn't have any motion at all. This is the error I get:
RangeError: Error #1125: The index 1 is out of range 1.
You're getting that error because you're splicing your array while in a for loop.
instead of using 'l' as your parameter for the for loop, use bulletList.length directly as every iteration it will look at the CURRENT length which will reflect anything spliced out of it. You'll also need subtract your iterator when splicing as that shifts all future indexes down by one.
for (i = 0; i < bulletList.length; i++) {
b = bulletList[i];
b.sprite.x += b.vx;
b.sprite.y += b.vy;
if (b.sprite.x > 1200 || b.sprite.x < -100 || b.sprite.y < -1000) {
deleteBullet(b);
bulletList.splice(i,1);
i--;
}
}