Why is splice removing all the elements from an array? - javascript

So I am trying to make this game(which I've seen in a video), but I'd like to make it differently, and I am stuck. I have this array with projectiles. Basically, every time a projectile moves out of the screen I'd like to delete that projectile from the array. The problem is when the projectile hits the screen all of the projectiles are being deleted.
The code:
function animate(){
requestAnimationFrame(animate);
c.clearRect(0, 0, width, height);
player.draw();
//shoot on click
addEventListener('click', function(event){
mousex = event.clientX;
mousey = event.clientY;
let angle = Math.atan2(mousey - player.y, mousex - player.x);
projectiledx = Math.cos(angle) * 8;
projectiledy = Math.sin(angle) * 8;
projectileArray.push(new Projectile(width/2, height/2, 10, projectiledx, projectiledy, black));
})
for(let i = 0; i < projectileArray.length; i++){
projectileArray[i].update();
if(projectileArray[i].x + projectileArray[i].radius < 0 || projectileArray[i].x - projectileArray[i].radius >= width){
projectileArray[i].splice(i, 1);
}
if(projectileArray[i].y + projectileArray[i].radius < 0 || projectileArray[i].y - projectileArray[i].radius >= height){
projectileArray[i].splice(i, 1);
}
}
}
animate();

I can see at least two problems here:
there should not be [i] before .splice
You are iterating the array with for loop and whithin that loop you want to modify the length of that array - it looks like a bad idea to me..
Better take a list of items to remove and after that loop ...remove them (begining from the last) in another loop like this:
var removalList = [];
for(let i = 0; i < projectileArray.length; i++){
projectileArray[i].update();
if(
projectileArray[i].x + projectileArray[i].radius < 0 ||
projectileArray[i].x - projectileArray[i].radius >= width ||
projectileArray[i].y + projectileArray[i].radius < 0 ||
projectileArray[i].y - projectileArray[i].radius >= height
){
removalList.push(i);
}
}
for(let i=removalList.length; i>0; i--){
projectileArray.splice( removalList[i-1], 1 );
}

Related

p5.js mouseclick move over Canvas

I am trying to solve a school assignment in p5 JavaScript. I want something to move over the canvas after one mouseclick. But it only moves a little bit and I have to click several times to get it all the way over. What have I done wrong? Shouldn't the loop make it move all the way? Can post the whole code if needed.
function CanvasPressed()
{
if ( mouseX > 0 && mouseX < 638 && mouseY > 0 && mouseY < 100 )
{
Bird.stop();
Bird.play();
for ( let b = 640; b > 0; b--)
{
x = x - 0.05;
}
}
Alright, so you've got a couple misunderstood things, here:
// purely aesthetic but in javascript functions are usually written as (i think) camelCase
// so: canvasPressed() rather than CanvasPressed(), Class-es start with upper case
function CanvasPressed()
{
// you can check for width & height if you want if ( mouseX > 0 && mouseX < width)
if ( mouseX > 0 && mouseX < 638 && mouseY > 0 && mouseY < height )
{
for ( let b = 640; b > 0; b--) // this, in this case, does the same as for(let i = 0; i < width; i ++)
{
x += 0.05
// 0.05 is very little, only a very small part of a pixel
}
// here it moves 0.05 * 640 (0.05 + 0.05 + 0.05 ... )
}
}
javascript naming conventions thingy if you want
and this is how i would make it move through the canvas:
let mouseWasPressed = false;
let x = 20
function draw() {
background(20);
ellipse(x, height / 2, 40)
if(mouseWasPressed) // don't need {} for 1 line after the if()
x ++; // x = x + 1 shortening in javascript
// }
}
function mousePressed(){
mouseWasPressed = true
}
if you don't want the "animation" you could use your previous method, but change the 0.05 to 1:
for(let i = 0; i <= width; i ++) // you don't have to add parentheses for 1 line
x ++; // x = x + 1 just a shortening in javascript
OR just
x = width // or x += width (x = x + width)

Perfect loop frameCount

I'm having trouble figuring out after how many frames the animation completes in order to create a perfect loop. I'm currently recording my canvas in 60 fps. Any ideas?
for (x = 0; x < pg.width; x += 2) {
for (y = 0; y < pg.height; y += 2) {
let csize = map(sin(x+y+frameCount/10), -1, 1, 2, 20);
if (a[x][y] > 0) {
rect(x*2-csize/2, y*2-csize/2, csize);
}
}
}
You should check for when sin(x+y+0/10) equals sin(x+y+frameCount/10) since I'm assuming that's the only place where you use frameCount.

JS Optimization - constantly setting variables on fast fire events

I am working on a script using Three.js where a lot a variables depend on mouse position. Does it matter if I am constantly setting variables to their same value each time the move event fires, or should I only set a variable when a change is detected?
Let's say I want to set a variable "quadrant" to 1,2,3 or 4 depending upon which part of the screen the mouse is over... should I use this :
var quadrant;
function mouseMove(e){
var mouse;
mouse.x = e.clientX;
mouse.y = e.clientY;
if(mouse.x < window.innerWidth / 2){
if(mouse.y < window.innerHeight / 2){
quadrant = 1;
} else {
quadrant = 3;
}
} else {
if(mouse.y < window.innerHeight / 2){
quadrant = 2;
} else {
quadrant = 4;
}
}
};
window.addEventListener('mousemove', mouseMove);
Which will reset the variable every time the event fires. Or should I only be setting variables when a change is detected, like this :
var quadrant;
function mouseMove(e){
var mouse;
mouse.x = e.clientX;
mouse.y = e.clientY;
if(mouse.x < window.innerWidth / 2){
if(mouse.y < window.innerHeight / 2){
if(quadrant != 1){
quadrant = 1;
}
} else {
if(quadrant != 3){
quadrant = 3;
};
}
} else {
if(mouse.y < window.innerHeight / 2){
if(quadrant != 2){
quadrant = 2;
};
} else {
if(quadrant != 4){
quadrant = 4;
};
}
}
};
window.addEventListener('mousemove', mouseMove);
Does the act of setting a variable to the memory (even if it's to the same value) cost more than it takes to read the extra lines of code necessary to add the conditions? I instinctively do the latter as it seems tidier and like less work at runtime, but I really have no idea how this actually translates to performance. I seem to remember reading that each time a variable is set in js that it's actually creating an instance of itself, which seems like work... but maybe I misunderstood.
As noted in the comments, the simpler version is very likely to be faster - and it's easier to read and less error-prone too.
While I've got you, let me suggest a completely different approach: calculate the quadrant instead of using a bunch of if statements.
// Calculate the quadrant for a given x and y and width and height.
// The quadrants are defined like this:
//
// +---+---+
// | 1 | 2 |
// +---+---+
// | 3 | 4 |
// +---+---+
function getQuadrant( x, y, width, height ) {
return 1 +
( x >= width / 2 ) +
( y >= height / 2 ) * 2;
}
console.log( getQuadrant( 25, 25, 100, 100 ) ); // 1
console.log( getQuadrant( 75, 25, 100, 100 ) ); // 2
console.log( getQuadrant( 25, 75, 100, 100 ) ); // 3
console.log( getQuadrant( 75, 75, 100, 100 ) ); // 4
This code works because when you use an arithmetic operator on a boolean value, it converts a false value to 0 and a true value to 1.
I don't know if this will be faster or slower (you would have to benchmark it to find out) but since you are looking at different approaches to solving the problem, I thought you might find it interesting.
You may wonder "aren't those multiplies and divides slow?" But modern JavaScript engines, like most optimizing compilers, can convert multiplies or divides by a power of 2 into a very fast bit-shifting operation.
Let's take a look at the machine code that V8 generates for the getQuadrant function (just showing the core part of the code, not the function setup and teardown).
When we enter this code, the four function parameters are stored in these registers:
r8 is x.
r11 is y.
rdx is width.
rdi is height.
And here's the compiled code:
; Divide height and width by 2 for the comparisons below
sarl rdi, 1
sarl rdx, 1
; Compare y with half the height and set rcx to 0 or 1
cmpl rdi,r11
setlel cl
movzxbl rcx,rcx
; Compare x with half the width and set rdx to 0 or 1
cmpl rdx,r8
setlel dl
movzxbl rdx,rdx
; Set rdx to the final result, calculated in a single instruction
leal rdx,[rdx+rcx*2+0x1]
One likely performance advantage is that this code avoids the branches used by the if statements. On modern CPUs, when you can avoid branches, it is often a performance win.
But again, any of these approaches will likely be more than fast enough! Just posting this alternative method in case it is of interest to you.
If you're curious how I got that machine code listing, I created a standalone JavaScript file called quadrants.js with this content:
function getQuadrant( x, y, width, height ) {
return 1 +
( x >= width / 2 ) +
( y >= height / 2 ) * 2;
}
// We need to actually do something with the result returned by getQuadrant,
// otherwise the JavaScript engine may notice that the result is unused and
// it may skip compiling the function body entirely.
quadrants = [];
for( let i = 0; i < 1000000; ++i ) {
quadrants.push( getQuadrant( 25, 25, 100, 100 ) );
quadrants.push( getQuadrant( 75, 25, 100, 100 ) );
quadrants.push( getQuadrant( 25, 75, 100, 100 ) );
quadrants.push( getQuadrant( 75, 75, 100, 100 ) );
}
// Log the first few results as a sanity check
console.log( quadrants.length );
for( let i = 0; i < 16; ++i ) {
console.log( quadrants[i] );
}
Then I ran it with this command:
node --print-opt-code --code-comments quadrants.js >code.txt
And then I looked through the generated code.txt file to find the code for the getQuadrant function.
Performance wise, they should be very similar. However it really depends on what happens after setting the variable. Are you going to call a function that do hefty work each time? Then you're better of using the second one.
You shouldn't bother yourself with micro-optimizations, A couple milliseconds delay won't really affect your application.
Also if you need to see for yourself here's some benchmark code to run (It won't be that accurate though). It shows the average time in seconds for running each function 1k times
let sum1 = 0, sum2 = 0, quadrant;
for(i = 0; i < 1000; i++){
let obj = calculate(1000);
sum1 += obj.t1;
sum2 += obj.t2;
}
console.log("avg for first: ", sum1 / 1000);
console.log("avg for second: ", sum2 / 1000);
function calculate(numIterations){
//first function
let start = Date.now();
for(let i = 0; i < numIterations; i++){
mouseMove(generateEventObject());
}
let t1 = (Date.now() - start) / 1000;
//second function
start = Date.now();
for(let i = 0; i < numIterations; i++){
mouseMove2(generateEventObject());
}
let t2 = (Date.now() - start) / 1000;
return {t1, t2}
}
function generateRandom(max) {
return Math.random() * max;
}
function generateEventObject() {
return {
clientX: generateRandom(window.innerWidth),
clientY: generateRandom(window.innerHeight)
}
}
function mouseMove(e){
var mouse = {};
mouse.x = e.clientX;
mouse.y = e.clientY;
if(mouse.x < window.innerWidth / 2){
if(mouse.y < window.innerHeight / 2){
quadrant = 1;
} else {
quadrant = 3;
}
} else {
if(mouse.y < window.innerHeight / 2){
quadrant = 2;
} else {
quadrant = 4;
}
}
};
function mouseMove2(e){
var mouse = {};
mouse.x = e.clientX;
mouse.y = e.clientY;
if(mouse.x < window.innerWidth / 2){
if(mouse.y < window.innerHeight / 2){
if(quadrant != 1){
quadrant = 1;
}
} else {
if(quadrant != 3){
quadrant = 3;
};
}
} else {
if(mouse.y < window.innerHeight / 2){
if(quadrant != 2){
quadrant = 2;
};
} else {
if(quadrant != 4){
quadrant = 4;
};
}
}
};

Cannot remove rect upon collision in javascript

I am trying to create a game using javascript. The function drawItems draws 5 rectangles which are meant to resemble coins. When the player collides with the coin thanks to the itemCollision function (which works), the coin should disappear. I have tried different ways of doing this such as assigning a status which should only allow the rect() method to draw if the status is 1. That didn't work. I also tried deleting the array item and that didnt work either. I also tried another way of using an if statement with the clearRect function at the itemX and itemY positions but to no avail. The updateGameArea method is called in a setInterval function with a 20ms timeout and I understand that in the latter the item is cleared however it is redrawn on the next loop however I have no idea how to get around this. Any suggestions please?
let itemWidth = 15;
let itemHeight = 15;
let itemX = 150;
let itemY = 600;
items = {
x:0,
y:0,
status : 1
};
function drawItems() {
for (var i = 1; i < 5; i++) {
itemX = i*100;
items[i] = {x: 0, y: 0, status: 1};
console.log(items[i].status);
if (items[i].status === 1){
items[i].x = itemX;
items[i].y = itemY;
ctx.beginPath();
ctx.rect(itemX, itemY, itemWidth, itemHeight);
ctx.fillStyle = "purple";
ctx.fill();
ctx.closePath();
}
// console.log(itemX + " " + player.x);
}
}
function itemCollision(){
for (var i = 1; i < 5; i++) {
var b = items[i];
if (b.status === 1) {
if (player.x > b.x && player.x < b.x + itemWidth + 10 && player.y > b.y && player.y < b.y + itemHeight) {
b.status = 0;
delete items[i];
}else b.status = 1;
}
}
}
function updateGameArea() {
window.addEventListener("keydown", controller.keyListener);
window.addEventListener("keyup", controller.keyListener);
movePlayer();
// console.log(platform.y + " and " + player.y);
ctx.clearRect(0, 0, gameAreaWidth, gameAreaHeight);
drawItems();
itemCollision();
In the for loop, you set items[i].status to be 1 with this code: items[i] = {x: 0, y: 0, status: 1};. And immediately after, you check if(items[i].status === 1). Of course, the condition of the if statement is always true, so the rectangle always gets drawn.
Keep the array initialization part in a separate function, and call it only once, before the game begins. Do something like this:
const items = []; // empty array
function gameInitialize() { // call it before the game begins
for (let i = 1; i < 5; i++) {
items.push({x: 0, y: 0, status: 1});
}
}
function drawItems() {
for (let i = 1; i < 5; i++) {
itemX = i*100; // this line is a bit suspicious, maybe it does not belong here, I don't know
if (items[i].status === 1) {
items[i].x = itemX;
items[i].y = itemY;
ctx.beginPath();
ctx.rect(itemX, itemY, itemWidth, itemHeight);
ctx.fillStyle = "purple";
ctx.fill();
ctx.closePath();
}
}
}
Also, do not use delete on array items (unless you really know what you're doing, but even then, better not to). To remove the i-th item from an array items, you can use items.splice(i, 1);. However, you don't need to do this with your status setting approach.

Shooting at angle towards last position html5 canvas js

I'm trying to make a game where bullets are shot at a target's last location. The bullets should miss the target unless the target is not moving. Each bullet should continuously move in a single direction using the most recently calculated angle.
for (var z=0; z < this.bullets.length; z++){
var by = enemy1.y - posY;
var bx = enemy1.x - posX;
var fangle = Math.atan2 (by, bx);
velocitiesx[z] = Math.cos(fangle) * 1;
velocitiesy[z] = Math.sin(fangle) * 1;
bullets[z].x += velocitiesx[z] ;
bullets[z].y += velocitiesy[z] ;
}
Here is my problem:
When the target is not moving, the bullets correctly hit the target. However, when the shooter is moving, but the target is still, all of the bullets miss -- but those bullets should all hit the still, non moving target.
I think what is happening is the program keeps calculating the angle for the newest bullet and using that calculated on the older bullets, causing them to change direction. I am not sure how I could make it so that each new bullet follows the most recently calculated angle and keeps moving in that direction.
changed to (still same problem):
function fire(){
counter++;
if (37 in keys && counter >= firerate ) {
var by = enemy1.y - posY;
var bx = enemy1.x - posX;
var fangle = Math.atan2 (by, bx);
velxf = Math.cos(fangle) * 1;
velyf = Math.sin(fangle) * 1;
bullets[bullets.length] = new Box({
x: posX ,
y: posY ,
width: 4,
height: 4,
color: '#7FFF00',
});
counter = 0;
}
}
function movebullets(){
for (var z=0; z < this.bullets.length; z++){
bullets[z].x += velxf ;
bullets[z].y += velyf ;
}
}
calculated angle and velxf and velyf on fire....and made velxf and velyf properties of box as suggested by kokodoko
bullets[bullets.length] = new Box({
x: posX ,
y: posY ,
width: 4,
height: 4,
color: '#7FFF00',
velxb: velxf,
velyb: velyf,
});
function movebullets(){
for (var z=0; z < this.bullets.length; z++){
bullets[z].x += bullets[z].velxb ;
bullets[z].y += bullets[z].velyb ;
}
}

Categories