簡體   English   中英

嵌套循環:帶有碰撞檢測的錯誤 javascript p5.js

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

我目前正在創建一個小游戲來學習在 p5.js 中編碼。

我面臨的錯誤是,當我嘗試向飛來飛去的彗星 [也是一系列物體] 發射導彈 [物體陣列] 鍵盤箭頭時。 為了了解導彈是否會射中彗星,我創建了一個類方法來比較它們的距離,如果它們的距離小於 ex 20,則導彈和彗星都從它們的陣列中拼接起來。

它隨機工作,有時對 20 顆彗星有效,有時在第一次遇到時滯后。 我懷疑這與我使用的嵌套 for 循環有關。

首先,我在 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)
  };
}

我基本上在我的對象 Mover 中創建了一個名為“destroy”的方法,它指示導彈是否小於 20 像素。

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

然后當我在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.

如果您願意幫助我,將不勝感激! 如果你想玩游戲了解錯誤,你可以用鼠標移動綠色矩形,避開彗星並用鍵盤箭頭射擊它們! 多謝 ! :)

這是整個代碼:

 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>

問題出在導彈/移動器碰撞檢測循環中:

  1. 你正在遍歷所有的“導彈”
  2. 對於每一枚導彈,你都會遍歷所有的推動者
  3. 如果發生碰撞,您將同時移除推進器和導彈
  4. 但是你繼續循環使用 i 的當前值,它現在可能已經超過了數組的末尾!

因此,有時您會收到錯誤消息,因為在隨后通過移動器循環時missil[i]未定義。 每當您更新當前正在循環的數組時,您都需要小心更新索引並在繼續之前重新檢查您的長度。

    // 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!
        }

這是您草圖的固定版本:

 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