Search code examples
actionscript-3game-physics

AS3 Error #1010 A term is undefined and has no properties (Collision between two arrays of objects)


For now I have the player throwing pizzas at randomly spawned PacMan. The collision detection only works 40% of the time. Sometimes, the pizza goes straight through the PacMan or disappears half way through, and this error messages keeps showing up: TypeError: Error #1010: A term is undefined and has no properties. at PizzaDelivery/update(). Please help!

package  
{
      import flash.display.MovieClip;
      import flash.events.Event;
      import flash.events.KeyboardEvent;

public class PizzaDelivery extends MovieClip
{
    var moveLeft:Boolean;
    var moveRight:Boolean;
    var moveDown:Boolean;
    var moveUp:Boolean;
    var playerSpeed = 10;
    var numOfPizza = 0;
    var genList:Array = [];
    var genX:Array = [10,10,10,115,115,115];
    var genY:Array = [10,115,215,10,115,215];
    var pizzaList:Array = [];
    var enemyList:Array = [];
    var spawnTimeList:Array = [];
    var timer = 0;

    public function PizzaDelivery() 
    {

        for(var s=0; s!=12; s++)
        {
            spawnTimeList[s] = Math.floor(Math.random()*480);
        }

        for(var a=0; a!=6; a++)
        {
            genList[a]= new pizzaGen();
            stage.addChild(genList[a]);
            genList[a].x = genX[a];
            genList[a].y = genY[a];
            genList[a].timer = 60;
        }

        stage.addEventListener(Event.ENTER_FRAME, update);
        stage.addEventListener(KeyboardEvent.KEY_DOWN, movePlayer);
        stage.addEventListener(KeyboardEvent.KEY_UP, ceasePlayer);

        stackOfPizza.stop();
        player.stop();
    }

    function update(event:Event):void
    {

        //spawning enemies
        timer++;
        for(var o=0; o<=spawnTimeList.length-1; o++)
        {
            if(timer==spawnTimeList[o])
            {
                addEnemy(new PacMan);
            }
        }

        //pizza
        if(pizzaList.length != 0)
        {
            for(var f = 0; f <= pizzaList.length - 1; f++)
            {
                if(pizzaList[f].x >= 1000)
                {
                    if(pizzaList[f].parent)
                    {
                        pizzaList[f].parent.removeChild(pizzaList[f]);
                        pizzaList.splice(f,1);
                    }
                }
                if(enemyList.length != 0)
                {
                    for(var e = 0; e != enemyList.length - 1; e++)
                    {
                        if(pizzaList[f].hitTestObject(enemyList[e]))
                        {
                            if(pizzaList[f].parent)
                            {
                                pizzaList[f].parent.removeChild(pizzaList[f]);
                                pizzaList.splice(f,1);
                            }
                            if(enemyList[e].parent)
                            {
                                enemyList[e].parent.removeChild(enemyList[e]);
                                enemyList.splice(f,1);
                            }
                        }
                    }
                }
            }
        }

        //pizza generators
        for(var b=0;b!=6;b++)
        {
            if(player.hitTestObject(genList[b]))
            {
                if(genList[b].timer==60)
                {
                    if(numOfPizza!=10)
                    {
                        genList[b].timer = 0;
                        genList[b].gotoAndStop(0);
                        genList[b].visible = false;
                        numOfPizza++;
                        stackOfPizza.gotoAndStop(numOfPizza+1);
                    }
                }
            }
            if(genList[b].timer != 60)
            {
                genList[b].timer++;
            } else
            {
                genList[b].visible = true;
                genList[b].gotoAndStop(4);
            }
        }

        //player movement
        if(moveLeft==true)
        {
            player.play();
            player.x -= playerSpeed;
            stackOfPizza.x -= playerSpeed;
        }
        if(moveRight==true)
        {
            player.play();
            player.x += playerSpeed;
            stackOfPizza.x += playerSpeed;
        }
        if(moveUp==true)
        {
            player.play();
            player.y -= playerSpeed;
            stackOfPizza.y -= playerSpeed;
        }
        if(moveDown==true)
        {
            player.play();
            player.y += playerSpeed;
            stackOfPizza.y += playerSpeed;
        }
    }

    function addEnemy(object:Object):void
    {
        enemyList.push(object);
        stage.addChild(enemyList[enemyList.length-1]);
        object.x = 1000;
        object.y = Math.floor(Math.random() * (400-object.height));
    }

Solution

  • You almost got it correct, but it needs couple tiny changes.

    1. When pizzaList[f].x > 1000, you remove that pizza, and you can't use it anymore to check collisions, as it's not there anymore. Easiest way is to have a separate loop for x-position checks before collision detection.

    2. For loops: when you run an array loop and remove its elements inside the loop, you need to remember that when removing any element, you also change order for the rest of the elements in the array.

    F.ex. if you remove 2nd element, then 3rd will become 2nd and so on. And at the same time your loop variable increases from 1 to 2, and the outcome is that when the next loop cycle uses array[2], it skips 2nd element (former 3rd element).

    To avoid this, you have few different solutions.

    • In some cases breaking the loop will do, but not always.
    • When you get a "match", decrease the loop variable by 1 to avoid the skip for next cycle.
    • Use inverted loop, that counts from end to beginning.

    3. When you get positive hitTestObject, you remove both the pizza and enemy. But as pizza is now removed, you can not continue the enemy loop with the same pizza, as it's not there anymore. So, after a match, just break; the inner loop and then outer loop continues with next element.

    //pizza
    var f:int;
    if(pizzaList.length != 0){
        for(f = pizzaList.length - 1; f > -1; f--){ // 1 & 2
            if(pizzaList[f].x >= 1000){
                if(pizzaList[f].parent){
                    pizzaList[f].parent.removeChild(pizzaList[f]);
                    pizzaList.splice(f,1);
                }
            }
        }
        if(enemyList.length != 0){
            for(f = pizzaList.length - 1; f > -1; f--){ // 2
                for(var e:int = enemyList.length - 1; e > -1; e--){ // 2
                    if(pizzaList[f].hitTestObject(enemyList[e])){
                        if(pizzaList[f].parent){
                            pizzaList[f].parent.removeChild(pizzaList[f]);
                            pizzaList.splice(f,1);
                        }
                        if(enemyList[e].parent){
                            enemyList[e].parent.removeChild(enemyList[e]);
                            enemyList.splice(f,1);
                        }
                        break; // 3
                    }
                }
            }
        }
    }
    

    And actually you shouldn't need to check .parent for pizzas nor enemies.

    Edit: My first point is not a must, as you could keep everything in same loop and just have else block for the bottom part:

    if(pizzaList[f].x >= 1000){
      //removing pizza
    }else{
      if(enemyList.length != 0){
        //check collisions
      }
    }
    

    Just remember that you don't need another pizzaList loop inside this else-block, as you're already inside of one.