javascript - How do I gain access to the array in a method? - Stack Overflow

I have created this program that checks the collisions of balls as they move in random directions. Each

I have created this program that checks the collisions of balls as they move in random directions. Each ball has life points, that decrease with each collision. I want my program to print out a message "character has died" when a ball is considered dead. The problem is that I don't know how to replace a dead ball with a new one in my balls array, as I don't know at which index to perform the replacement.

let balls = []; 
let numBalls = 20;
let ballTextures = [];

let subaruImg, emiliaImg, remImg;

//------- Variables initialsed above ---------- //

function preload(){
  subaruImg = loadImage('assets/subaru_chibi.jpg');
  emiliaImg = loadImage('assets/emilaia_chibi.jpg');
  remImg = loadImage('assets/rem_chibi.jpg');

  ballTextures = [subaruImg, emiliaImg, remImg]; // Store my GOAT subaru and his friends in the array
}

// Preload is for images and textures

function setup() {
  createCanvas(600, 600, WEBGL);
  angleMode(DEGREES);

  for (let i = 0; i < numBalls; i++) {
    let pos = createVector(
      random(-width / 2 + 20, width / 2 - 20),
      random(-height / 2 + 20, height / 2 - 20),
      random(-200, 200)
    );
    let vel = p5.Vector.random3D().mult(random(1, 2));
    let r = random(20, 30);
    
    // Let 
    let tex = random(ballTextures); //allows the t
    
    balls.push(new Ball(pos, vel, r, tex));
  }

  frameRate(60);
}

function draw() {
  background(20);
  orbitControl(); // makes me be able to have 3d movementt on the canvas

  //different lights 
  ambientLight(60, 60, 60);

  
  directionalLight(255, 255, 255, -0.5, -1, -0.5);

  // Moving point light (dynamic, cool effect)
  let lightX = sin(frameCount * 0.01) * 300;
  let lightY = cos(frameCount * 0.01) * 300;
  let lightZ = sin(frameCount * 0.005) * 300;
  pointLight(255, 255, 200, lightX, lightY, lightZ);

  // Optional: visualize the light source
  push();
  translate(lightX, lightY, lightZ);
  noStroke();
  emissiveMaterial(255, 255, 200);
  sphere(5); // glowing light orb
  pop();

  for (let i = 0; i < balls.length; i++) {
    balls[i].move();
    balls[i].edgeCheck();
    balls[i].drawBall();

    for (let j = i + 1; j < balls.length; j++) {
      balls[i].checkCollision(balls[j]);
    }
  }
}

class Ball {
  constructor(pos, vel, r, tex) {
    this.pos = pos;
    this.vel = vel;
    this.r = r;
    this.tex = tex;
    this.life = 3; // start with 3 lives
    this.alive = true;
  }

  drawBall() {
    if (!this.alive) return; // don't draw if dead

    push();
    translate(this.pos.x, this.pos.y, this.pos.z);
    texture(this.tex);
    noStroke();
    sphere(this.r);
    pop();
  }

  move() {
    if (!this.alive) return;
    this.pos.add(this.vel);
  }

  edgeCheck() {
    if (!this.alive) return;
    if (this.pos.x + this.r > width / 2 || this.pos.x - this.r < -width / 2) {
      this.vel.x *= -1;
    }
    if (this.pos.y + this.r > height / 2 || this.pos.y - this.r < -height / 2) {
      this.vel.y *= -1;
    }
    if (this.pos.z + this.r > 300 || this.pos.z - this.r < -300) {
      this.vel.z *= -1;
    }
  }

  checkCollision(other) {
    if (!this.alive || !other.alive) return;

    let d = p5.Vector.dist(this.pos, other.pos);
    if (d < this.r + other.r) {
      // Swap velocities
      let temp = this.vel.copy();
      this.vel = other.vel.copy();
      other.vel = temp;

      // Separate overlapping balls
      let overlap = (this.r + other.r - d) / 2;
      let direction = p5.Vector.sub(this.pos, other.pos).normalize();
      this.pos.add(direction.mult(overlap));
      other.pos.sub(direction.mult(overlap));

      // Decrease life
      this.lifeCounter();
      other.lifeCounter();
    }
  }

  lifeCounter() {
    this.life -= 1;
    if (this.life <= 0) {
      this.alive = false && new Ball(pos, vel, r, tex.subaruImg);
      console.log("subaru has died");
    }
    if(this.life <=0) {
      this.alive = false && new Ball(pos, vel, r, tex.emiliaImg);
      console.log("emilia has died")
    }
    if(this.life <= 0){
      this.alive = false && new Ball(pos, vel, r, tex.remImg);
      console.log("Rem has died")
    }
  }


}
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src=".js/1.11.1/p5.js"></script>
    <script src=".js/1.11.1/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>

I have created this program that checks the collisions of balls as they move in random directions. Each ball has life points, that decrease with each collision. I want my program to print out a message "character has died" when a ball is considered dead. The problem is that I don't know how to replace a dead ball with a new one in my balls array, as I don't know at which index to perform the replacement.

let balls = []; 
let numBalls = 20;
let ballTextures = [];

let subaruImg, emiliaImg, remImg;

//------- Variables initialsed above ---------- //

function preload(){
  subaruImg = loadImage('assets/subaru_chibi.jpg');
  emiliaImg = loadImage('assets/emilaia_chibi.jpg');
  remImg = loadImage('assets/rem_chibi.jpg');

  ballTextures = [subaruImg, emiliaImg, remImg]; // Store my GOAT subaru and his friends in the array
}

// Preload is for images and textures

function setup() {
  createCanvas(600, 600, WEBGL);
  angleMode(DEGREES);

  for (let i = 0; i < numBalls; i++) {
    let pos = createVector(
      random(-width / 2 + 20, width / 2 - 20),
      random(-height / 2 + 20, height / 2 - 20),
      random(-200, 200)
    );
    let vel = p5.Vector.random3D().mult(random(1, 2));
    let r = random(20, 30);
    
    // Let 
    let tex = random(ballTextures); //allows the t
    
    balls.push(new Ball(pos, vel, r, tex));
  }

  frameRate(60);
}

function draw() {
  background(20);
  orbitControl(); // makes me be able to have 3d movementt on the canvas

  //different lights 
  ambientLight(60, 60, 60);

  
  directionalLight(255, 255, 255, -0.5, -1, -0.5);

  // Moving point light (dynamic, cool effect)
  let lightX = sin(frameCount * 0.01) * 300;
  let lightY = cos(frameCount * 0.01) * 300;
  let lightZ = sin(frameCount * 0.005) * 300;
  pointLight(255, 255, 200, lightX, lightY, lightZ);

  // Optional: visualize the light source
  push();
  translate(lightX, lightY, lightZ);
  noStroke();
  emissiveMaterial(255, 255, 200);
  sphere(5); // glowing light orb
  pop();

  for (let i = 0; i < balls.length; i++) {
    balls[i].move();
    balls[i].edgeCheck();
    balls[i].drawBall();

    for (let j = i + 1; j < balls.length; j++) {
      balls[i].checkCollision(balls[j]);
    }
  }
}

class Ball {
  constructor(pos, vel, r, tex) {
    this.pos = pos;
    this.vel = vel;
    this.r = r;
    this.tex = tex;
    this.life = 3; // start with 3 lives
    this.alive = true;
  }

  drawBall() {
    if (!this.alive) return; // don't draw if dead

    push();
    translate(this.pos.x, this.pos.y, this.pos.z);
    texture(this.tex);
    noStroke();
    sphere(this.r);
    pop();
  }

  move() {
    if (!this.alive) return;
    this.pos.add(this.vel);
  }

  edgeCheck() {
    if (!this.alive) return;
    if (this.pos.x + this.r > width / 2 || this.pos.x - this.r < -width / 2) {
      this.vel.x *= -1;
    }
    if (this.pos.y + this.r > height / 2 || this.pos.y - this.r < -height / 2) {
      this.vel.y *= -1;
    }
    if (this.pos.z + this.r > 300 || this.pos.z - this.r < -300) {
      this.vel.z *= -1;
    }
  }

  checkCollision(other) {
    if (!this.alive || !other.alive) return;

    let d = p5.Vector.dist(this.pos, other.pos);
    if (d < this.r + other.r) {
      // Swap velocities
      let temp = this.vel.copy();
      this.vel = other.vel.copy();
      other.vel = temp;

      // Separate overlapping balls
      let overlap = (this.r + other.r - d) / 2;
      let direction = p5.Vector.sub(this.pos, other.pos).normalize();
      this.pos.add(direction.mult(overlap));
      other.pos.sub(direction.mult(overlap));

      // Decrease life
      this.lifeCounter();
      other.lifeCounter();
    }
  }

  lifeCounter() {
    this.life -= 1;
    if (this.life <= 0) {
      this.alive = false && new Ball(pos, vel, r, tex.subaruImg);
      console.log("subaru has died");
    }
    if(this.life <=0) {
      this.alive = false && new Ball(pos, vel, r, tex.emiliaImg);
      console.log("emilia has died")
    }
    if(this.life <= 0){
      this.alive = false && new Ball(pos, vel, r, tex.remImg);
      console.log("Rem has died")
    }
  }


}
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare/ajax/libs/p5.js/1.11.1/p5.js"></script>
    <script src="https://cdnjs.cloudflare/ajax/libs/p5.js/1.11.1/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>

Here is a screenshot of when the balls start moving across the screen:

However when a ball "dies", then in the console log it shows three messages -- one for each name, so always in groups of three:

The section where this happens is in the lifeCounter method in the Ball class at the end of the code:

  lifeCounter() {
    this.life -= 1;
    if (this.life <= 0) {
      this.alive = false && new Ball(pos, vel, r, tex.subaruImg);
      console.log("subaru has died");
    }
    if(this.life <=0) {
      this.alive = false && new Ball(pos, vel, r, tex.emiliaImg);
      console.log("emilia has died")
    }
    if(this.life <= 0){
      this.alive = false && new Ball(pos, vel, r, tex.remImg);
      console.log("Rem has died")
    }
  }

Instead of three messages per ball "death", I want to print one message only, and have the ball replaced with a new Ball instance, but I have no idea how I can know where to put it in the array balls, so currently the array is not updated and keeps continuing with balls that already "died". How can I achieve this update?

Share Improve this question edited Mar 27 at 8:47 trincot 352k36 gold badges273 silver badges328 bronze badges asked Mar 27 at 1:05 nouserbruh1nouserbruh1 174 bronze badges 1
  • 1 I suggest providing a bit more context here. What is the game you're creating and what is the expected behavior? this.alive = false && new Ball(pos, vel, r, tex.remImg); can be simply this.alive = false. I suggest a runnable, minimal example--remove textures, lighting and things that don't pertain to the question. Thanks. – ggorlen Commented Mar 27 at 4:37
Add a comment  | 

1 Answer 1

Reset to default 1

There are several issues:

  • When this.life <= 0 is true, it will be true in the three copies you have of the same if statement, and so you'll always print three names each time it is true.

  • tex does not have a property that is called subaruImg (nor emiliaImg or remImg). You don't actually know the name of the ball that died. To know the name, you would better store this as a ball property when the ball is created.

  • && is a short-circuit operator, so if the left operand is false, the right operand will not be evaluated. That means that in your && expressions the new Ball expression will never be evaluated: no new ball is created.

  • pos, vel and r are not variables that are in scope, so executing those new Ball expressions would not lead to desired behaviour anyway.

As to your main question: indeed you don't have the index where in the balls array the current ball is placed. But this is not something that is the responsibility of your Balls class. If you want to replace a ball in the balls array, this should happen outside of that class, like in the loop where you call the checkCollision method.

I have updated your snippet with the following changes:

  • The textures are now stored in a plain object that is keyed by the (avatar) names. This way you have the correspondence between a name and a texture. This affects the code of preload and of the random selection of textures.

  • The Ball constructor takes an extra parameter for the name that is associated with the ball, and that name is stored as a new instance property.

  • The erroneous code in lifeCounter has been removed, and so it just updates the counter and the alive boolean -- that's all it needs to do.

  • The death-check and replacement of a dead ball by a new one now happens in the loop that calls checkCollision.

As this snippet has no access to your images, I have renamed preload so it doesn't execute here -- you would of course enable it in your own environment. I have removed all the comments in your code -- not because they are bad, but to highlight with my own comments where I have changed the code:

let balls = []; 
let numBalls = 20;
// Make the textures collection a plain object, keyed by (avatar) name
const ballTextures = {
  "subaru": null, // null will be replaced by image in preload()
  "emilaia": null,
  "rem": null,
}; 

//------- Variables initialsed above ---------- //

function _preload(){ // de-activated for this demo (by adding underscore)
  // Load the textures based on the names we defined
  for (const name in ballTextures) {
    ballTextures[name] = loadImage(`assets/${name}_chibi.jpg`);
  }
}

function setup() {
  createCanvas(600, 600, WEBGL);
  angleMode(DEGREES);

  for (let i = 0; i < numBalls; i++) {
    balls.push(randomBall()); // Moved ball generation code into separate function
  }

  frameRate(60);
}

function randomBall() { // New function
  let pos = createVector(
    random(-width / 2 + 20, width / 2 - 20),
    random(-height / 2 + 20, height / 2 - 20),
    random(-200, 200)
  );
  let vel = p5.Vector.random3D().mult(random(1, 2));
  let r = random(20, 30);

  // Choose a random name instead of a texture;
  //    we can get the texture from the name later:
  const name = random(Object.keys(ballTextures));

  // Add the name as an argument for the Ball constructor:
  return new Ball(pos, vel, r, ballTextures[name], name); 
}

function draw() {
  background(20);
  orbitControl();
  ambientLight(60, 60, 60);
  directionalLight(255, 255, 255, -0.5, -1, -0.5);

  let lightX = sin(frameCount * 0.01) * 300;
  let lightY = cos(frameCount * 0.01) * 300;
  let lightZ = sin(frameCount * 0.005) * 300;
  pointLight(255, 255, 200, lightX, lightY, lightZ);

  push();
  translate(lightX, lightY, lightZ);
  noStroke();
  emissiveMaterial(255, 255, 200);
  sphere(5);
  pop();

  for (let i = 0; i < balls.length; i++) {
    balls[i].move();
    balls[i].edgeCheck();
    balls[i].drawBall();

    for (let j = i + 1; j < balls.length; j++) {
      balls[i].checkCollision(balls[j]);
      // Perform ball replacement here instead of in Ball class
      if (!balls[i].alive) {
        // Retrieve the name and report its death:
        console.log(balls[i].name, "has died");
        // Replace with a new ball: here we have access to array index:
        balls[i] = randomBall(); // Create the ball as you please...
        console.log(balls[i].name, "was born");
      }
    }
  }
}

class Ball {
  constructor(pos, vel, r, tex, name) { // Added name as parameter
    this.pos = pos;
    this.vel = vel;
    this.r = r;
    this.tex = tex;
    this.name = name; // Added name property
    this.life = 3;
    this.alive = true;
  }

  drawBall() {
    if (!this.alive) return;

    push();
    translate(this.pos.x, this.pos.y, this.pos.z);
    //texture(this.tex); // skip texture for this demo only
    noStroke();
    sphere(this.r);
    pop();
  }

  move() {
    if (!this.alive) return;
    this.pos.add(this.vel);
  }

  edgeCheck() {
    if (!this.alive) return;
    if (this.pos.x + this.r > width / 2 || this.pos.x - this.r < -width / 2) {
      this.vel.x *= -1;
    }
    if (this.pos.y + this.r > height / 2 || this.pos.y - this.r < -height / 2) {
      this.vel.y *= -1;
    }
    if (this.pos.z + this.r > 300 || this.pos.z - this.r < -300) {
      this.vel.z *= -1;
    }
  }

  checkCollision(other) {
    if (!this.alive || !other.alive) return;

    let d = p5.Vector.dist(this.pos, other.pos);
    if (d < this.r + other.r) {

      let temp = this.vel.copy();
      this.vel = other.vel.copy();
      other.vel = temp;

      let overlap = (this.r + other.r - d) / 2;
      let direction = p5.Vector.sub(this.pos, other.pos).normalize();
      this.pos.add(direction.mult(overlap));
      other.pos.sub(direction.mult(overlap));

      this.lifeCounter();
      other.lifeCounter();
    }
  }

  lifeCounter() {
    this.life -= 1;
    this.alive = this.life > 0; // One assignment only (removed rest of code)
  }


}
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare/ajax/libs/p5.js/1.11.1/p5.js"></script>
    <script src="https://cdnjs.cloudflare/ajax/libs/p5.js/1.11.1/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    
  </body>
</html>

I didn't touch, nor verify, other functionality in your code, but this change should answer your question.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744118189a4559272.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信