My interaction between two objects aren't working and result with "Cannot read property 'x' of undefined"

Alex :

Alright! So after looking on stack overflow for awhile, p5.js referencing and debugging for a couple of hours, I've decided I need a fresh pair of eyes to look over my code. Right now, my game is one in which the user uses WASD to move and clicks to shoot bullets, only problem is, when the bullets are in range of the enemies (specifically more than one) I get an error saying x is not defined. Now, I know this has to do with an object problem and something around those lines, I just can't find where. Can anyone help?

//Character Position
let playX = 300;
let playY = 300;

let up = false;
let down = false;
let left = false;
let right = false;
let working = false;

let bulletSpeed = 3;
var bullets = [];
var shadows = [];
var angleEn;
var enemySpeed = 1;
var spawnRate = 300;

function setup() {
  var canvas = createCanvas(600, 600);
  generateEnemy();
}

function generateEnemy() {
  side = floor(random(0, 2));
  side2 = floor(random(0, 2));
  if (side % 2 === 0) { // top and bottom
    shadows.push(new Enemy(random(0, width), height * (side2 % 2)));
  } else { // sides
    shadows.push(new Enemy(width * (side2 % 2), random(0, height)));
  }
}

function timeEnter() {
  if (frameCount % spawnRate === 0) {
    generateEnemy();
    if (enemySpeed < 3) {
      enemySpeed += 0.1;
    }
    if (spawnRate > 50) {
      spawnRate -= 10;
    }
  }
}

function character() {
  fill(0);
  rect(playerX, playerY, 50, 50);
}

function movement() {
  if (up) {
    playY -= 2;
  }
  if (down) {
    playY += 2;
  }
  if (left) {
    playX -= 2;
  }
  if (right) {
    playX += 2;
  }
}

function draw() {
  //Setting up background
  playerX = playX - 25;
  playerY = playY - 25;
  background(0);
  fill(255);
  rect(100, 100, 400, 400);

  //Basic movement and display of character
  movement();
  character();
  timeEnter();

  //Updating and moving bullets
  for (let i = 0; i < bullets.length; i++) {
    bullets[i].update();
    bullets[i].display();

    if (bullets[i].x < -50 || bullets[i].x > 650 || bullets[i].y < -50 || bullets[i].y > 650) {
      bullets.splice(i, 1);
    }
  }

  //Updating and moving shadows
  for (let s = 0; s < shadows.length; s++) {
    shadows[s].update();
    shadows[s].display();
    for (let e = 0; e < bullets.length; e++) {
      if (abs(bullets[e].x - shadows[s].x) < 25 && abs(bullets[e].y - shadows[s].y) < 25) {
        bullets.splice(e, 1);
        shadows.splice(s, 1);
      }
    }

  }

}

function keyTyped() {
  if (key === 'w') {
    up = true;
  }
  if (key === 's') {
    down = true;
  }
  if (key === 'a') {
    left = true;
  }
  if (key === 'd') {
    right = true;
  }
}

function keyReleased() {
  if (key === 'w') {
    up = false;
  }
  if (key === 's') {
    down = false;
  }
  if (key === 'a') {
    left = false;
  }
  if (key === 'd') {
    right = false;
  }

}

function mousePressed() {
  dx = mouseX - 25 - playerX;
  dy = mouseY - 25 - playerY;
  angle = atan2(dy, dx);
  vx = bulletSpeed * cos(angle);
  vy = bulletSpeed * sin(angle);
  bullets.push(new Bullet(playX, playY, vx, vy));

}


function Enemy(x, y) {
  this.x = x;
  this.y = y;


  this.update = function() {
    angleEn = atan2(playerY - this.y, playerX - this.x);
    this.vx = cos(angleEn);
    this.vy = sin(angleEn);
    this.x += this.vx * enemySpeed;
    this.y += this.vy * enemySpeed;
  }

  this.display = function() {
    stroke(255);
    strokeWeight(4);
    fill(0);
    rect(this.x, this.y, 50, 50);
  }
}


function Bullet(x, y) {
  this.x = x;
  this.y = y;
  this.vx = vx;
  this.vy = vy;


  this.update = function() {
    this.x += this.vx;
    this.y += this.vy;
  }

  this.display = function() {
    ellipse(this.x, this.y, 25, 25);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/p5.min.js"></script>

So, the problem is definitely somewhere in the objects or draw function when they're used. If anyone can help that'd be great, and if you need me to explain more, just say. Thanks in advance!

Rabbid76 :

The major issue is, that you iterate the arrays while you remove elements from the arrays. You will miss an element for each element that is removed (except the last element is removed).
See also (Looping through array and removing items, without breaking for loop)

Furthermore you 2 nested arrays and remove elements form the outer list in the inner loop. That causes the error if the last element of the outer list is removed and the inner array still is iterated:

for (let s = 0; s < shadows.length; s++) {
   // [...]

   for (let e = 0; e < bullets.length; e++) {
      if (abs(bullets[e].x - shadows[s].x) < 25 && abs(bullets[e].y - shadows[s].y) < 25) {
           bullets.splice(e, 1);
           shadows.splice(s, 1); // <--- causes error if last element is removed 
                                 //      but inner loop still runs  
       }
   }
}

Another issue is that the collision test does not work correctly, because (shadows[s].x, shadows[s].y) is the top left of the rectangle rather than the center.

Iterate the arrays in reverse order and use (shadows[s].x+25, shadows[s].y+25) for the collision test, to solve the issue:

//Updating and moving bullets
for (let i = bullets.length-1; i >=0 ; i--) {
      bullets[i].update();
      bullets[i].display();

    if (bullets[i].x < -50 || bullets[i].x > 650 || bullets[i].y < -50 || bullets[i].y > 650) {
        bullets.splice(i, 1);
    }
}

//Updating and moving shadows
for (let s = shadows.length-1; s >= 0; s--) {
    shadows[s].update();
    shadows[s].display();

    let remove_enemy = false;
    for (let e = bullets.length-1; e >=0 ; e--) {
        if (abs(bullets[e].x - (shadows[s].x+25)) < 25 && abs(bullets[e].y - (shadows[s].y+25)) < 25) {
            bullets.splice(e, 1);
            remove_enemy = true;
        }
    }
    if (remove_enemy) {
        shadows.splice(s, 1);
    }
}

Complete example:

//Character Position
let playX = 300;
let playY = 300;

let up = false;
let down = false;
let left = false;
let right = false;
let working = false;

let bulletSpeed = 3;
var bullets = [];
var shadows = [];
var angleEn;
var enemySpeed = 1;
var spawnRate = 300;

function setup() {
  var canvas = createCanvas(600, 600);
  generateEnemy();
}

function generateEnemy() {
  side = floor(random(0, 2));
  side2 = floor(random(0, 2));
  if (side % 2 === 0) { // top and bottom
    shadows.push(new Enemy(random(0, width), height * (side2 % 2)));
  } else { // sides
    shadows.push(new Enemy(width * (side2 % 2), random(0, height)));
  }
}

function timeEnter() {
  if (frameCount % spawnRate === 0) {
    generateEnemy();
    if (enemySpeed < 3) {
      enemySpeed += 0.1;
    }
    if (spawnRate > 50) {
      spawnRate -= 10;
    }
  }
}

function character() {
  fill(0);
  rect(playerX, playerY, 50, 50);
}

function movement() {
  if (up) {
    playY -= 2;
  }
  if (down) {
    playY += 2;
  }
  if (left) {
    playX -= 2;
  }
  if (right) {
    playX += 2;
  }
}

function draw() {
  //Setting up background
  playerX = playX - 25;
  playerY = playY - 25;
  background(0);
  fill(255);
  rect(100, 100, 400, 400);

  //Basic movement and display of character
  movement();
  character();
  timeEnter();

    //Updating and moving bullets
    for (let i = bullets.length-1; i >=0 ; i--) {
          bullets[i].update();
          bullets[i].display();

        if (bullets[i].x < -50 || bullets[i].x > 650 || bullets[i].y < -50 || bullets[i].y > 650) {
            bullets.splice(i, 1);
        }
    }

    //Updating and moving shadows
    for (let s = shadows.length-1; s >= 0; s--) {
        shadows[s].update();
        shadows[s].display();
        
        let remove_enemy = false;
        for (let e = bullets.length-1; e >=0 ; e--) {
            if (abs(bullets[e].x - (shadows[s].x+25)) < 25 && abs(bullets[e].y - (shadows[s].y+25)) < 25) {
                bullets.splice(e, 1);
                remove_enemy = true;
            }
        }
        if (remove_enemy) {
            shadows.splice(s, 1);
        }
    }
}

function keyTyped() {
  if (key === 'w') {
    up = true;
  }
  if (key === 's') {
    down = true;
  }
  if (key === 'a') {
    left = true;
  }
  if (key === 'd') {
    right = true;
  }
}

function keyReleased() {
  if (key === 'w') {
    up = false;
  }
  if (key === 's') {
    down = false;
  }
  if (key === 'a') {
    left = false;
  }
  if (key === 'd') {
    right = false;
  }

}

function mousePressed() {
  dx = mouseX - 25 - playerX;
  dy = mouseY - 25 - playerY;
  angle = atan2(dy, dx);
  vx = bulletSpeed * cos(angle);
  vy = bulletSpeed * sin(angle);
  bullets.push(new Bullet(playX, playY, vx, vy));
}


function Enemy(x, y) {
  this.x = x;
  this.y = y;


  this.update = function() {
    angleEn = atan2(playerY - this.y, playerX - this.x);
    this.vx = cos(angleEn);
    this.vy = sin(angleEn);
    this.x += this.vx * enemySpeed;
    this.y += this.vy * enemySpeed;
  }

  this.display = function() {
    stroke(255);
    strokeWeight(4);
    fill(0);
    rect(this.x, this.y, 50, 50);
  }
}


function Bullet(x, y) {
  this.x = x;
  this.y = y;
  this.vx = vx;
  this.vy = vy;


  this.update = function() {
    this.x += this.vx;
    this.y += this.vy;
  }

  this.display = function() {
    ellipse(this.x, this.y, 25, 25);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/p5.js"></script>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=174632&siteId=1