0
votes

I have encountered quite a challenging one and I'd love to get some support. Here is the scenario :

The main Game class instances the Level1 Class in charge for spawning enemies through nested For loops and push them to an array. It then checks for collisions between the Bullet and Enemy and if it find a collision it calls a method in the Enemy class that removes removeChild and Splice itself from the array.

The thing is it works for the first few enemies, and then it will pick the wrong Enemy to destroy, and stop completely to function. I tried using indexOf to be sure I am referring to the right object, but to no avail. I think the Pslice and removeChild are pointing to different objects.

This mess happended when I moved the removeChild and splice from the Game Class to the Enmy class

Link to the work in progress : https://dl.dropboxusercontent.com/s/69hcmzygnkx7h1e/space_shooter.swf

I'd like some help on this one. Thank you !!!

Main class : Game.AS

package 
{
    import flash.display.MovieClip;

    import flash.events.MouseEvent;
    import flash.events.Event;
    import flash.text.*;
    import flash.geom.Point;

    public class Game extends MovieClip
    {
        public var _instance : Game;
        public var player:Player;
        public  static var level1:Level1;
        public var bullet:Bullet;
        private var bullets_arr:Array;
        var fire_on : Boolean;
        var fire_counter : int;

        public function Game()
        {

            level1=new Level1(this.stage);
            player = new Player  ;
            addChild(player);
            player.y = 600;
            bullets_arr = [];
            addEventListener(Event.ENTER_FRAME,Main);
            stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP,mouseUpHandler);
        }

        function mouseDownHandler($e:MouseEvent):void
        {
            fire_on = true;
        }

        function mouseUpHandler($e:MouseEvent):void
        {
            fire_on = false;
            fire_counter = 0;
        }

        function fire():void
        {
            bullet = new Bullet  ;
            addChild(bullet);
            bullet.x = player.x;
            bullet.y = player.y - 32;
            bullets_arr.push(bullet);
        }

        public function Main(e: Event):void
        {

            player.x = mouseX;

            if (bullets_arr)
            {
                for (var m:int = 0; m < bullets_arr.length; m++)
                {
                    bullets_arr[m].y -=  20;

                    if(Game.level1.enemies_arr)
                    {   
                        for (var n:int = 0; n < Game.level1.enemies_arr.length; n++)
                        {
                            if (Game.level1.enemies_arr[n].hitTestObject(bullets_arr[m]))
                            {
                                if(bullets_arr[m].parent)
                                {
                                    bullets_arr[m].parent.removeChild(bullets_arr[m]);
                                    bullets_arr.splice(bullets_arr[m],1);
                                    Game.level1.enemies_arr[n].Damage(10, Game.level1.enemies_arr[n]);
                                }
                            }
                        }
                    }
                }
            }

            if(fire_on)
            {
                fire_counter++;
                if(fire_counter == 01)
                {
                    fire();
                }
                else if(fire_counter >2)
                {
                    fire_counter =0;
                }
            }
        }
    }
}

Level1.as where the enemies are spawned and pushed to the array.

  package 
{

import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;

public class Level1 extends MovieClip
{
    var i:int;
    var j:int;
    var frame :int;
    public var enemy:Enemy;
    public var enemies_arr:Array;

    public function Level1(target:Stage) 
    {
        frame = 0;
        enemies_arr = [];

        for (var i:int = 0; i < 5; i++)
        {
            for (var j:int = 0; j < 3; j++)
            {
                enemy = new Enemy;
                enemy.x = j*100 + 260;
                enemy.y = i*40 - 150;
                target.addChild(enemy);
                enemies_arr.push(enemy);
                trace(enemy.parent);
            }
        }
    }
}
}

The Enemy class Enemy.AS

package
{
    import flash.display.MovieClip;

    public class Enemy extends MovieClip
    {
        var Health : int;

        function Enemy()
        {
            Health =2;
        }
        public function Damage(Damage:int, enemyHit:Enemy)
        {
            Health -= Damage;
            if (Health <1)
            {
                Die(enemyHit);
            }
        }
        private function Die(enemyHit:Enemy)
        {       
            if(enemyHit.parent)
            {
                this.parent.removeChild(this);
                Game.level1.enemies_arr.splice(Game.level1.enemies_arr.indexOf(enemyHit,1));
            } 
        }
    }
}
2

2 Answers

0
votes

You should traverse both Game.level1.enemies_arr and bullets_arr backwards. The point is, splice() shortens the array, shifts the elements that are in greater positions than the spliced one(s) to lesser indexes, and the loop counter is not automatically adjusted. The error is pretty common, but is often overlooked. Also, with bullets_arr you can get out of the array causing a 1009 error if your last bullet out of bullets_arr will hit an enemy.

A small nitpick: You are checking for the array's existence within a loop, and for another array's existence once within the enter frame listener. In fact you should either initialize them with at least new Array() or [] before you add an event listener to the Main object, or wherever this is assigned to, or check like if (!bullets_arr) bullets_arr=new Array(); and leave it at that, so one check of the array's existence will be needed.

public function Main(e: Event):void
{
    player.x = mouseX;
    if (!bullets_arr) bullets_arr=new Array();
    if (!Game.level1.enemies_arr) throw new Error('Hey programmer, initialize your arrays!'); 
    // ^ bad practice to throw exceptions in listeners, but if you get one, you've coded something wrongly.
    for (var m:int=bullets_arr.length-1;m>=0;m--) {
        var bm:Bullet=bullets_arr[m]; // TODO fix type
        // the local variable is a cleaner and faster approach
        bm.y-=20;
        for (var n:int=Game.level1.enemies_arr.length-1;n>=0;n--) {
           if (!bm) break; // bullet was destroyed, why checking more enemies vs that?
           if (Game.level1.enemies_arr[n].hitTestObject(bm)) {
              bm.parent.removeChild(bm);
              bullets_arr.splice(m,1); // splice is done by index, not by object
              bm=null; // oops, missed this. The bullet hit something and is now lost
              // so we null the local var, so we can break out the loop.
              Game.level1.enemies_arr[n].Damage(10, Game.level1.enemies_arr[n]);
           }
        }
     }
    // rest of your code follows here
}
0
votes

I finally found the problem, it was very stupid of me : I just mistyped the Die function in the Enemy class :

I have written :splice(Game.level1.enemies_arr.indexOf(enemyHit,1)) instead of splice(Game.level1.enemies_arr.indexOf(enemyHit),1)

Anyway I learnt a lot of think by trying to fix this error. Thanks