简体   繁体   English

嵌套循环:带有碰撞检测的错误 javascript p5.js

[英]Nested for loops: Bug with collision detection javascript p5.js

I'm currently creating a little game to learn to code in p5.js.我目前正在创建一个小游戏来学习在 p5.js 中编码。

The bug I'm facing is when I try to shoot missils[array of object] the Keyboard Arrow at comets that fly around [also an array of objects].我面临的错误是,当我尝试向飞来飞去的彗星 [也是一系列物体] 发射导弹 [物体阵列] 键盘箭头时。 In order to understand if the missils would shoot the comet, I created a class method that compare their distance and if their distance is less than for ex 20, both the missil and the comets are getting spliced from their array.为了了解导弹是否会射中彗星,我创建了一个类方法来比较它们的距离,如果它们的距离小于 ex 20,则导弹和彗星都从它们的阵列中拼接起来。

It works randomly, sometimes it works for 20 comets and sometimes it lags at the first encounter.它随机工作,有时对 20 颗彗星有效,有时在第一次遇到时滞后。 I suspect that this has to do with the nested for loops that I'm using.我怀疑这与我使用的嵌套 for 循环有关。

First of all I'm creating 2 objects MOVER every 5 seconds in the Setup function and store them into an array.首先,我在 Setup 函数中每 5 秒创建 2 个对象 MOVER 并将它们存储到数组中。

for(let i=0; i<2; i++) { 
  {
    setInterval(
      function(){
        movertest = new Mover(random(400, width), random(400, height));
        mover.push(movertest);
      },
      5000)
  };
}

I basically created then a method called "destroy" in my object Mover, which indicates whether or not a missil is less then 20 pixels away.我基本上在我的对象 Mover 中创建了一个名为“destroy”的方法,它指示导弹是否小于 20 像素。

destroy(px, py) {
  if( dist(this.position.x, this.position.y, px, py) <= 20) {
    return true
  } else {
    return false;
  }
}

and then when I summon those functions in the draw functions of P5.js, it is lagging.然后当我在P5.js的draw函数中调用这些函数时,它是滞后的。

for (i=0; i < missil.length; i++) {
  missil[i].show();
  missil[i].update();
  for (p=0; p < planets.length; p++) {
    missil[i].bounce(planets[p].position.x, planets[p].position.y, planets[p].lrect/2)
  };

  //if missil is in the canvas then check for collision
  if (missil[i].contains(width/2,height/2)) {
    for (let u = 0; u < mover.length; u++) {
      if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) {
        mover.splice(u,1);
        missil.splice(i,1);
        console.log(u);
        console.log(i)
      };
    }
  } else {
    missil.splice(i,1)
  }
} // if missil is out of the canvas it get erased from the array.

If you would like to help me that would be super appreciated!如果您愿意帮助我,将不胜感激! If you want to play the game to understand the error, you can move green rectangle with your mouse, and avoid the comets and shoot them with the Keyboard arrow!如果你想玩游戏了解错误,你可以用鼠标移动绿色矩形,避开彗星并用键盘箭头射击它们! thanks a lot !多谢 ! :) :)

Here is the entire code:这是整个代码:

 let mover = []; let x = 0; let collision = 0; //counter to know when to end the game let collu = 0; let lrect = 10; //size of the ship. let accslider, velslider; let shoot = false; //to know if to shoot a missil. let started = true; let movertest = []; //number of missil. let missil = []; let outofcanvas; let a = 0; let b = 0; let munitions = 100; let planets = []; let cron; function windowalert() { if (confirm("Votre score est de rejouer?")) { location.reload() } else { location.close(); } } function setup() { background(0, 1); cnv = createCanvas(900, 600); for (let i = 0; i < 2; i++) { setInterval(function() { movertest = new Mover(random(400, width), random(400, height)); mover.push(movertest); }, 5000) } for (p = 0; p < 4; p++) { planets[p] = new Planet(100, 100, 20 + p * 20, 20 + p * 20) } ship = new Ship(300, 300); accslider = createSlider(0, 255, 100); accslider.position(width + 20, 20); // noLoop(); // putted here since loop is when pressed the button start => function } function draw() { text(munitions, 80, 20); if (started) { background(0, 50); for (let i = 0; i < mover.length; i++) { mover[i].show(); mover[i].update(x); mover[i].edge(); // if rollover/ contains is true => then change collision +1 => collsison arrives at 255=> you are dead. if (mover[i].contains(mouseX, mouseY)) { collision += 1; lrect += 0.04; } } if (collision >= 255) { clearInterval(cron); started = false; windowalert() } ship.move(mouseX, mouseY); //ship.impact(mover[i].position.x,mover[i].position.y) ship.show(); ship.edge(); noCursor(); x += 0.00005; for (p = 0; p < planets.length; p++) { planets[p].show(50 + p * 10, 180 + p * 40, 120 + p * 10); planets[p].move(); } for (i = 0; i < missil.length; i++) { missil[i].show(); missil[i].update(); for (p = 0; p < planets.length; p++) { missil[i].bounce(planets[p].position.x, planets[p].position.y, planets[p].lrect / 2) } //if missil is in the canvas then check for collision if (missil[i].contains(width / 2, height / 2)) { for (let u = 0; u < mover.length; u++) { if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) { mover.splice(u, 1); missil.splice(i, 1); console.log(u); console.log(i) }; } } else { missil.splice(i, 1) } } } } // if missil is out of the canvas it get erased from the array. setInterval(function() { if (munitions < 100) { munitions += 1 } }, 1000); function keyPressed() { if (munitions > 0) { if (keyIsDown(LEFT_ARROW)) { a = -1; munitions += -1 } // a is in the class munitions and represent the vector x. I did it like that so when we click both arrow it goes in diagonal. if (keyIsDown(RIGHT_ARROW)) { a = 1; munitions += -1 } if (keyIsDown(UP_ARROW)) { b = -1; munitions += -1 } if (keyIsDown(DOWN_ARROW)) { b = 1; munitions += -1 } for (let u = 0; u < 1; u++) { let mi = new Missil(mouseX, mouseY, a, b); missil.push(mi); } } } function keyReleased() { if (keyCode == LEFT_ARROW) { a = 0 } // this was implemented to reput the missil vector at the default value when released the key. if (keyCode == RIGHT_ARROW) { a = 0 } if (keyCode == UP_ARROW) { b = 0 } if (keyCode == DOWN_ARROW) { b = 0 } } class Missil { constructor(x, y, a, b) //partira de mx,my. { this.position = createVector(x, y); this.vel = createVector(a, b); this.vel.mult(random(2, 4)) } update() { this.position = this.position.add(this.vel); } show() { stroke(255); noStroke(); fill(255, 0, 0, 100); ellipse(this.position.x, this.position.y, 5); } contains(px, py) { if (dist(this.position.x, this.position.y, px, py) < width / 2) { return true } else { return false } } bounce(px, py, dista) { { if (dist(this.position.x, this.position.y, px, py) < dista) { let v = createVector(this.position.x - px, this.position.y - py); this.vel = this.vel.mult(1.5).reflect(v) } } } } class Planet { constructor(x, y, lrect, lrect2) { this.position = createVector(x, y) this.vel = p5.Vector.random2D(); this.lrect = lrect; this.lrect2 = lrect2; } show(r, g, b) { fill(r, g, b); noStroke(); ellipseMode(CENTER); ellipse(this.position.x, this.position.y, this.lrect, this.lrect2) stroke(255); } move() { let center = createVector(width / 2, height / 2) this.gravityacc = p5.Vector.sub(center, this.position); this.gravityacc.setMag(0.004); this.vel = this.vel.add(this.gravityacc); this.position = this.position.add(this.vel); this.vel.limit(1.3); } } class Mover { //those are the comets constructor(x, y) { this.position = createVector(x, y); this.vel = p5.Vector.random2D(); this.vel.mult(random(3)); //this.acc=p5.Vector.random2D();// acceleeration is aa random vector here. // this.acc.setMag(0.01); // magnitude of acceleration is slow. Acceleration is a vector. // this.vel.limit(3); // shrinks size of vector to 5, but if it smaller then 5 then it doent equal to 5 like in setMag(). } update(speed) { setInterval(function() { this.speed += 0.01 }, 3000); let mouse = createVector(mouseX, mouseY); this.acc = p5.Vector.sub(mouse, this.position); this.acc.setMag(0.04) this.acc.limit(0.1) this.vel.add(this.acc); /// add acceleration to velocitiy. this.position.add(this.vel); this.vel.limit(5); } show() { stroke(255, 10); strokeWeight(0); fill(map(this.position.x, 0, width, 0, 255), map(this.position.y, 0, height, 0, 255), 255, 255); ellipse(this.position.x, this.position.y, 5) } edge() { if (this.position.x >= width) { let n = createVector(-1, 0); this.vel = this.vel.reflect(n) } if (this.position.x <= 0) { let n = createVector(1, 0); this.vel = this.vel.reflect(n) } if (this.position.y >= height) { let n = createVector(0, -1); this.vel = this.vel.reflect(n) } if (this.position.y <= 0) { let n = createVector(0, 1); this.vel = this.vel.reflect(n) } } contains(px, py) { if (dist(this.position.x, this.position.y, px, py) < 5 + lrect) { return true } else { return false } } destroy(px, py) { if (dist(this.position.x, this.position.y, px, py) <= 20) { return true } else { return false; } } } class Ship { constructor(x, y) { this.position = createVector(x, y); this.vel = createVector(); this.acc = createVector(); } move(px, py) { this.position.x = px; this.position.y = py } //if key pressed. edge() { if (this.position.x >= width) { this.position.x = width } if (this.position.y >= height - 50) { this.position.y = height - 50 } } show() { stroke(255); strokeWeight(0); fill(collision * 1, 255 - collision * 2, 0, 100); rect(this.position.x, this.position.y, lrect, lrect) } } /* Without the HTML this isn't functional "use strict"; document.form_main.start.onclick = () => start(); document.form_main.pause.onclick = () => pause(); document.form_main.reset.onclick = () => reset(); function start() { pause(); cron = setInterval(() => { timer(); }, 10); started = true; // to indicate to start draw loop(); // noLoop in fucntion setup. } function pause() { clearInterval(cron); started = false; } function reset() { location.reload(); } function timer() { if ((millisecond += 10) == 1000) { millisecond = 0; second++; } if (second == 60) { second = 0; minute++; } if (minute == 60) { minute = 0; hour++; } document.getElementById('hour').innerText = returnData(hour); document.getElementById('minute').innerText = returnData(minute); document.getElementById('second').innerText = returnData(second); document.getElementById('millisecond').innerText = returnData(millisecond); } function returnData(input) { return input > 10 ? input : `0${input}` } */
 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>

The problem is in the the missil/mover collision detection loop:问题出在导弹/移动器碰撞检测循环中:

  1. You are looping through all of the "missils"你正在遍历所有的“导弹”
  2. For each missil you loop through all the movers对于每一枚导弹,你都会遍历所有的推动者
  3. In the event of a collision you remove both the mover and the missil如果发生碰撞,您将同时移除推进器和导弹
  4. But you continue looping with the current value of i, which may now be past the end of the array!但是你继续循环使用 i 的当前值,它现在可能已经超过了数组的末尾!

As a result you are getting an error sometimes because on a subsequent pass through the mover loop missil[i] is undefined.因此,有时您会收到错误消息,因为在随后通过移动器循环时missil[i]未定义。 Any time you update an array you are currently looping over you need to be careful to update your indices and re-check your length before continuing.每当您更新当前正在循环的数组时,您都需要小心更新索引并在继续之前重新检查您的长度。

    // 1. You are looping through all of the "missils"
    for (i = 0; i < missil.length; i++) {
        // ...
        
        // 2. For each missil you loop through all the movers
        for (let u = 0; u < mover.length; u++) {
          if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) {
            // 3. In the event of a collision you remove both the mover and the missil
            mover.splice(u, 1);
            missil.splice(i, 1);
            console.log(u);
            console.log(i)
          }

          // 4. But you continue looping with the current value of i, which may now be past the end of the array!
        }

Here is a fixed version of your sketch:这是您草图的固定版本:

 let mover = []; let x = 0; let collision = 0; //counter to know when to end the game let collu = 0; let lrect = 10; //size of the ship. let accslider, velslider; let shoot = false; //to know if to shoot a missil. let started = true; let movertest = []; //number of missil. let missil = []; let outofcanvas; let a = 0; let b = 0; let munitions = 100; let planets = []; let cron; function windowalert() { if (confirm("Votre score est de rejouer?")) { location.reload() } else { location.close(); } } function setup() { background(0, 1); cnv = createCanvas(900, 600); for (let i = 0; i < 2; i++) { setInterval(function() { movertest = new Mover(random(400, width), random(400, height)); mover.push(movertest); }, 5000) } for (p = 0; p < 4; p++) { planets[p] = new Planet(100, 100, 20 + p * 20, 20 + p * 20) } ship = new Ship(300, 300); accslider = createSlider(0, 255, 100); accslider.position(width + 20, 20); // noLoop(); // putted here since loop is when pressed the button start => function } function draw() { text(munitions, 80, 20); if (started) { background(0, 50); for (let i = 0; i < mover.length; i++) { mover[i].show(); mover[i].update(x); mover[i].edge(); // if rollover/ contains is true => then change collision +1 => collsison arrives at 255=> you are dead. if (mover[i].contains(mouseX, mouseY)) { collision += 1; lrect += 0.04; } } if (collision >= 255) { clearInterval(cron); started = false; windowalert() } ship.move(mouseX, mouseY); //ship.impact(mover[i].position.x,mover[i].position.y) ship.show(); ship.edge(); noCursor(); x += 0.00005; for (p = 0; p < planets.length; p++) { planets[p].show(50 + p * 10, 180 + p * 40, 120 + p * 10); planets[p].move(); } for (i = 0; i < missil.length; i++) { missil[i].show(); missil[i].update(); for (p = 0; p < planets.length; p++) { missil[i].bounce(planets[p].position.x, planets[p].position.y, planets[p].lrect / 2) } //if missil is in the canvas then check for collision if (missil[i].contains(width / 2, height / 2)) { let colission = false; for (let u = 0; u < mover.length; u++) { if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) { mover.splice(u, 1); missil.splice(i, 1); // Exit the mover loop immediately colission = true; break; } } if (colission) { // because we've deleted the item at i, the item that was at // i + 1 is now at i, so in order not to skip that item we // need to decrement i before continuing i--; } } else { missil.splice(i, 1); i--; } } } } // if missil is out of the canvas it get erased from the array. setInterval(function() { if (munitions < 100) { munitions += 1 } }, 1000); function keyPressed() { if (munitions > 0) { if (keyIsDown(LEFT_ARROW)) { a = -1; munitions += -1 } // a is in the class munitions and represent the vector x. I did it like that so when we click both arrow it goes in diagonal. if (keyIsDown(RIGHT_ARROW)) { a = 1; munitions += -1 } if (keyIsDown(UP_ARROW)) { b = -1; munitions += -1 } if (keyIsDown(DOWN_ARROW)) { b = 1; munitions += -1 } for (let u = 0; u < 1; u++) { let mi = new Missil(mouseX, mouseY, a, b); missil.push(mi); } } } function keyReleased() { if (keyCode == LEFT_ARROW) { a = 0 } // this was implemented to reput the missil vector at the default value when released the key. if (keyCode == RIGHT_ARROW) { a = 0 } if (keyCode == UP_ARROW) { b = 0 } if (keyCode == DOWN_ARROW) { b = 0 } } class Missil { constructor(x, y, a, b) //partira de mx,my. { this.position = createVector(x, y); this.vel = createVector(a, b); this.vel.mult(random(2, 4)) } update() { this.position = this.position.add(this.vel); } show() { stroke(255); noStroke(); fill(255, 0, 0, 100); ellipse(this.position.x, this.position.y, 5); } contains(px, py) { if (dist(this.position.x, this.position.y, px, py) < width / 2) { return true } else { return false } } bounce(px, py, dista) { { if (dist(this.position.x, this.position.y, px, py) < dista) { let v = createVector(this.position.x - px, this.position.y - py); this.vel = this.vel.mult(1.5).reflect(v) } } } } class Planet { constructor(x, y, lrect, lrect2) { this.position = createVector(x, y) this.vel = p5.Vector.random2D(); this.lrect = lrect; this.lrect2 = lrect2; } show(r, g, b) { fill(r, g, b); noStroke(); ellipseMode(CENTER); ellipse(this.position.x, this.position.y, this.lrect, this.lrect2) stroke(255); } move() { let center = createVector(width / 2, height / 2) this.gravityacc = p5.Vector.sub(center, this.position); this.gravityacc.setMag(0.004); this.vel = this.vel.add(this.gravityacc); this.position = this.position.add(this.vel); this.vel.limit(1.3); } } class Mover { //those are the comets constructor(x, y) { this.position = createVector(x, y); this.vel = p5.Vector.random2D(); this.vel.mult(random(3)); //this.acc=p5.Vector.random2D();// acceleeration is aa random vector here. // this.acc.setMag(0.01); // magnitude of acceleration is slow. Acceleration is a vector. // this.vel.limit(3); // shrinks size of vector to 5, but if it smaller then 5 then it doent equal to 5 like in setMag(). } update(speed) { setInterval(function() { this.speed += 0.01 }, 3000); let mouse = createVector(mouseX, mouseY); this.acc = p5.Vector.sub(mouse, this.position); this.acc.setMag(0.04) this.acc.limit(0.1) this.vel.add(this.acc); /// add acceleration to velocitiy. this.position.add(this.vel); this.vel.limit(5); } show() { stroke(255, 10); strokeWeight(0); fill(map(this.position.x, 0, width, 0, 255), map(this.position.y, 0, height, 0, 255), 255, 255); ellipse(this.position.x, this.position.y, 5) } edge() { if (this.position.x >= width) { let n = createVector(-1, 0); this.vel = this.vel.reflect(n) } if (this.position.x <= 0) { let n = createVector(1, 0); this.vel = this.vel.reflect(n) } if (this.position.y >= height) { let n = createVector(0, -1); this.vel = this.vel.reflect(n) } if (this.position.y <= 0) { let n = createVector(0, 1); this.vel = this.vel.reflect(n) } } contains(px, py) { if (dist(this.position.x, this.position.y, px, py) < 5 + lrect) { return true } else { return false } } destroy(px, py) { if (dist(this.position.x, this.position.y, px, py) <= 20) { return true } else { return false; } } } class Ship { constructor(x, y) { this.position = createVector(x, y); this.vel = createVector(); this.acc = createVector(); } move(px, py) { this.position.x = px; this.position.y = py } //if key pressed. edge() { if (this.position.x >= width) { this.position.x = width } if (this.position.y >= height - 50) { this.position.y = height - 50 } } show() { stroke(255); strokeWeight(0); fill(collision * 1, 255 - collision * 2, 0, 100); rect(this.position.x, this.position.y, lrect, lrect) } } /* Without the HTML this isn't functional "use strict"; document.form_main.start.onclick = () => start(); document.form_main.pause.onclick = () => pause(); document.form_main.reset.onclick = () => reset(); function start() { pause(); cron = setInterval(() => { timer(); }, 10); started = true; // to indicate to start draw loop(); // noLoop in fucntion setup. } function pause() { clearInterval(cron); started = false; } function reset() { location.reload(); } function timer() { if ((millisecond += 10) == 1000) { millisecond = 0; second++; } if (second == 60) { second = 0; minute++; } if (minute == 60) { minute = 0; hour++; } document.getElementById('hour').innerText = returnData(hour); document.getElementById('minute').innerText = returnData(minute); document.getElementById('second').innerText = returnData(second); document.getElementById('millisecond').innerText = returnData(millisecond); } function returnData(input) { return input > 10 ? input : `0${input}` } */
 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM