I wasn't sure how to word this, because I don't actually know what exactly causes this bug. I'm trying to put together a simple Asteroids knockoff.
When the player shoots, a new object (Bullet) is created using array.push(...)
. Once this bullet goes beyond the canvas (out of bounds), it is deleted using array.splice(...);
The problem is that the bullets are moving in unpredictable ways. I don't know how to word it so here's the full code (working, including html/css): https://pastebin.com/tKiSnDzX Hold spacebar for a few seconds (to shoot) and you'll see the issue clearly. You can also use A/D to turn and W to go forward.
Here's what I think is happening. The code runs fine as long as there is only one bullet on the screen (in the array). This means that either the incorrect element is being deleted or the values that go into the constructor of the object are messed up somewhere along the way.
Exhibit A (bullet constructor and its methods):
function Bullet(x,y,rot,vel) {
this.x = x;
this.y = y;
this.rot = rot;
this.vel = (vel+5);
this.move = function() {
this.x += this.vel*Math.cos(this.rot-Math.PI/2);
this.y += this.vel*Math.sin(this.rot-Math.PI/2);
}
this.draw = function() {
engine.circle(this.x, this.y, 4, "black");
var c = engine.canvas.getContext('2d');
c.translate(this.x, this.y);
c.rotate(this.rot);
c.beginPath();
c.strokeStyle="#00FF00";
c.strokeRect(-5, -5, 10, 10);
c.closePath();
c.stroke();
}
}
Exhibit B (function that creates/deletes the bullets):
shoot: function() {
if(engine.keyDown.sp == true) {
if(this.fire > 20) {
engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
this.fire = 0;
} else {
this.fire++
}
}
for(i = 0; i < engine.bullets.length; i++) {
engine.bullets[i].move();
engine.bullets[i].draw();
if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5
|| engine.bullets[i].y > engine.canvas.height+5 || engine.bullets[i].y < -5) {
console.log('bullet gone, '+i);
engine.bullets.splice(i, 1);
}
}
}
the array is declared like so: bullets: []
Thank you for any answers.
The problem was that I was translating the context in which all the bullets are, each time a new bullet was created. This meant that each bullet would be moved by x and y away from the previous one. It made it seem like the bullets were being created where they weren't supposed to be. The "unpredictability" was caused by the fact that the bullet takes on the player's rotation, so whenever a new bullet was created, it's rotation was increased by however much the player rotated before the new bullet was fired, on top of the previous bullet's rotation.
Putting context.save();
before the translation/rotation of the bullet's hitbox and context.restore();
after it perfectly solved the issue:
Bullet.draw = function() {
engine.circle(this.x, this.y, 4, "black");
if(hitbox == true) {
var c = engine.canvas.getContext('2d');
c.save();
c.translate(this.x, this.y);
//c.rotate(this.rot);
c.beginPath();
c.strokeStyle="#00FF00";
c.strokeRect(-5, -5, 10, 10);
c.closePath();
c.stroke();
c.restore();
}
}
Someone else mentioned that I was using array.splice();
in a for loop. This made it so that when a bullet (i) is deleted, the bullet right after (i+1) is moved one index back (i). So that bullet was essentially skipped over.
I could notice this sometimes when looking at the bullets while one was deleted- They "jumped" ahead.
The solution was to put i -= 1
after bullets.splice(i, 1);
. This makes the next iteration of the loop go one index back, solving the occassional stuttering of the bullets:
shoot: function() {
if(engine.keyDown.sp == true) {
if(this.fire > 5) {
engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
this.fire = 0;
}
}
if(this.fire<=5) {
this.fire++
}
for(i = 0; i < engine.bullets.length; i++) {
engine.bullets[i].move();
engine.bullets[i].draw();
if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5
|| engine.bullets[i].y > engine.canvas.height+5 || engine.bullets[i].y < -5) {
engine.bullets.splice(i, 1);
i-=1;
}
}
}
Thanks for all the answers.