嵌套循环:带有冲突检测的Bug 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中,它指示邮件的距离是否小于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 a a 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的当前值,它现在可能已经过了数组的末尾

因此,有时会出现错误,因为在后续通过mover循环时,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 a a 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>

最新更新