Ошибка JavaScript при итерации двух массивов для обнаружения попаданий

Я делаю небольшую html5 canvas игру на чистом JS (со стандартами Ecmascript 6). Пока что все прошло хорошо, но теперь я застрял на повторяющейся ошибке TypeError (Uncaught TypeError: Невозможно прочитать свойство 'position' из undefined).

Это случается очень часто, когда игра проверяет наличие столкновений между объектами внутри двух массивов (точнее: обнаружение столкновений между пулями и врагами).

В основном, моя функция обнаружения столкновений работает нормально. Когда я думаю, что исправил проблему, через некоторое время это случилось снова.

Это мой код:

const collisionCheckBulletEnemy = () => {

    for(let i = bullets.length -1; i >= 0; i--){
        for(let j = enemies.length -1; j >= 0; j--){

            if(collisionCheck(bullets[i], enemies[j], 10, 10)){

                bullets.splice(i, 1);
                enemies.splice(j, 1);
            }
        }
    }
}

Это функция обнаружения столкновений:

const collisionCheck = (a, b, marginA, marginB) => {
    // margins can be added to make things a little easier/harder on the user
    if(!marginA){
        marginA = 0;
    }
    if(!marginB){
        marginB = 0;
    }

    return !(
        a.position.x - marginA > b.position.x + b.width + marginB ||
        a.position.x + a.width + marginA < b.position.x - marginB ||
        a.position.y - marginA > b.position.y + b.height + marginB ||
        a.position.y + a.height + marginA < b.position.y - marginB
    );
}

1 ответ

Решение

Это происходит потому, что иногда либо параметр a или же b передается в функцию, хотя это просто undefined значение. Пытаюсь сделать undefined.position приведет к ошибке вашего типа.

Простое, хакерское решение - просто поставить условие наверх:

if (!a || !b) {
  return 0; // or whatever your default value is supposed to be
};

Настоящее лучшее решение - выяснить, почему bullets а также enemies содержат некоторые неопределенные значения.

После прочтения вашего кода я думаю, что это ответ:

если это условие проходит, когда i = bullets.length - 1:

if(collisionCheck(bullets[i], enemies[j], 10, 10)) {
  bullets.splice(i, 1);
  enemies.splice(j, 1);
}

именно эта часть bullets.splice(i, 1); Вы сокращаете свой массив на 1, но никогда не уменьшаете i,

Так что если bullets[i] был самый последний элемент в вашем массиве сейчас bullets[i] является undefined потому что JavaScript не выдает ошибку, как indexOutOfBounds,

Теперь вы начинаете видеть, что огромный недостаток в вашем коде состоит в том, что он не прекращает зацикливаться, когда пуля удаляется из массива, и что вы заметили, только когда это был последний индекс. Даже если это не самый последний индекс, он будет продолжать цикл для другой пули, которая не соответствует вашим намерениям.

Вместо этого вы должны выйти из цикла, так как если вы уничтожаете вашу пулю, когда она попадает, вы не должны продолжать проверять столкновения для той же самой пули:

if(collisionCheck(bullets[i], enemies[j], 10, 10)) {
  bullets.splice(i, 1);
  enemies.splice(j, 1);
  break;
}
Другие вопросы по тегам