无法读取小行星游戏未定义的 属性 '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]
将等于 undefined
。 if
将使用它作为 undefined
来检查 isInside
。 isInside
会将 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;
}
}
}
我有一个小行星游戏,玩家控制宇宙飞船物体并向小行星发射子弹以摧毁它们。如果子弹离开屏幕,它们将从子弹阵列中移除。有时,在测试 运行 程序时,我会收到一条错误消息:
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]
将等于 undefined
。 if
将使用它作为 undefined
来检查 isInside
。 isInside
会将 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;
}
}
}