Create bullets for a game at an interval - javascript

I have a game, and in it there is a main, controllable character and then enemies that shoot back at the character. For the enemies, when they shoot back I want them to shoot at intervals so that it isn't just one massive block of bullets, and it worked with a setInterval for one, but when a second enemy comes in they don't shoot. Only one of the two will. If anybody has a solution that would be great!
function enemies() {
if (enemy_soldiers.length == 0) {
level += 0.2;
for (var i = 0; i<(1 + Math.floor(Math.round(level))); i++) {
var gx = 1450
var gy = getRandom(430, 630);
enemy_soldiers.push({
x: gx,
y: gy,
l: gl,
d: getRandom(350, 600),
shooting: false,
interval: setInterval (function() {enemy.shooting = true;},fire_rate),
shoot: function() {
enemy_bullets.push({
x: enemy.x+40,
y: enemy.y+87,
vel: 10,
});
}
});
}
}
var enemy;
gctx.clearRect(0, 0, 1400, 800);
for (var i in enemy_soldiers) {
enemy = enemy_soldiers[i];
drawenemy(enemy.x, enemy.y, enemy.l);
//ai
if (distance(enemy.x, enemy.y, cx, cy) >= enemy.d && enemy.x>cx) {
enemy.x-=vel;
}
else if (distance(enemy.x, enemy.y, cx, cy) >= enemy.d && enemy.x<cx) {
enemy.x+=vel;
}
if (distance(enemy.x, enemy.y, cx, cy) <= 600) {
if (enemy.shooting == true) {
enemy.shoot(enemy.x,enemy.y);
enemy.shooting = false;
}
gbctx.clearRect(0, 0, 1400, 800);
for (var j in enemy_bullets) {
enemy_bullet = enemy_bullets[j];
enemy_bullet.x -= enemy_bullet.vel;
if (enemy_bullet.x > 1400 || enemy_bullet.x < -5 || enemy_bullet.y > 800 || enemy_bullet.y < -5) {
enemy_bullets.splice(j,1);
}
drawEnemyBullet(enemy_bullet.x, enemy_bullet.y);
}
}
}}

This solution relies on the function Date.now() which returns the current time in milliseconds. It's possible for each enemy to keep track of when they have to fire next and each one can have a different delay between shots.
I've only added a graphic to show when they are firing their weapons, but this can be easily altered to something like a raycast or spawn a projectile particle.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
border: solid 1px white;
border-radius: 1px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// Enemy constructor
function Enemy(x,y) {
this.x = x;
this.y = y;
this.isFiring = false;
this.fireDelay = (500 + Math.random() * 500) | 0; // Time between shots
this.lastFired = Date.now(); // Last time at which the enemy fired
}
/*
Enemy prototype
(All objects created using the constructor share these properties)
E.G.
var e1 = new Enemy(10,0);
var e2 = new Enemy(20,0);
if (e1.__proto__ === e2.__proto__) {
console.log("Match");
}
prints "Match"
*/
Enemy.prototype = {
WIDTH: 10,
HEIGHT: 20,
FIRE_DURATION: 100, // Amount of time 'fire' graphic is shown
FIRE_WIDTH: 10,
FIRE_HEIGHT: 10,
update: function() {
// If current time - time when I last fired > the amount of time between shots
if (Date.now() - this.lastFired > this.fireDelay) {
this.lastFired = Date.now();
this.isFiring = true;
// If you were using projectile particles, this is where you would spawn one
}
if (this.isFiring && Date.now() - this.lastFired > this.FIRE_DURATION) {
this.isFiring = false;
}
},
render: function(ctx) {
ctx.fillStyle = "darkred";
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.rect(
this.x,
this.y,
this.WIDTH,
this.HEIGHT
);
ctx.fill();
ctx.stroke();
if (this.isFiring) {
ctx.fillStyle = "yellow";
ctx.strokeStyle = "darkyellow";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.rect(
this.x + this.WIDTH,
this.y + this.HEIGHT * 0.5 - this.FIRE_HEIGHT * 0.5,
this.FIRE_WIDTH,
this.FIRE_HEIGHT
);
ctx.fill();
ctx.stroke();
}
}
};
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
var enemies = [];
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
for (var i = 0; i < 5; ++i) {
enemies[i] = new Enemy(20 + i * 3,10 + i * 30);
}
loop();
}
function loop() {
// Update
for (var i = 0; i < enemies.length; ++i) {
enemies[i].update();
}
// Render
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
for (var i = 0; i < enemies.length; ++i) {
enemies[i].render(ctx);
}
//
requestAnimationFrame(loop);
}
</script>
</body>
</html>

Related

fillRect not showing

I am trying to code a game about snowboarding but so far, when I try to make trees (green rectangles), they aren't showing up.
Code:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 800;
var x = canvas.width / 2,
speed = 0.1,
size = 5,
alive = 1,
keys = [];
function player() {
if (alive === 1) {
if (keys[37] || keys[65]) {
x -= 3;
}
if (keys[39] || keys[68]) {
x += 3;
}
if (x > canvas.width) {
x = canvas.width;
}
if (x < 0) {
x = 0;
}
ctx.beginPath();
ctx.fillStyle = "black"
ctx.arc(x, 100, size, 0, Math.PI * 2);
ctx.fill();
}
}
function tree() {
var treeX = Math.floor(Math.random() * (canvas.width + 1)) + 25;
var treeY;
if (treeY < 1) {
treeY = canvas.height;
} else {
treeY -= speed;
}
ctx.fillStyle = "green";
ctx.fillRect(treeX, treeY, 5, 8);
}
function update() {
requestAnimationFrame(update);
ctx.clearRect(0, 0, canvas.width, canvas.height);
player();
tree();
}
update();
document.body.addEventListener("keydown", function(e) {
keys[e.keyCode] = true;
});
document.body.addEventListener("keyup", function(e) {
keys[e.keyCode] = false;
});
<canvas id="canvas"></canvas>
I tried switching the order of things and using console to see where the trees are but none worked.
For player, you are tracking state outside of the function in some different variables.
For a tree to be able to update, you also have to track its state.
Here's a way you can do that:
Instead of always initializing treeX and treeY fresh in the function, pass values as arguments
After updating the tree state, return its x and y coordinates
Outside of your tree function, keep track of a list of trees.
In update, replace your list of trees with updated trees.
Here's a running example:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 200;
var x = canvas.width / 2,
speed = 1,
size = 5,
alive = 1,
keys = [],
trees = [ tree(), tree(), tree() ];
function player() {
if (alive === 1) {
if (keys[37] || keys[65]) {
x -= 3;
}
if (keys[39] || keys[68]) {
x += 3;
}
if (x > canvas.width) {
x = canvas.width;
}
if (x < 0) {
x = 0;
}
ctx.beginPath();
ctx.fillStyle = "black"
ctx.arc(x, 100, size, 0, Math.PI * 2);
ctx.fill();
}
}
function tree(
treeX = Math.floor(Math.random() * (canvas.width + 1)),
treeY = Math.floor(Math.random() * (canvas.height + 1))
) {
if (treeY < 1) {
treeY = canvas.height;
} else {
treeY -= speed;
}
ctx.fillStyle = "green";
ctx.fillRect(treeX, treeY, 5, 8);
return [ treeX, treeY ];
}
function update() {
requestAnimationFrame(update);
ctx.clearRect(0, 0, canvas.width, canvas.height);
player();
trees = trees.map(([x, y]) => tree(x, y));
}
update();
document.body.addEventListener("keydown", function(e) {
keys[e.keyCode] = true;
});
document.body.addEventListener("keyup", function(e) {
keys[e.keyCode] = false;
});
* { margin: 0; }
canvas { border: 1px solid red; }
<canvas id="canvas"></canvas>

Why does the page start to lag when drawing many elements on the canvas?

I'm creating a game, and I need to draw) some elements at the top of the <canvas>, but the more elements appear, the more lag the page itself. I found this example where a lot of circles appear, but everything works fine - JSFiddle. Can someone tell me how to optimize my case?
"use strict";
/*Determing canvas*/
window.onload = () => {
const canvas = document.getElementById("canvas"),
ctx = canvas.getContext('2d'),
endTitle = document.getElementById('gameover');
let spawnRate = 300,
lastspawn = -1;
class Wall {
/*Getting values*/
constructor(x, y, width, height, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
}
/*Draw rectangle*/
draw() {
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(this.x, this.y, this.width, this.height)
}
}
/*Making walls*/
let walls = [];
/*Spawn walls endlessly*/
function spawnWalls() {
const wall_x = Math.floor(Math.random() * (canvas.width - 20)) + 10
const wall_y = 0
for (let i = 0; i < 200; i++) {
walls.push(new Wall(wall_x, wall_y, 10, 10, 10))
}
}
/*Update game*/
function refresh() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let time = Date.now()
if (time > (lastspawn + spawnRate)) {
lastspawn = time;
spawnWalls();
spawnRate -= 10;
}
walls.forEach(wall => wall.draw())
for (let j of walls) {
j.y += j.speed;
j.draw();
};
};
let interval = setInterval(refresh, 50);
Walls is too big.
It looks like you're never removing old walls, so they are continuing to be drawn well after they have been removed from the canvas. In your Refresh function, check if the wall has passed the canvas size and if so, remove that from the walls array.
EDIT:
I've added your 'remove' code from the comment.
Another easy win is to stop using new Date() because of who knows what, probably time zones and localization, dates are very expensive to instantiate. However modern browsers offer a performance API, and that can tell you the time since page load, which seems to have a substantial improvement on your existing performance.
"use strict";
/*Determing canvas*/
window.onload = () => {
const canvas = document.getElementById("canvas"),
ctx = canvas.getContext('2d'),
endTitle = document.getElementById('gameover');
let spawnRate = 300,
lastspawn = -1;
endTitle.style.display = "none";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
/*Classes*/
class Player {
/*Get player info*/
constructor(x, y, width, height, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
}
/*Draw player*/
draw() {
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(this.x, this.y, this.width, this.height);
}
/*Move player*/
move() {
}
};
class Wall {
/*Getting values*/
constructor(x, y, width, height, speed) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
}
/*Draw rectangle*/
draw() {
ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(this.x, this.y, this.width, this.height)
}
}
/*Defining players*/
let player_01 = new Player(20, 70, 20, 20, 10);
let player_02 = new Player(50, 500, 20, 20, 10);
let players = [];
players.push(player_01);
/*Making walls*/
let walls = [];
/*Spawn Walls for infinity*/
function spawnWalls() {
const wall_x = Math.floor(Math.random() * (canvas.width - 20)) + 10
const wall_y = 0
for (let i = 0; i < 200; i++) {
walls.push(new Wall(wall_x, wall_y, 10, 10, 10))
}
}
/*Update game*/
function refresh() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let time = performance.now()
if (time > (lastspawn + spawnRate)) {
lastspawn = time;
spawnWalls();
spawnRate -= 10;
}
walls.forEach(wall => wall.draw())
outOfWindow()
for (let i of players) {
i.draw();
};
for (let j of walls) {
if (j.y > canvas.height) {
walls.shift(j)
}
j.y += j.speed;
j.draw();
if (player_01.height + player_01.y > j.y && j.height + j.y > player_01.y && player_01.width + player_01.x > j.x && j.width + j.x > player_01.x) {
clearInterval(interval);
endTitle.style.display = "flex";
};
};
};
let interval = setInterval(refresh, 50);
/*Move players on keypress*/
for (let i of players) {
window.addEventListener("keydown", (event) => {
let key = event.key.toLowerCase();
if (key == "w") i.y -= i.speed;
else if (key == "s") i.y += i.speed;
else if (key == "a") i.x -= i.speed;
else if (key == "d") i.x += i.speed;
})
}
/*Check if player out of the window*/
function outOfWindow() {
for (let i of players) {
if (i.x < 0) i.x = 0;
else if (i.x + i.width > canvas.width) i.x = canvas.width - i.width;
else if (i.y < 0) i.y = 0;
else if (i.y + i.height > canvas.height) i.y = canvas.height - i.height;
}
}
}
#gameover {
position: absolute;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: rgba(0, 0, 0, .5);
}
<div id="gameover">
<h2>The Game Is Over</h2>
<button onclick="restart()">Try again!</button>
</div>
<canvas id="canvas"></canvas>

Initializing a possibly infinite array?

I'm attempting to make sand fall from the mouse (i've changed the x and y of the sand to be the center of the canvas for testing purposes.)
I'm having some issues with one of my functions. drawSand() will not run - I believe it's because sandObj[] can't be initialized before hand, but i'm uncertain. i've tried initializing it by just limiting the array to 200 elements but that didn't seem to help any. I've added some booleans to test to see if the function is finishing and draw is the only one that isn't (besides the drawPaintTool, it's not enabled on purpose) Any help / criticism would be helpful!
"use strict";
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var ballX = 0;
var ballY = 0;
var ballRadius = 20;
var sandObj = [];
var sandActive = false;
var sandAmount = 10;
var sandX = 10;
var sandY = 0;
var testDrawPaintToolFunction = false;
var testPaintToolFunction = false;
var testDrawSandFunction = false;
var testMoveSandFunction = false;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
sandAmountDisplay();
paintTool();
moveSand();
drawSand();
}
function drawPaintTool() {
ctx.beginPath();
ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "white";
ctx.fill();
ctx.closePath();
testDrawPaintToolFunction = true;
}
function paintTool() {
if (sandActive == true) {
sandAmount++;
}
testPaintToolFunction = true;
}
function drawSand() {
for (var i = 0; i < sandAmount; i++) {
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx.beginPath();
ctx.rect(sandObj[i].x, sandObj[i].y, 5, 5);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
testDrawSandFunction = true;
}
function moveSand() {
testMoveSandFunction = true;
}
function sandAmountDisplay() {
ctx.font = "16px Arial";
ctx.fillStyle = "black";
ctx.fillText("Sand Amount: " + sandAmount, 8, 20);
ctx.fillText("Sand Active: " + sandActive, 8, 40);
ctx.fillText("drawPaintTool: " + testDrawPaintToolFunction, 8, 60);
ctx.fillText("PaintTool: " + testPaintToolFunction, 8, 80);
ctx.fillText("drawSand: " + testDrawSandFunction, 8, 100);
ctx.fillText("moveSand: " + testMoveSandFunction, 8, 120);
}
document.addEventListener("mousemove", mouseMoveHandler, false);
document.addEventListener("mousedown", mouseDownHandler);
document.addEventListener("mouseup", mouseUpHandler);
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft + ballRadius / 2;
var relativeY = e.clientY - canvas.offsetTop + ballRadius / 2;
if (relativeX > 0 && relativeX < canvas.width) {
ballX = relativeX - ballRadius / 2;
}
if (relativeY > 0 && relativeY < canvas.height) {
ballY = relativeY - ballRadius / 2;
}
}
function mouseDownHandler() {
sandActive = true;
}
function mouseUpHandler() {
sandActive = false;
}
setInterval(draw, 10);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Gamedev Canvas Workshop</title>
<style>
* {
padding: 0;
margin: 15;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="sand.js">
</script>
</body>
</html>
so to just help out some, because if I comment the code I would most likely have to write a larger paragraph just to get this posted, i'm going to explain the drawSand function. So the for loop used is for re-drawing all the sand everytime it's called - however, i've had to set the x and y here because I couldn't think of a way to initialize something that could continuously spawn sand. I'm lost to be honest.
EDIT: Also the sandAmount is changing constantly which is most likely the problem with something like this. when the mouse is held down the sandAmount goes up - which is what paintTool is for.
Inside of this function:
function drawSand() {
for (var i = 0; i < sandAmount; i++) {
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx.beginPath();
ctx.rect(sandObj[i].x, sandObj[i].y, 5, 5);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
testDrawSandFunction = true;
}
...you are assigning a new object value to sandObj, which was previously an array. You are accessing it as an array with sandObj[i]... further down, so I'm guessing that was a mistake on your part. Perhaps you meant to do:
sandObj[i] = {
x: canvas.width / 2,
y: canvas.height / 2
};
...rather than:
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};

Stopping animated rain in Canvas smmoothly

Rain Picture HereSo my issue isn't stopping the rain its stopping the making of the rain so the already rendered rain completes its animation off the screen.
I tried setInterval and setTimeout in JS but it just freezes.
And using JQuery to remove canvas tag takes off all drops at once.
Any ideas or direction would be great!
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var mf = 70;
var drops = [];
for(var i = 0; i < mf; i++){
drops.push({
x: Math.random()*canvasW,
y: Math.random()*canvasH,
r: Math.random()*5+2,
d: Math.random() + 1
})
}
function fill() {
ctx.fill();
}
function drawRain(){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(255, 255, 255, .5)";
ctx.beginPath();
for(var i = 0; i < mf; i++){
var f = drops[i];
ctx.moveTo(f.x-5, f.y);
ctx.lineTo(f.x, f.y-15);
ctx.lineTo(f.x+5, f.y);
ctx.arc(f.x, f.y + f.r*.7,5, 0, Math.PI, false);
}
fill();
moveRain();
}
function moveRain(){
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH){
drops[i] = {x: Math.random()*canvasW, y: 0, r: f.r, d: f.d};
}
}
}
var i = setInterval(drawRain, 20);
setTimeout(function( ) { clearInterval(); }, 2000);
canvas{ background-color: black }
<canvas id="rainCanvas"></canvas>
What you want to do is set a flag to stop the rain from wrapping back to the top in your function to stop raining:
var stopRain = false;
...
setTimeout(function( ) { stopRain = true; }, 2000);
Now inside moveRain when that variable is true instead of moving it back to the top remove it from the array. Once we removed all the drops we can then clear the interval as it's no longer needed:
function moveRain(){
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH){
if(stopRain) { // When we stop raining
drops.splice(i, 1); // Remove drop from array
mf--; // Make sure to update the "length"
if(mf<1) clearInterval(i); // If there are not more drops clear the interval
} else drops[i] = {x: Math.random()*canvasW, y: 0, r: f.r, d: f.d};
}
}
}
Fiddle Example
You could also use drops.length instead of using mf, this way you don't need to do mf--.
Here I fixed some of the code and added a rainDensity variable which controls the count of new raindrops. The original code was designed to have a fixed amount of raindrops, it had to be changed to achieve the desired effect.
window.onload = function(){
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var drops = [];
function makeDrop() {
drops.push({
x: Math.random()*canvasW,
y: -10,
r: Math.random()*4+1,
d: Math.pow(Math.random() + 1, 2) + 1
})
}
function drawRain(){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(128, 128, 255, .5)";
for(var f of drops){
ctx.beginPath();
ctx.moveTo(f.x-5, f.y);
ctx.lineTo(f.x, f.y-15);
ctx.lineTo(f.x+5, f.y);
ctx.arc(f.x, f.y + f.r*.7,5, 0, Math.PI, false);
ctx.fill();
}
}
function handleFrame() {
drawRain();
updateRain();
}
function updateRain() {
moveRain();
makeNewDrops();
}
var dropPerFrameCounter = 0;
var startTime = Date.now();
function makeNewDrops() {
var elapsedTime = (Date.now() - startTime) / 1000;
// rainDensity: set it to 0 to stop rain
var rainDensity = Math.max(0, Math.sin(elapsedTime / 3) + 0.5);
dropPerFrameCounter += rainDensity;
while (dropPerFrameCounter >= 1) {
dropPerFrameCounter--;
makeDrop();
}
}
function moveRain() {
for(var f of drops){
f.y += f.d;
}
drops = drops.filter(d => d.y < canvasH);
}
var intervalRender = setInterval(handleFrame, 20);
}
<canvas id="rainCanvas"></canvas>
Had to add an answer because the example all used setInterval to do the animation which looks awful. Use requestAnimationFrame to animate. Also fixed some other problems with the code.
I have added a pause the pauses the animation when you click on the canvas. Clicking again will continue.
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var mf = 70;
var drops = [];
var paused = false;
for(var i = 0; i < mf; i++){
drops.push({
x: Math.random()*canvasW,
y: Math.random()*canvasH,
r: Math.random()*5+2,
d: Math.random() + 1
})
}
function drawRain(){
if(!paused){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(255, 255, 255, .5)";
ctx.beginPath();
for(var i = 0; i < mf; i++){
var f = drops[i];
ctx.moveTo(f.x - 2.5 * f.d, f.y);
ctx.lineTo(f.x, f.y - 7.5 * f.d);
ctx.lineTo(f.x + 2.5 * f.d, f.y);
ctx.arc(f.x, f.y + f.r * 0.7, 2.5 * f.d, 0, Math.PI, false);
}
ctx.fill();
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH + 15){ // make sure completely off the bottom
// dont create a new drop reuse it will stop GC from
// having to interrupt the code.
f.x = Math.random()*canvasW;
f.y = -6; // move off top
}
}
}
requestAnimationFrame(drawRain);
}
requestAnimationFrame(drawRain);
canvas.addEventListener("click",()=>{paused = ! paused});
canvas{
background-color: black;
position : absolute;
top : 0px;
left : 0px;
}
<canvas id="rainCanvas"></canvas>

How to update text written on canvas

When I try to change the scoreboard using the computerCount variable (as you can see in my if statement in the update function for the ball variable) the other numbers aren't shown properly. Keep in mind that at first 0 works properly.
JSFiddle format - You can change code by going at top right corner that says "Edit" in JSFiddle.
Code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pong</title>
<!-- Basic styling, centering the canvas -->
<style>
canvas{
position: absolute;
margin: auto;
top:60px;
bottom:0;
right:0;
left:0;
}
#scoreBoard{
position: absolute;
margin: auto;
top:0;
bottom:640px;
right:720px;
left:20px;
}
</style>
</head>
<body>
<canvas id = "scoreBoard" width="500" height = "100"></canvas>
<script>
var WIDTH = 700
var HEIGHT = 600
var pi = Math.PI
var canvas
var ctx
var keystate
var upArrow = 38;
var downArrow = 40;
var computerCount = 0;
var playerCount = 0;
var player = {
x: null,
y: null,
width: 20,
height: 100,
/**
* Update the position depending on pressed keys
*/
update: function() {
if(keystate[upArrow]){
this.y-=7;
}
if(keystate[downArrow]){
this.y+=7;
}},
/**
* Draw the player paddle to the canvas
*/
draw: function() {
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
/**
* The ai paddle
*
* #type {Object}
*/
var ai = {
x: null,
y: null,
width: 20,
height: 100,
/**
* Update the position depending on the ball position
*/
update: function() {
var desty = ball.y-(this.height - ball.side)*0.5
this.y += (desty-this.y)*0.2;
},
draw: function() {
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
/**
* The ball object
*
* #type {Object}
*/
var ball = {
x: null,
y: null,
vel:null,
side: 20,
speed:5,
update: function() {
this.x += this.vel.x;
this.y += this.vel.y;
if(0>this.y || this.y+this.side>HEIGHT){
var offset = this.vel.y<0 ? 0-this.y : HEIGHT-(this.y+this.side);
this.y+=2*offset
this.vel.y*=-1;
}
var AABBIntersect = function(ax, ay, aw, ah, bx, by, bw, bh) {
return ax < bx+bw && ay < by+bh && bx < ax+aw && by < ay+ah;
};
var pdle = this.vel.x<0 ? player : ai;
if(AABBIntersect(pdle.x,pdle.y,pdle.width,pdle.height, this.x, this.y, this.side, this.side)){
this.x = pdle===player ? player.x + player.width : ai.x - this.side;
var n = (this.y+this.side-pdle.y)/(pdle.height+this.side)
var phi = 0.25*pi*(2*n - 1) // pi/4 = 45
this.x = pdle===player ? player.x+player.width : ai.x - this.side;
var n = (this.y+this.side - pdle.y)/(pdle.height+this.side);
var phi = 0.25*pi*(2*n - 1); // pi/4 = 45
this.vel.x = (pdle===player ? 1 : -1)*this.speed*Math.cos(phi);
this.vel.y = this.speed*Math.sin(phi);
}
if(ball.x<0){
computerCount =+1;
ball.x = (WIDTH - ball.side) / 2;
ball.y = (HEIGHT - ball.side) / 2;
}
},
// reset the ball when ball outside of the canvas in the
draw: function() {
ctx.fillRect(this.x, this.y, this.side, this.side);
}
}
function score(){
}
/**
* Starts the game
*/
function main() {
// create, initiate and append game canvas
canvas = document.createElement("canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas2 = document.createElement("canvas");
canvas2.setAttribute("id", "scoreBoard");
canvas2.width = 200;
canvas2.height = 100;
ctx2 = canvas2.getContext("2d");
document.body.appendChild(canvas2);
keystate = {};
document.addEventListener("keydown" , function(event){
keystate[event.keyCode] = true;
})
document.addEventListener("keyup" , function(event){
delete keystate[event.keyCode];
})
init();
var loop = function() {
update();
draw();
window.requestAnimationFrame(loop, canvas);
};
window.requestAnimationFrame(loop, canvas);
}
/**
* Initatite game objects and set start positions
*/
function init() {
player.x = player.width;
player.y = (HEIGHT - player.height) / 2;
ai.x = WIDTH - (player.width + ai.width);
ai.y = (HEIGHT - ai.height) / 2;
ball.x = (WIDTH - ball.side) / 2;
ball.y = (HEIGHT - ball.side) / 2;
ball.vel = {
x:ball.speed,
y: 0
}
}
/**
* Update all game objects
*/
function update() {
ball.update();
player.update();
ai.update();
}
function draw(){
ctx.fillStyle = "black"
ctx.fillRect(0,0,WIDTH,HEIGHT)
ctx.save();
ctx2.fillStyle = "red"
ctx2.fillText(String(computerCount),100,100)
ctx2.fillText("VS",120,100)
ctx2.fillText(String(playerCount),175,100)
ctx2.font = "bold 40px monaco"
ctx.fillStyle = "white"
ball.draw();
player.draw();
ai.draw();
var w=4;
var x= (WIDTH-w)*0.5;
var y=2;
var step = HEIGHT/20;
while(y<HEIGHT){
ctx.fillRect(x,y+step*0.25,w,step*0.5)
y+=step;
}
ctx.restore();
}
// start and run the game
main();
</script>
</body>
</html>
You have two problems:
When you draw a number, you do not erase the previous number that is already printed. That is, when the score becomes 1, you render 1 on top of already rendered 0. The easiest way to address it is to do fillRect and draw a black rectangle on top of the previously written score before you draw a new score.
When you fix that, you will notice that your score never goes above 1. The reason is a typo in the code that increments it, replace
computerCount =+1;
with
computerCount += 1;
Mentioned by #Ishamael, line 139's computer count needs to be changed to computerCount += 1; //After this if statement add if the player scores.
In line 221,
function draw(){
ctx.fillStyle = "black";
ctx.fillRect(0,0,WIDTH,HEIGHT);
ctx.save();
ctx2.fillStyle = "black";
ctx2.fillRect(0,0,WIDTH, 100);
ctx2.save();
ctx2.fillStyle = "red";
ctx2.font = "bold 40px monaco";
ctx2.fillText(String(computerCount),100,100);
ctx2.fillText("VS",120,100);
ctx2.fillText(String(playerCount),175,100);
ctx2.save();
ctx.fillStyle = "white";
. . .
}
remember your semicolons and set text style before writing. I also added the repaint to erase the previous scoreboard.

Categories

Resources