RayJW
RayJW

Reputation: 25

P5.js game crashes due to TypeError for a missing element in an array

OK, so I run into this problem where my program regularly crashes mid-game due to a TypeError: Uncaught TypeError: bullets[i] is undefined The error occurs in the function where I check for every variable in the bullets and the enemies array if there is a collision. This is the Source Code of this small school project I created, and I've been trying to troubleshoot this particular bug for hours at a time now. If anyone were able to help me that would be amazing as I can't quite grasp what the problem could be other than their occurring a collision when an element has already been spliced. But since the draw() function in P5.js runs 60 times per second that shouldn't really happen as regularly as it does, so I don't understand how this even can be an issue.

function setup() {
    createCanvas(displayWidth - 20, displayHeight - 20);
    angleMode(DEGREES);
    rectMode(CENTER);
    ellipseMode(CENTER);
    playerX = width / 2;
    playerY = height / 2;
}

let playerX = 0;
let playerY = 0;
let angle = 0;
let timer = 120;
let time = 0;
let v = 2;
let cooldown = 20;
let color = 50;
let hit = 0;

var bullets = [];
var enemies_easy = [];
var enemies_hard = [];
var score = 0;
var highscore = 0;
var gameover = false;
var gamestarted = false;
var nexthard = 5;
var currentenemy = 0;

function draw() {
    if (!gamestarted) {
        push();
        textAlign(CENTER);
        background('black');
        translate(displayWidth / 2, displayHeight / 2);
        textSize(width / 50);
        fill('white');
        text('Welcome!', 0, -100);
        text('Controls:', 0, 0);
        text('Fullscreen: F   Moving: W, A, S, D   Restart: R   Shooting: Left Mouse Button   Start: Space Bar', 0, 100);
        pop();
        if (keyIsDown(32)) {
            bullets = [];
            gamestarted = true;
        }
    } else {
        if (!gameover) {

            //calculates shot enemies_easy

            bulletcollision();

            //shoots if weapon cooldown is expired

            background('black');
            cooldown--;
            activategun();

            //draw hero

            angle = atan2(mouseY - playerY, mouseX - playerX) + 90;
            push();
            fill('blue');
            translate(playerX, playerY);
            rotate(angle);
            ellipse(0, 0, 40, 40);

            //draw gun

            fill('grey');
            rect(0, 0 - 25, 10, 20);
            pop();

            //move hero

            move();

            //creates enemies with increasing difficulty

            time++;
            difficulty();
            spawnenemy_easy();
            spawnenemy_hard();

            //shows score on screen

            showscore();

            //draw crosshair

            noCursor();
            push();
            fill('white');
            stroke('white');
            line(mouseX, mouseY, mouseX + 20, mouseY);
            line(mouseX, mouseY, mouseX - 20, mouseY);
            line(mouseX, mouseY, mouseX, mouseY + 20);
            line(mouseX, mouseY, mouseX, mouseY - 20);
            pop();

            //checks for game over

            playercollision();

        } else {
            if (keyIsDown(82)) {
                bullets = [];
                enemies_easy = [];
                enemies_hard = [];
                timer = 120;
                time = 0;
                cooldown = 20;
                score = 0;
                playerX = width / 2;
                playerY = height / 2;
                v = 2;
                gameover = false;
            }
        }
    }
}

class bullet {
    constructor() {
        this.x = playerX;
        this.y = playerY;
        this.angle = createVector(mouseX - playerX, mouseY - playerY);
        this.angle.normalize();
    }
    drawbullet() {
        push();
        fill('white');
        ellipse(this.x, this.y, 10, 10);
        pop();
        this.y = this.y + 10 * this.angle.y;
        this.x = this.x + 10 * this.angle.x;
    }
}

class enemy_easy {
    constructor() {
        this.x = random(-1000, width + 1000);
        if (this.x > width || this.x < 0) {
            if (this.x > width) {
                this.x = width;
                this.y = random(0, height + 1);
            }
            if (this.x < 0) {
                this.x = 0;
                this.y = random(0, height + 1);
            } else {}
        } else {
            let i = floor(random(0, 2));
            this.y = i * height;
        }
    }
    drawenemy_easy() {
        push();
        this.angle = createVector(this.x - playerX, this.y - playerY);
        this.angle.normalize();
        fill('red');
        ellipse(this.x, this.y, 30, 30);
        rotate(angle);
        pop();
        this.x = this.x - v * this.angle.x; // * random(0, 5);
        this.y = this.y - v * this.angle.y; // * random(0, 5);
    }
}

class enemy_hard {
    constructor() {
        this.x = random(-1000, width + 1000);
        if (this.x > width || this.x < 0) {
            if (this.x > width) {
                this.x = width;
                this.y = random(0, height + 1);
            }
            if (this.x < 0) {
                this.x = 0;
                this.y = random(0, height + 1);
            } else {}
        } else {
            let i = floor(random(0, 2));
            this.y = i * height;
        }
    }
    drawenemy_hard() {
        push();
        this.angle = createVector(this.x - playerX, this.y - playerY);
        this.angle.normalize();
        fill('purple');
        ellipse(this.x, this.y, 30, 30);
        rotate(angle);
        pop();
        this.x = this.x - v * this.angle.x; // * random(0, 5);
        this.y = this.y - v * this.angle.y; // * random(0, 5);
    }
}

function keyPressed() {

    //fullscreen Taste F: https://www.geeksforgeeks.org/p5-js-fullscreen-function/

    if (keyCode === 70) {
        let fs = fullscreen();
        fullscreen(!fs);
    }
}

function move() {
    if (keyIsDown(83) && playerY <= height - 22) {
        playerY = playerY + 4;
    }
    if (keyIsDown(87) && playerY >= 22) {
        playerY = playerY - 4;
    }
    if (keyIsDown(68) && playerX <= width - 22) {
        playerX = playerX + 4;
    }
    if (keyIsDown(65) && playerX >= 22) {
        playerX = playerX - 4;
    }
}

function activategun() {
    for (var i = 0; i < bullets.length; i++) {
        bullets[i].drawbullet();
        if (bullets[i].x <= 0 || bullets[i].y <= 0 || bullets[i].x >= width || bullets[i].y >= height) {
            bullets.splice(i, 1);
        }
    }
}

function mousePressed() {
    if (cooldown < 0) {
        bullets.push(new bullet());
        cooldown = 20;
    }
}

function spawnenemy_easy() {
    for (var i = 0; i < enemies_easy.length; i++) {
        enemies_easy[i].drawenemy_easy();
    }
}

function spawnenemy_hard() {
    for (var i = 0; i < enemies_hard.length; i++) {
        enemies_hard[i].drawenemy_hard();
    }
}

function newenemy() {
    if (currentenemy < nexthard) {
        enemies_easy.push(new enemy_easy());
        currentenemy++;
    } else {
        enemies_hard.push(new enemy_hard());
        currentenemy = 0;
        nexthard = random(2, 6);
    }
}

function bulletcollision() {
    for (var i = 0; i < bullets.length; i++) {
        for (var e = 0; e < enemies_easy.length; e++) {
            var distance = createVector(bullets[i].x - enemies_easy[e].x, bullets[i].y - enemies_easy[e].y);
            if (distance.mag() <= 18) {
                bullets.splice(i, 1);
                enemies_easy.splice(e, 1);
                score++;
                if (score > highscore) {
                    highscore++;
                }
            }
        }
        for (var e = 0; e < enemies_hard.length; e++) {
            var distance = createVector(bullets[i].x - enemies_hard[e].x, bullets[i].y - enemies_hard[e].y);
            if (distance.mag() <= 18) {
                bullets.splice(i, 1);
                hit++;
                if (hit == 2) {
                    enemies_hard.splice(e, 1);
                    score++;
                    if (score > highscore) {
                        highscore++;
                    }
                    hit = 0;
                }
            }
        }
    }
}

function playercollision() {
    for (var i = 0; i < enemies_easy.length; i++) {
        var distance = createVector(enemies_easy[i].x - playerX, enemies_easy[i].y - playerY);
        if (distance.mag() <= 25) {
            push();
            background(abs(255 * sin(random(0, 255))), 255 * sin(random(0, 255)), 255 * sin(random(0, 255)));
            fill('white');
            textAlign(CENTER);
            textSize(width / 40);
            translate(width / 2, height / 2);
            text("Game Over!", 0, -100);
            text("Score: " + score, 0, 0);
            text("High Score: " + highscore, 0, 100);
            pop();
            return gameover = true;
        }
    }
    for (var i = 0; i < enemies_hard.length; i++) {
        var distance = createVector(enemies_hard[i].x - playerX, enemies_hard[i].y - playerY);
        if (distance.mag() <= 25) {
            push();
            background(abs(255 * sin(random(0, 255))), 255 * sin(random(0, 255)), 255 * sin(random(0, 255)));
            fill('white');
            textAlign(CENTER);
            textSize(width / 40);
            translate(width / 2, height / 2);
            text("Game Over!", 0, -100);
            text("Score: " + score, 0, 0);
            text("High Score: " + highscore, 0, 100);
            pop();
            return gameover = true;
        }
    }
}

function difficulty() {
    if (time >= timer) {
        newenemy();
        time = 0;
        timer = timer * 0.99;
        v = v * 1.02;
    }
}

function showscore() {
    push();
    fill('white');
    textSize(width / 55);
    textAlign(CENTER);
    text('Score: ' + score, 100, 100);
    pop();
}

Does anyone know how I could fix this issue or if I even can? Any help is appreciated!

Upvotes: 1

Views: 80

Answers (1)

Rabbid76
Rabbid76

Reputation: 210909

The problem is that you are removing bullets from the array as you iterate through the array. Use while-loop instead of the for-loop:

function bulletcollision() {
    let i = 0;
    while (i < bullets.length) {
        let bullet_hit = false;
        for (var e = 0; e < enemies_easy.length; e++) {
            var distance = createVector(bullets[i].x - enemies_easy[e].x, bullets[i].y - enemies_easy[e].y);
            if (distance.mag() <= 18) {
                bullet_hit = true;
                destroy_enemy(enemies_easy, e);
            }
        }
        for (var e = 0; e < enemies_hard.length; e++) {
            var distance = createVector(bullets[i].x - enemies_hard[e].x, bullets[i].y - enemies_hard[e].y);
            if (distance.mag() <= 18) {
                bullet_hit = true;
                hit++;
                if (hit == 2) {
                    destroy_enemy(enemies_hard, e);
                    hit = 0;
                }
            }
        }
        if (bullet_hit) {
            bullets.splice(i, 1);
        } else {
            i ++;
        }
    }
}

function destroy_enemy(enemies, e) {
    enemies.splice(e, 1);
    score++;
    highscore = Math.max(highscore, score);
}

See also Looping through array and removing items, without breaking for loop.

Upvotes: 2

Related Questions