1
votes

I've been making a simple game just with the HTML5 canvas and plain JavaScript. Most recently I tried adding a feature to shoot bullets which would then destroy obstacles but I've come upon a problem. Whenever you shoot a bullet it doesn't move until another is shot. Even when it starts moving and collides with one of the obstacles nothing happens. I've done some troubleshooting and I can't seem to get to the bottom of it. Any help and/or suggestions would be greatly appreciated.

Collision handling code:

for(var i = 0; i < this.bullets.length - 1; i++) { // -1 is present because whenever I remove it I get an error. I have a feeling this may be related to the order of how everything updates, will be fixed
    this.bullets[i].x += this.bullets[i].speed;

    if(this.bullets[i].x > cvs.width) {
        this.bullets.splice(i, 1);
    }

    for(var j = 0; j < obstacles.length - 1; j++) { // Same here
        if(rectanglesColliding(this.bullets[i], obstacles[j])) {
             console.log("Collision");
        }
    }
}

Full player object:

var player = {
    score: 0,
    x: 50,
    y: 150,
    radius: 10,
    jumping: false,
    bullets: [],
    velocityX: 0,
    velocityY: 1,
    angle: 90,

    update: function() {
        this.draw();
        this.score++;

        if(this.y + this.radius + 2 > cvs.height - 50 && this.jumping === false || this.y - this.radius + 2 < 0 + 50 && this.jumping === true) {
            this.velocityY = 0;
        } else if(this.jumping === false){
            this.velocityY = 4;
        } else {
            this.velocityY = -4;
        }

        obstacles.forEach((obstacle) => {
            if(colliding(this, obstacle)) {
                gameEnded = true;
            }
        });

        this.x += this.velocityX;
        this.y += this.velocityY;

        for(var i = 0; i < this.bullets.length - 1; i++) { // -1 is present because whenever I remove it I get an error. I have a feeling this may be related to the order of how everything updates, will be fixed
            this.bullets[i].x += this.bullets[i].speed;

            if(this.bullets[i].x > cvs.width) {
                this.bullets.splice(i, 1);
            }

            for(var j = 0; j < obstacles.length - 1; j++) { // Same here
                if(rectanglesColliding(this.bullets[i], obstacles[j])) {
                    console.log("Collision");
                }
            }
        }
    }, 

    draw: function() {
        this.bullets.forEach((bullet) => {
            ctx.fillStyle = 'green';
            ctx.fillRect(bullet.x, bullet.y, 7, 2);
        });

        ctx.fillStyle = 'blue';
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        ctx.fill();
    }
};

Collision Detection Function:

function rectanglesColliding(rect1, rect2) {
    if(rect1.x < rect2.x + rect2.width &&
        rect1.x + rect1.width > rect2.x && // Found on Mozilla.com: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
        rect1.y < rect2.y + rect2.height &&
        rect1.y + rect1.height > rect2.y) return true;
    else return false;
}

Shoot Function:

function shoot() {
    player.bullets.push({ x: player.x + 10, y: player.y, speed: 6 });
}

Object of the game: Survive as long as you can by avoiding the obstacles.

Controls: Space: Go up W: Shoot Bullet

Full code snippet:

var cvs = document.getElementById('canvas');
var ctx = cvs.getContext("2d");
var gameEnded = false;

var player = {
    score: 0,
    x: 50,
    y: 150,
    radius: 10,
    jumping: false,
    bullets: [],
    velocityX: 0,
    velocityY: 1,
    angle: 90,

    update: function() {
        this.draw();
        this.score++;

        if(this.y + this.radius + 2 > cvs.height - 50 && this.jumping === false || this.y - this.radius + 2 < 0 + 50 && this.jumping === true) {
            this.velocityY = 0;
        } else if(this.jumping === false){
            this.velocityY = 4;
        } else {
            this.velocityY = -4;
        }

        obstacles.forEach((obstacle) => {
            if(colliding(this, obstacle)) {
                gameEnded = true;
            }
        });

        this.x += this.velocityX;
        this.y += this.velocityY;

        for(var i = 0; i < this.bullets.length - 1; i++) {
            this.bullets[i].x += this.bullets[i].speed;

            if(this.bullets[i].x > cvs.width) {
                this.bullets.splice(i, 1);
            }

            for(var j = 0; j < obstacles.length - 1; j++) {
                if(rectanglesColliding(this.bullets[i], obstacles[j])) {
                    console.log("Collision");
                    // obstacles.splice(i, 1);
                }
            }
        }
    }, 

    draw: function() {
        this.bullets.forEach((bullet) => {
            ctx.fillStyle = 'green';
            ctx.fillRect(bullet.x, bullet.y, 7, 2);
        });

        ctx.fillStyle = 'blue';
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        ctx.fill();
    }
};

var obstacles = [{ x: 150, y: 60, width: 10, height: 20 }];

document.addEventListener('keydown', (event) => {
    if(event.key === ' ') {
        player.jumping = true;
    }
    if(event.key === 'w') {
        shoot();
    }
});
document.addEventListener('keyup', (event) => {
    if(event.key === ' ') {
        player.jumping = false;
    }
});

function randomIntFromRange(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function getWidth() {
    return Math.max(
        document.body.scrollWidth,
        document.documentElement.scrollWidth,
        document.body.offsetWidth,
        document.documentElement.offsetWidth,
        document.documentElement.clientWidth
    );
}

function getHeight() {
    return Math.max(
        document.body.scrollHeight,
        document.documentElement.scrollHeight,
        document.body.offsetHeight,
        document.documentElement.offsetHeight,
        document.documentElement.clientHeight
    );
}

function colliding(circle, rect) {
    var distX = Math.abs(circle.x - rect.x - rect.width / 2);
    var distY = Math.abs(circle.y - rect.y - rect.height / 2);

    if(distX > (rect.width / 2 + circle.radius)) return false;
    if(distY > (rect.height / 2 + circle.radius)) return false;

    if(distX <= (rect.width / 2)) return true;
    if(distY <= (rect.height / 2)) return true;
}

function rectanglesColliding(rect1, rect2) {
    if(rect1.x < rect2.x + rect2.width &&
        rect1.x + rect1.width > rect2.x &&
        rect1.y < rect2.y + rect2.height &&
        rect1.y + rect1.height > rect2.y) {
            return true;
    }
}

function distance(x1, y1, x2, y2) {
    const xDist = x2 - x1;
    const yDist = y2 - y1;

    return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2));
}

function shoot() {
    player.bullets.push({ x: player.x + 10, y: player.y, speed: 6 });
}

function generateObstacle() {
    var obstacle = {
        x: obstacles[0].x + randomIntFromRange(cvs.width - 100, cvs.width + 70),
        y: 0 + randomIntFromRange(20, 550),
        width: randomIntFromRange(25, 35),
        height: randomIntFromRange(10, 25)
    };

    for(var i = 0; i < obstacles.length; i++) {
        if(distance(obstacle.x, obstacle.y, obstacles[i].x, obstacles[i].y) < 65) {
            obstacle = {
                x: obstacles[0].x + randomIntFromRange(cvs.width - 100, cvs.width + 70),
                y: 0 + randomIntFromRange(20, 550),
                width: randomIntFromRange(25, 35),
                height: randomIntFromRange(10, 25)
            };

            i = -1;
        }
    }

    obstacles.push({ x: obstacle.x, y: obstacle.y, width: obstacle.width, height: obstacle.height });
}

function removeBadObstacles() {
    for(var i = 0; i < obstacles.length; i++) {
        if(obstacles[i].x < 0) {
            obstacles.splice(i, 1);
        }
    }
}

function updateObstacles() {
    if(obstacles[obstacles.length - 1].x < cvs.width + 5) {
        generateObstacle();
        generateObstacle();
    }

    obstacles.forEach((obstacle) => {
        obstacle.x -= 10;
        ctx.fillStyle = 'red';
        ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
    });
}

function init() {
    cvs.width = getWidth();
    cvs.height = getHeight();
    update();
}

function update() {
    if(gameEnded) {
        ctx.font = '50px Verdana';
        return ctx.fillText('Game Over', 110, cvs.height / 2);
    }
    requestAnimationFrame(update);

    removeBadObstacles();
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, cvs.width, cvs.height);
    updateObstacles();
    player.update();
    ctx.font = '30px Impact';
    ctx.fillText("Score: " + player.score, 20, 40);
}

init();
<!DOCTYPE html>
<html>
    <head>
        <title>Game</title>
    </head>
    <body>
        <canvas id="canvas"></canvas>

        <script type="text/javascript" src="./JS/main.js"></script>
    </body>
</html>
1
1. i < this.bullets.length 2. your bullet object has no width.apple apple
@appleapple Thanks! I needed somebody besides myself to run over the code.Kenzoid

1 Answers

1
votes
  • when iterate through a array, the last item is at length-1 (which mean you should use < length, not < length-1)
  • the remove code should be obstacles.splice(j, 1); (i is the index for bullet)
  • you do not assign size to bullet (and you may also want to use this size when draw)

var cvs = document.getElementById('canvas');
var ctx = cvs.getContext("2d");
var gameEnded = false;

var player = {
    score: 0,
    x: 50,
    y: 150,
    radius: 10,
    jumping: false,
    bullets: [],
    velocityX: 0,
    velocityY: 1,
    angle: 90,

    update: function() {
        this.draw();
        this.score++;

        if(this.y + this.radius + 2 > cvs.height - 50 && this.jumping === false || this.y - this.radius + 2 < 0 + 50 && this.jumping === true) {
            this.velocityY = 0;
        } else if(this.jumping === false){
            this.velocityY = 4;
        } else {
            this.velocityY = -4;
        }

        obstacles.forEach((obstacle) => {
            if(colliding(this, obstacle)) {
                gameEnded = true;
            }
        });

        this.x += this.velocityX;
        this.y += this.velocityY;

        for(var i = 0; i < this.bullets.length; i++) {//<==============
            this.bullets[i].x += this.bullets[i].speed;

            if(this.bullets[i].x > cvs.width) {
                this.bullets.splice(i, 1);
            }

            for(var j = 0; j < obstacles.length; j++) {//<==============
                if(rectanglesColliding(this.bullets[i], obstacles[j])) {
                    console.log("Collision");
                    obstacles.splice(j, 1);//<==============
                }
            }
        }
    }, 

    draw: function() {
        this.bullets.forEach((bullet) => {
            ctx.fillStyle = 'green';
            ctx.fillRect(bullet.x, bullet.y, 7, 2);
        });

        ctx.fillStyle = 'blue';
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        ctx.fill();
    }
};

var obstacles = [{ x: 150, y: 60, width: 10, height: 20 }];

document.addEventListener('keydown', (event) => {
    if(event.key === ' ') {
        player.jumping = true;
    }
    if(event.key === 'w') {
        shoot();
    }
});
document.addEventListener('keyup', (event) => {
    if(event.key === ' ') {
        player.jumping = false;
    }
});

function randomIntFromRange(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function getWidth() {
    return Math.max(
        document.body.scrollWidth,
        document.documentElement.scrollWidth,
        document.body.offsetWidth,
        document.documentElement.offsetWidth,
        document.documentElement.clientWidth
    );
}

function getHeight() {
    return Math.max(
        document.body.scrollHeight,
        document.documentElement.scrollHeight,
        document.body.offsetHeight,
        document.documentElement.offsetHeight,
        document.documentElement.clientHeight
    );
}

function colliding(circle, rect) {
    var distX = Math.abs(circle.x - rect.x - rect.width / 2);
    var distY = Math.abs(circle.y - rect.y - rect.height / 2);

    if(distX > (rect.width / 2 + circle.radius)) return false;
    if(distY > (rect.height / 2 + circle.radius)) return false;

    if(distX <= (rect.width / 2)) return true;
    if(distY <= (rect.height / 2)) return true;
}

function rectanglesColliding(rect1, rect2) {
    if(rect1.x < rect2.x + rect2.width &&
        rect1.x + rect1.width > rect2.x &&
        rect1.y < rect2.y + rect2.height &&
        rect1.y + rect1.height > rect2.y) {
            return true;
    }
}

function distance(x1, y1, x2, y2) {
    const xDist = x2 - x1;
    const yDist = y2 - y1;

    return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2));
}

function shoot() {
    player.bullets.push({ x: player.x + 10, y: player.y, speed: 6, width: 7, height: 6  }); //<====================
}

function generateObstacle() {
    var obstacle = {
        x: obstacles[0].x + randomIntFromRange(cvs.width - 100, cvs.width + 70),
        y: 0 + randomIntFromRange(20, 550),
        width: randomIntFromRange(25, 35),
        height: randomIntFromRange(10, 25)
    };

    for(var i = 0; i < obstacles.length; i++) {
        if(distance(obstacle.x, obstacle.y, obstacles[i].x, obstacles[i].y) < 65) {
            obstacle = {
                x: obstacles[0].x + randomIntFromRange(cvs.width - 100, cvs.width + 70),
                y: 0 + randomIntFromRange(20, 550),
                width: randomIntFromRange(25, 35),
                height: randomIntFromRange(10, 25)
            };

            i = -1;
        }
    }

    obstacles.push({ x: obstacle.x, y: obstacle.y, width: obstacle.width, height: obstacle.height });
}

function removeBadObstacles() {
    for(var i = 0; i < obstacles.length; i++) {
        if(obstacles[i].x < 0) {
            obstacles.splice(i, 1);
        }
    }
}

function updateObstacles() {
    if(obstacles[obstacles.length - 1].x < cvs.width + 5) {
        generateObstacle();
        generateObstacle();
    }

    obstacles.forEach((obstacle) => {
        obstacle.x -= 10;
        ctx.fillStyle = 'red';
        ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
    });
}

function init() {
    cvs.width = getWidth();
    cvs.height = getHeight();
    update();
}

function update() {
    if(gameEnded) {
        ctx.font = '50px Verdana';
        return ctx.fillText('Game Over', 110, cvs.height / 2);
    }
    requestAnimationFrame(update);

    removeBadObstacles();
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, cvs.width, cvs.height);
    updateObstacles();
    player.update();
    ctx.font = '30px Impact';
    ctx.fillText("Score: " + player.score, 20, 40);
}

init();
<!DOCTYPE html>
<html>
    <head>
        <title>Game</title>
    </head>
    <body>
        <canvas id="canvas"></canvas>

        <script type="text/javascript" src="./JS/main.js"></script>
    </body>
</html>