无法读取小行星游戏未定义的 属性 'x'

Cannot read property 'x' of undefined for asteroid game

我有一个小行星游戏,玩家控制宇宙飞船物体并向小行星发射子弹以摧毁它们。如果子弹离开屏幕,它们将从子弹阵列中移除。有时,在测试 运行 程序时,我会收到一条错误消息:

Uncaught TypeError: Cannot read property 'x' of undefined
    at isInside (sketch.js:90)
    at checkCollisions (sketch.js:80)
    at draw (sketch.js:32)
    at b.a.default.redraw (p5.min.js:3)
    at _draw (p5.min.js:3)

如果我只是随机发射一堆子弹,这似乎经常发生。问题似乎来自小行星和子弹之间的碰撞检测。我假设一些子弹击中了屏幕外的小行星,但子弹已被移除,因此无法正确计算碰撞?我不太确定。这是小行星游戏的代码。

子弹系统

class BulletSystem {

  constructor(){
    this.bullets = [];
    this.velocity = new createVector(0, -5);
    this.diam = 10;
  }

  run(){
      this.move();
      this.draw();
      this.edges();
  }

  fire(x, y){
    this.bullets.push(createVector(x,y));
      print(this.bullets.length);
  }

  //draws all bullets
  draw(){
    for (var i=0; i<this.bullets.length; i++){
      fill("#00e5ff");
      ellipse(this.bullets[i].x, this.bullets[i].y, this.diam, this.diam);
    }
  }

  //updates the location of all bullets
  move(){
    for (var i=0; i<this.bullets.length; i++){
      this.bullets[i].y += this.velocity.y;
    }
  }

  //check if bullets leave the screen and remove them from the array
  edges(){
    for (var i=this.bullets.length-1; i>=0; i--){
      if(this.bullets[i].y < 0){
          this.bullets.splice(i, 1);
      }
    }
  }
}

小行星系统

class AsteroidSystem {
    
  //creates arrays to store each asteroid's data
  constructor(){
    this.locations = [];
    this.velocities = [];
    this.accelerations = [];
    this.diams = [];
    this.score = 0;
    this.difficulty = 0;
  }

  run(){
      this.spawn();
      this.move();
      this.draw();
  }

  // spawns asteroid at random intervals
  spawn(){
    if (random(1)<0.01 + this.difficulty){ //spawn frequency increases with time
      this.accelerations.push(new createVector(0,random(0.1,1)));
      this.velocities.push(new createVector(0, 0));
      this.locations.push(new createVector(random(width), 0));
      this.diams.push(random(30,50));
      this.difficulty+=0.0001;
    }
  }

  //moves all asteroids
  move(){
    for (var i=0; i<this.locations.length; i++){
      this.velocities[i].add(this.accelerations[i]);
      this.locations[i].add(this.velocities[i]);
      this.accelerations[i].mult(0);   
    }
  }

  applyForce(f){
    for (var i=0; i<this.locations.length; i++){
      this.accelerations[i].add(f);
    }
  }

  //draws all asteroids
  draw(){
    noStroke();
    fill(100);
    for (var i=0; i<this.locations.length; i++){
      //this.polygon(this.locations[i].x, this.locations[i].y, this.diams[i]*0.5, 8); //create octagon
      image(asteroidimg, this.locations[i].x, this.locations[i].y, this.diams[i], this.diams[i]); //create images of asteroid
    }
  }

  //function that calculates effect of gravity on each asteroid and accelerates it
  calcGravity(centerOfMass){
    for (var i=0; i<this.locations.length; i++){
      var gravity = p5.Vector.sub(centerOfMass, this.locations[i]);
      gravity.normalize();
      gravity.mult(.001);
      this.applyForce(gravity);
    }
  }

  //destroys all data associated with each asteroid
  destroy(index){
    this.locations.splice(index,1);
    this.velocities.splice(index,1);
    this.accelerations.splice(index,1);
    this.diams.splice(index,1);
    this.score++;
  }
    
  //function to create polygons
  polygon(x, y, radius, npoints) {
    let angle = TWO_PI / npoints;
    beginShape();
    for (let a = 0; a < TWO_PI; a += angle) {
        let sx = x + cos(a) * radius;
        let sy = y + sin(a) * radius;
        vertex(sx, sy);
    }
    endShape(CLOSE);
  }
}

主文件

var spaceship;
var asteroids;
var atmosphereLoc;
var atmosphereSize;
var earthLoc;
var earthSize;
var starLocs = [];

//////////////////////////////////////////////////
function setup() {
  createCanvas(1200,800);
  spaceship = new Spaceship();
  asteroids = new AsteroidSystem();

  //location and size of earth and its atmosphere
  atmosphereLoc = new createVector(width/2, height*2.9);
  atmosphereSize = new createVector(width*3, width*3);
  earthLoc = new createVector(width/2, height*3.1);
  earthSize = new createVector(width*3, width*3);
}

//////////////////////////////////////////////////
function draw() {
  background(0);
  sky();

  spaceship.run();
  asteroids.run();

  drawEarth();

  checkCollisions(spaceship, asteroids); // function that checks collision between various elements
  
  score(); //function that keeps score of how many asteroids have been hit 
}

//////////////////////////////////////////////////
//draws earth and atmosphere
function drawEarth(){
  noStroke();
  //draw atmosphere
  fill(150,150,150,50);
  ellipse(atmosphereLoc.x, atmosphereLoc.y, atmosphereSize.x,  atmosphereSize.y);
  //draw earth
  fill(0,0,200);
  ellipse(earthLoc.x, earthLoc.y, earthSize.x, earthSize.y);
}

//////////////////////////////////////////////////
//checks collisions between all types of bodies
function checkCollisions(spaceship, asteroids){

    //spaceship-2-asteroid collisions
    for (var i=0; i<asteroids.locations.length; i++){
        if (isInside(spaceship.location, spaceship.size, asteroids.locations[i], asteroids.diams[i]*2)==true){
            gameOver();
        }
    }

    //asteroid-2-earth collisions
    for (var i=0; i<asteroids.locations.length; i++){
        if (isInside(earthLoc, earthSize.x, asteroids.locations[i], asteroids.diams[i]*2)==true){
            gameOver();
        }
    }

    //spaceship-2-earth
    if (isInside(spaceship.location, spaceship.size, earthLoc, earthSize.x)==true){
        gameOver();
    }

    //spaceship-2-atmosphere
    if (isInside(spaceship.location, spaceship.size, atmosphereLoc, atmosphereSize.x)==true){
        spaceship.setNearEarth();
    }

    //bullet collisions
    for (var i=0; i<asteroids.locations.length; i++){
        for (var j=0; j<spaceship.bulletSys.bullets.length; j++){
            if (isInside(spaceship.bulletSys.bullets[j], spaceship.bulletSys.diam, asteroids.locations[i], asteroids.diams[i]*2)==true){
                asteroids.destroy(i);
            }
        }
    }
}

//////////////////////////////////////////////////
//helper function checking if there's collision between object A and object B
function isInside(locA, sizeA, locB, sizeB){
    var d = dist(locA.x, locA.y, locB.x, locB.y); //distance between both objects
    var s = sizeA/2 + sizeB/2; //sum of both objects' radius, size is divided by 2 since size is the diameter 
    if(d < s) //if distance between objects is smaller than sum of both objects' radius, overlap occurs
    {
        return true;
    } else { 
        return false;
    }
}

//////////////////////////////////////////////////
function keyPressed(){
  if (keyIsPressed && keyCode === 32){ // if spacebar is pressed, fire!
    spaceship.fire();
  }
}

//////////////////////////////////////////////////
// function that ends the game by stopping the loops and displaying "Game Over"
function gameOver(){
  fill(255);
  textFont(font);
  textSize(80);
  textAlign(CENTER);
  text("GAME OVER", width/2, height/2)
  noLoop();
}

//////////////////////////////////////////////////
// function that creates a star lit sky
function sky(){
  push();
  while (starLocs.length<300){
    starLocs.push(new createVector(random(width), random(height)));
  }
  fill(255);
  for (var i=0; i<starLocs.length; i++){
    rect(starLocs[i].x, starLocs[i].y,2,2);
  }

  if (random(1)<0.3) starLocs.splice(int(random(starLocs.length)),1);
  pop();
}

function preload() {
  font = loadFont('assets/zorque.ttf');
  asteroidimg = loadImage('assets/asteroid.png');
  spaceshipimg = loadImage('assets/spaceship.png');
}

//////////////////////////////////////////////////
// function that keeps score of how many asteroids have been hit
function score(){
  fill(255);
  textFont(font);
  textSize(30);
  textAlign(CENTER);
  text("Score: " + asteroids.score, width/2, height-40)
}

这是发生错误的代码片段:

for (var i=0; i<asteroids.locations.length; i++){
    for (var j=0; j<spaceship.bulletSys.bullets.length; j++){
        if (isInside(spaceship.bulletSys.bullets[j], spaceship.bulletSys.diam, asteroids.locations[i], asteroids.diams[i]*2)){
            asteroids.destroy(i);
        }
    }
}

错误原因:asteroids.destroy(i);执行时,循环还在继续执行,if检查还在运行。如果 i == asteroids.locations.length - 1,则表示 asteroids.locations[i] 是数组中的最后一个元素。 asteroids.destroy(i)splice 它从数组中取出,就像切掉数组的尾巴一样。那么 asteroids.locations[i] 将等于 undefinedif 将使用它作为 undefined 来检查 isInsideisInside 会将 locB.x 视为 undefined.x 并抛出 Cannot read property 'x' of undefined.

的错误

提出解决方案:销毁 asteroids 时,您应该 break 跳出 for 循环。例如:

for (var i=0; i<asteroids.locations.length; i++){
    for (var j=0; j<spaceship.bulletSys.bullets.length; j++){
        if (isInside(spaceship.bulletSys.bullets[j], spaceship.bulletSys.diam, asteroids.locations[i], asteroids.diams[i]*2)){
            asteroids.destroy(i);
            break;
        }
    }
}