I was following the Mozilla game dev tutorial, and making a simple Breakout game in the HTML5 Canvas and JS.
However, I wanted to enlarge the canvas because it was a bit small so I tried a 800x600 canvas. Then, I noticed the ball was a bit too slow for this new canvas size.
Originaly in the mozilla tutorial, the ball speed was 2. I tried to use 3 instead. And thus comes the problem ...
As I use requestAnimationFrame, which refreshes about 60x per second, we can say that my ball will move about 3 x 60 = 180px per second.
To detect the collision with the right edge of the canvas I use the condition:
if (ball.x + ball.radius >= canvas.width) {
ball.speed = -ball.speed;
// I reverse the speed, which goes from 3 to -3, so the ball bounces.
}
THE PROBLEM IS :
If I put the ball in the starting position x = 2 and, my canvas is 600 pixels wide.
As the ball moves 3px by 3px and a radius of 10px, the ball will arrive at 589 ... and only at 592 it will bounce. While it should bounce at 590.
I tried a lot of things but nothing seems to correct this problem.
The goal is the ball to bounce on the position 590 (well the canvas.width - ball.radius), regardless of the speed, or the starting position.
The problem maybe lies within my gameloop.
I'm using a simple gameloop like this :
function update {
requestAnimationFrame(update)
Drawball();
Moveball()
Collision();
}
requestAnimationFrame(update);
Is it wrong to do collision like this ??
Thanks for the help i'm stuck with this problem since 2 weeks !
I will provide my code
<style>
* {
padding: 0;
margin: 0;
}
body {
position: relative;
background-color: black;
background-image: radial-gradient(rgba(0, 150, 0, 0.3), black 120%);
height: 100vh;
}
canvas {
background: #000;
display: block;
position: absolute;
top: 20px;
right: 20px;
border: solid #00FA61 1px;
}
#debug {
position: absolute;
padding: 10px;
top: 20px;
left: 20px;
border: solid #00FA61 1px;
color: #00FA61;
font-family: 'Courier', monospace;
font-size: 18px;
text-align: center;
}
#debug span {
font-size: 2em;
font-weight: bold;
}
</style>
</head>
<body>
<div id="debug">
<h3>Debug mode</h3>
<p id="posX"></p>
<p id="posY"></p>
<p id="collision"></p>
</div>
<canvas id="myCanvas" width="800" height="600"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var hauteur_canvas = canvas.height;
var largeur_canvas = canvas.width;
var infoPosX = document.getElementById("posX");
var infoPosY = document.getElementById("posY");
var infoCollide = document.getElementById("collision");
/* BALL POS X/Y */
var x = canvas.width / 2;
var y = canvas.height - 40;
/* BALL SPEED */
var direction_x = 8;
var direction_y = -direction_x;
/* RAYON DE LA BOULE */
var rayon_balle = 10;
var timer = 0;
var id_frame;
var currentSeconde = 0,
frameCount = 0,
frameLastSecond = 0;
var colorBall = "#00FA61";
/* HAUTEUR ET LARGEUR DU PADDLE */
var paddleHeight = 10;
var paddleWidth = 75;
/* POSITION X ET Y DU COIN GAUCHE HAUT */
var paddleX = (largeur_canvas - paddleWidth) / 2;
var paddleY = hauteur_canvas - paddleHeight;
/* DISTANCES ENTRE BOULES ET PADDLE */
var dist_center_X;
var dist_center_Y;
/* DETECTION DES INPUTS */
var rightPressed = false;
var leftPressed = false;
/* GESTION DES BRIQUES */
var brickRowCount = 3;
var brickColumnCount = 5;
var brick = {
hauteur: 50,
largeur: 132,
padding: 20,
offsettop: 30,
offsetleft: 30
};
var bricks = [];
for (var c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (var r = 0; r < brickRowCount; r++) {
bricks[c][r] = {
x: 0,
y: 0
};
}
}
/* ------------------- */
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, rayon_balle, 0, Math.PI * 2);
ctx.fillStyle = colorBall;
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#00FA61";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for (var c = 0; c < brickColumnCount; c++) {
for (var r = 0; r < brickRowCount; r++) {
var brickX = (c * (brick.largeur + brick.padding)) + brick.offsetleft;
var brickY = (r * (brick.hauteur + brick.padding)) + brick.offsettop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brick.largeur, brick.hauteur);
ctx.fillStyle = "#1aa829";
ctx.fill();
ctx.closePath();
}
}
}
function distance_boule_paddle() {
/* CALCUL DES DISTANCES ENTRE BOULES ET PADDLE */
dist_center_X = Math.abs(x - paddleX - paddleWidth / 2);
dist_center_Y = Math.abs(y - paddleY - paddleHeight / 2);
}
function fps_count() {
var sec = Math.floor(Date.now() / 1000);
if (sec != currentSeconde) {
currentSeconde = sec;
frameLastSecond = frameCount;
frameCount = 1;
} else {
frameCount++;
}
ctx.fillText("FPS : " + frameLastSecond, 10, 20);
}
function clear() {ctx.clearRect(0, 0, largeur_canvas, hauteur_canvas);}
function collide_paddle() {
if (dist_center_X > (paddleWidth / 2 + rayon_balle)) {
return false;}
if (dist_center_Y > (paddleHeight / 2 + rayon_balle)) {
return false;}
if (dist_center_X <= (paddleWidth / 2)) {
return true;}
if (dist_center_Y <= (paddleHeight / 2)) {
return true;}
var dx = dist_center_X - paddleWidth / 2;
var dy = dist_center_Y - paddleHeight / 2;
return (dx * dx + dy * dy <= (rayon_balle * rayon_balle));
}
function collision() {
/* COLLIDE AVEC LE HAUT DU CANVAS */
if (y - rayon_balle <= 0) {
direction_y = -direction_y;
collideInfo();
} else {
if (y + rayon_balle >= hauteur_canvas - paddleHeight) {
if (collide_paddle()) {
direction_y = -direction_y;
} else {
if (y - rayon_balle > hauteur_canvas) {
// so if the ball is 100% off screen of the down edge its gameover
collideInfo();
alert("GAME OVER");
document.location.reload();
}
}
}
}
/* COLLIDE WITH LEFT AND RIGHT EDGES */
if (x + rayon_balle >= largeur_canvas) {
direction_x = -direction_x;
collideInfo();
} else {
if (x - rayon_balle <= 0) {
direction_x = -direction_x;
collideInfo();
}
}
}
function move_ball() {
x += direction_x;
y += direction_y;
}
function move_paddle() {
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
}
/* EVENTS LISTENERS */
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if (e.keyCode == 39) {
rightPressed = true;
} else if (e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.keyCode == 39) {
rightPressed = false;
} else if (e.keyCode == 37) {
leftPressed = false;
}
}
function collideInfo() {
infoCollide.innerHTML = "<br>La collision s'est produite <br> à la position X <span>: " + x + "</span>" + "<br> la position Y : <span>" + y + "</span>";
}
function gameInfo() {
infoPosX.innerHTML = "La position X de la balle : " + x;
infoPosY.innerHTML = "La position Y de la balle : " + y;
}
function draw() {
id_frame = requestAnimationFrame(draw);
clear();
fps_count();
drawBall();
drawPaddle();
drawBricks();
move_ball();
move_paddle();
distance_boule_paddle();
collision();
gameInfo();
timer++;
if (timer === 2500) {
console.log("STOP !");
cancelAnimationFrame(id_frame);
}
}
requestAnimationFrame(draw);
</script>
</body>
</html>
First of all I don't think you should be doing this
function update {
requestAnimationFrame(update)
Drawball();
Moveball()
Collision();
}
requestAnimationFrame(update);
due to the fact that the requestAnimationFrame(update); is already in the function it self.
try changing it to this
function update {
requestAnimationFrame(update)
Drawball();
Moveball()
Collision();
}
update();
Related
I've making a breakout game and I had to make some blocks and give them random colors defined in a array, but for making more blocks I had to use a for loop. So, when I add them to my update function, colors are flashing at frame rate. I think you'll understand better if you run the snippet
one more thing: that canvasRendering...rundedRectangle is a function that draws rounded edge rectangles someone please find a solution!
CanvasRenderingContext2D.prototype.roundedRectangle = function(x, y, width, height, rounded) {
const radiansInCircle = 2 * Math.PI;
const halfRadians = (2 * Math.PI)/2;
const quarterRadians = (2 * Math.PI)/4 ;
// top left arc
this.arc(rounded + x, rounded + y, rounded, -quarterRadians, halfRadians, true);
// line from top left to bottom left
this.lineTo(x, y + height - rounded);
// bottom left arc
this.arc(rounded + x, height - rounded + y, rounded, halfRadians, quarterRadians, true) ;
// line from bottom left to bottom right
this.lineTo(x + width - rounded, y + height);
// bottom right arc
this.arc(x + width - rounded, y + height - rounded, rounded, quarterRadians, 0, true) ;
// line from bottom right to top right
this.lineTo(x + width, y + rounded) ;
// top right arc
this.arc(x + width - rounded, y + rounded, rounded, 0, -quarterRadians, true) ;
// line from top right to top left
this.lineTo(x + rounded, y) ;
};
var canvas= document.getElementById("gameCanvas");
var ctx = canvas.getContext("2d");
function Player(x,y,w,h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.show = function(){
ctx.beginPath();
ctx.rect(this.x, this.y, this.w, this.h);
ctx.fillStyle = "#ffff";
ctx.fill();
ctx.closePath();
};
this.move = function(speed){
this.x += speed;
};
}
function Ball(x,y,r){
this.x = x;
this.y = y;
this.r = r;
this.show = function(){
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2* Math.PI);
ctx.fillStyle = "tomato";
ctx.fill();
ctx.closePath();
};
this.move= function(speedX,speedY){
this.show();
this.speed = 2;
this.x += speedX;
this.y += speedY;
};
}
var colors = ['#A5E75A','#7254AD','#FFD606','#FF093D'];
function Block(x,y,w,h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.status =1;
this.show= function(color){
ctx.beginPath();
ctx.roundedRectangle(this.x,this.y,this.w,this.h,5);
//ctx.arc(this.x,this.y,10,0,2*Math.PI);
//ctx.fillStyle = colors[Math.floor(Math.random()*colors.length)];
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
};
}
var player = new Player(canvas.width/2-50,780,100,20);
var ball = new Ball(player.x+player.w/2, player.y,15);
var rigthPressed = false;
var leftPressed = false;
var blocks = [];
var rowCount = 5;
var columnCount = 6;
var noInRow = 6;
var blockCount = (rowCount*columnCount)+1;
var rc = {blockRow : 0,
blockCol : 0};
for(let i = 0; i < blockCount; i++){
blocks.push(new Block(rc.blockCol*60+25,rc.blockRow*60-30,50,50));
rc.blockCol++;
if(i % noInRow === 0){
rc.blockRow++;
rc.blockCol = 0;
}
}
window.addEventListener("keydown", function(e){
if(e.keyCode == 39){
rigthPressed = true;
}
if(e.keyCode == 37){
leftPressed = true;
}
});
window.addEventListener("keyup", function(e){
if(e.keyCode == 39){
rigthPressed = false;
}
if(e.keyCode == 37){
leftPressed = false;
}
});
function objMovement(){
if(rigthPressed){
player.move(5);
if (player.x > canvas.width-player.w){
player.x = canvas.width-player.w;
}
}
if(leftPressed){
player.move(-5);
if(player.x < 0){
player.x = 0;
}
}
if(ball.x > canvas.width-ball.r || ball.x < 0+ball.r){
ballSpeedX = -ballSpeedX;
}
if (/*ball.y > canvas.height-ball.r ||*/ball.y < 0+ball.r){
ballSpeedY = -ballSpeedY;
}
if(ball.x<player.x+player.w &&ball.x>player.x && ball.y>player.y && ball.y<player.y+player.h){
ballSpeedY = -ballSpeedY;
ballSpeedX= ballSpeedX;
}
function Bump(){
if (ball.x>player.x && ball.x<player.x+player.w/2){
if (ball.y >= player.y){
ballSpeedX = -5;
}
}
if(ball.x>player.x+player.w/2 && ball.x<player.x+player.w){
if(ball.y >= player.y){
ballSpeedX = 5;
}
}
}
//Bump();
}
function reload(){
if (ball.y>canvas.height){
//alert('gameOver');
ball.x =player.x+player.w/2;
ball.y = player.y-ball.r;
ballSpeedX = 0;
ballSpeedY = 0;
}
}
var ballSpeedX = 0;
var ballSpeedY = -0;
function collision(){
for(let i=1;i<blockCount;i++){
if(ball.x>blocks[i].x &&
ball.x<blocks[i].x+blocks[i].w &&
ball.y>blocks[i].y &&
ball.y<blocks[i].y+blocks[i].h){
blocks[i].status = 0;
ballSpeedY = -ballSpeedY;
blocks.splice(i,1);
blockCount--;
//ballSpeedX = 0;
//ballSpeedY = 0;
console.log('hit');
}
}
}
function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
objMovement();
for(let i=1;i<blockCount;i++){
if(blocks[i].status == 1){
blocks[i].show(colors[Math.floor(Math.random()*colors.length)]);
}
}
collision();
ball.show();
ball.move(ballSpeedX,ballSpeedY);
player.show();
reload();
window.requestAnimationFrame(update);
}
update();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>🌝🌝</title>
<style>
#body{
background-color: rgb(31, 30, 30);
}
#gameCanvas{
border: 15px solid rgb(44, 44, 44);
border-radius: 20px;
background-color:rgb(19, 18, 18);
margin: 250px;
}
</style>
</head>
<body id="body">
<canvas id="gameCanvas" width=400 height=800></canvas>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>
Because you remove and redraw all rectangles from the canvas each update and assign a new color on show, they get assigned a new color each update. You might be able to avert this by adding a property color to the rectangle, which is initialised (once, so in the initial for loop) with a random color, and alter the show function to use this.color rather than accept a color as an argument. This way, you don't assign a new color to a rectangle each update, and it won't change color each update.
I'm trying to code a snake game that will randomly move through the canvas. for now, I won't worry about the "food" as my main problem is the self-playing part not running. it seems to start but does not change course over time, so not sure if need to implement a timer (?)
Tried with a switch and thought by generating with random() one of the alternatives but only goes in one direction until hits the border
*/<-------------------------HTML----------------------->*/
<html>
<head>
<meta charset='utf-8'/>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class= 'game'>
<div id = 'home'>
<canvas id='mycanvas' width='350' height='350'>
</canvas>
</div>
<button id='btn'>START</button>
</div>
<script src="js/logic.js"></script>
</body>
</html>
CSS:
/*<----------------------CSS----------------------->*/
#home {
width: 350px;
height: 350px;
background-size: auto 350px;
background-repeat: no-repeat;
background-color: lightgrey;
background-position: center center;
padding: 0;
margin: 03;
}
button {
background-color: green;
color: white;
border: none;
bottom: 0;
height: 30px;
font-size: 12pt;
float: left;
width: 90px;
margin: 10px 0 0 0;
}
button:hover {
background-color: darkgreen;
}
button:disabled {
background-color: grey;
}
.game {
margin: 0 auto;
}
JS:
/*<-------------------JS----------------->*/
var mycanvas = document.getElementById('mycanvas');
var ctx = mycanvas.getContext('2d');
var snakeSize = 10;
var w = 350;
var h = 350;
var snake;
var snakeSize = 10;
var pixel;
var drawModule = (function() {
var bodySnake = function(x, y) {
ctx.fillStyle = 'green';
ctx.fillRect(x * snakeSize, y * snakeSize, snakeSize, snakeSize);
ctx.strokeStyle = 'black';
ctx.strokeRect(x * snakeSize, y * snakeSize, snakeSize, snakeSize);
}
var drawSnake = function() {
var length = 4;
snake = [];
for (var i = length - 1; i >= 0; i--) {
snake.push({ x: i, y: 0 });
}
}
var paint = function() {
ctx.fillStyle = 'lightgrey';
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = 'black';
ctx.strokeRect(0, 0, w, h);
btn.setAttribute('disabled', true);
var snakeX = snake[0].x;
var snakeY = snake[0].y;
if (direction == 'right') {
snakeX++;
} else if (direction == 'left') {
snakeX--;
} else if (direction == 'up') {
snakeY--;
} else if (direction == 'down') {
snakeY++;
}
if (snakeX == -1 || snakeX == w / snakeSize || snakeY == -1 || snakeY == h / snakeSize || checkCollision(snakeX, snakeY, snake)) {
btn.removeAttribute('disabled', true);
ctx.clearRect(0, 0, w, h);
gameloop = clearInterval(gameloop);
return;
}
if (snakeX == pixel.x && snakeY == pixel.y) {
var tail = { x: snakeX, y: snakeY };
} else {
var tail = snake.pop();
tail.x = snakeX;
tail.y = snakeY;
}
snake.unshift(tail);
for (var i = 0; i < snake.length; i++) {
bodySnake(snake[i].x, snake[i].y);
}
}
var createPixels = function() {
pixel = {
x: Math.floor((Math.random() * 30) + 1),
y: Math.floor((Math.random() * 30) + 1)
}
}
var checkCollision = function(x, y, array) {
for (var i = 0; i < array.length; i++) {
if (array[i].x === x && array[i].y === y) {
return true;
//this part should reinitiate the game
//when it hits an edge
/*}
if (x > w - 1 || x < 0 || y > h - 1 || h < 0) {
return true;*/
}
return false;
}
}
var init = function() {
var r = Math.round(Math.random() * 5);
switch (r) {
case 1:
direction = 'left';
console.log('left');
break;
case 2:
direction = 'right';
console.log('right');
break;
case 3:
direction = 'up';
console.log('up');
break;
case 4:
direction = 'down';
console.log('down');
break;
}
drawSnake();
createPixels();
gameloop = setInterval(paint, 80);
}
return {
init: init
};
}());
(function(window, document, undefined) {
var btn = document.getElementById('btn');
btn.addEventListener("click", function() { drawModule.init(); });
}
)(window, document, drawModule);
Assign direction a random value inside the paint() function.
Explanation: Currently, the only call to random() is in the init() function, which is only called once. Thus direction is only set once, and there is no way for it to change.
I'm in the middle of making a start-screen for my JavaScript Breakout game. I've (poorly) made the background and a play button.
The background is inside the canvas, which I want. But when I want to place the clickable play button ontop of the background in the canvas, it disappears. I've tried making another picture, and I can place that ontop, I just cant make it clickable.
I dont know what the best solution is, I'm very new to JavaScript.
//Script
var background = new Image();
background.src="breakoutbg.png";
var play = new Image();
play.src="breaoutplay.png";
var startBtn = document.getElementById('startBtn');
//game
function drawCanvas() {
ctx.beginPath();
ctx.drawImage(background,0,0);
ctx.fill();
ctx.closePath();
}
function drawPlay() {
ctx.beginPath();
ctx.drawImage(play,250,250);
ctx.fill();clickable;
ctx.closePath();
}
<div id="container">
<button type="button" id="startBtn" onclick="draw()" >
<img src="breaoutplay.png">
</button>
<canvas id="myCanvas" width="600" height="550"></canvas>
</div>
I dont know if I gave enough of the code, for someone to have an Idea about it. The entire code is on github: https://github.com/katrinemira/katrinemira.github.io/blob/master/breakout.html
Add this to ur CSS
#container {
display: inline-block;
position: relative;
}
#startBtn {
border: none;
background: none;
position: absolute;
top: 50%;
left: 50%;
z-index: 1;
transform: translate(-50%,-50%);
-webkit-transform: translate(-50%,-50%);
-moz-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
}
The above code will place the button on the canvas in the center.
Also add z-index: 1; to the button to place it on top of canvas
<html>
<head>
<center>
<style>
body {
background-color: black;
}
* {
padding: 0;
margin: 0;
}
canvas {
background: #353d49;
display: block;
margin: 100px;
}
#startBtn {
border: none;
background: none;
position: absolute;
top: 50%;
left: 50%;
z-index: 1;
transform: translate(-50%,-50%);
-webkit-transform: translate(-50%,-50%);
-moz-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
}
#container {
display: inline-block;
position: relative;
}
#myCanvas {
position: relative;
}
</style>
</head>
<body>
<div id="container">
<button type="button" id="startBtn" onclick="draw()"><img src="https://1.bp.blogspot.com/-fVAKH-3TLuo/W5onDDHje0I/AAAAAAAAB4I/q2ooE6GuzQkS80dtw1JILXjFWdfQ3IKkwCLcBGAs/s1600/breaoutplay.png">
</button>
<canvas id="myCanvas" width="600" height="550"></canvas>
</div>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var ballRadius = 9;
var x = canvas.width - Math.floor(Math.random() * 600)
var y = canvas.height - 30;
var dx = 5;
var dy = -4;
var paddleHeight = 10;
var paddleWidth = 75;
var paddleX = (canvas.width - paddleWidth) / 2;
var rightPressed = false;
var leftPressed = false;
var brickRowCount = 7;
var brickColumnCount = 5;
var brickWidth = 75;
var brickHeight = 20;
var brickPadding = 4;
var brickOffsetTop = 30;
var brickOffsetLeft = 30;
var score = 0;
var lives = 3;
var background = new Image();
background.src = "https://1.bp.blogspot.com/-hs2fckXJBkE/W5obuBm9kII/AAAAAAAAB38/C89KFBJCIlEwfl-g8d-T1Cu4cHFWjYI2QCLcBGAs/s1600/breakoutbg.png";
var play = new Image();
play.src = "https://1.bp.blogspot.com/-fVAKH-3TLuo/W5onDDHje0I/AAAAAAAAB4I/q2ooE6GuzQkS80dtw1JILXjFWdfQ3IKkwCLcBGAs/s1600/breaoutplay.png";
var startBtn = document.getElementById('startBtn');
//game
function drawCanvas() {
ctx.beginPath();
ctx.drawImage(background, 0, 0);
ctx.fill();
ctx.closePath();
}
function drawPlay() {
ctx.beginPath();
ctx.drawImage(play, 250, 250);
ctx.fill();
clickable;
ctx.closePath();
}
function newBrick() {
return {
x: 0,
y: 0,
status: 1
};
}
var bricks = [];
for (var c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (var r = 0; r < brickRowCount; r++) {
bricks[c].unshift(newBrick());
}
}
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
function keyDownHandler(e) {
if (e.keyCode == 39) {
rightPressed = true;
} else if (e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.keyCode == 39) {
rightPressed = false;
} else if (e.keyCode == 37) {
leftPressed = false;
}
}
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft;
if (relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
}
}
function collisionDetection() {
for (var c = 0; c < brickColumnCount; c++) {
for (var r = 0; r < brickRowCount; r++) {
var b = bricks[c][r];
if (b.status == 1) {
if (x > b.x && x < b.x + brickWidth + ballRadius && y > b.y && y < b.y + brickHeight + ballRadius) {
dy = -dy;
b.status = 0;
score++;
if (score == 9999) {
alert("YOU WIN, CONGRATS!");
document.location.reload();
}
}
}
}
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function moreBricks() {
bricks.unshift([]);
newBrick();
brickColumnCount++;
for (r = 0; r < brickRowCount; r++) {
bricks[0].unshift(newBrick());
}
}
function drawBricks() {
for (var c = 0; c < brickColumnCount; c++) {
for (var r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status == 1) {
var brickX = (r * (brickWidth + brickPadding)) + brickOffsetLeft;
var brickY = (c * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: " + score, 8, 20);
}
function drawLives() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Lives: " + lives, canvas.width - 65, 20);
}
var frameCount = 0;
const FRAME_COUNT_NEW_LINE = 500;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
startBtn.style.display = 'none';
frameCount += 1;
if (frameCount === FRAME_COUNT_NEW_LINE) {
frameCount = 0;
moreBricks();
}
drawBricks();
drawBall();
drawPaddle();
drawScore();
drawLives();
collisionDetection();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy < ballRadius) {
dy = -dy;
} else if (y + dy > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
lives--;
if (!lives) {
document.location.reload();
} else {
x = canvas.width - Math.floor(Math.random() * 600);
y = canvas.height - 30;
dx = 5;
dy = -4;
paddleX = (canvas.width - paddleWidth) / 2;
}
}
}
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
requestAnimationFrame(draw);
}
drawCanvas();
drawPlay();
</script>
</body>
</html>
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>
I'm trying to make a ball which moves on a line and it bounces on it when clicked on it, but I don't know how to make it bounce on click.
Also, c.fill() doesn't work, I don't know why. (I've used it to fill the ball, maybe it doesn't work like that?)
Any advice would be really helpful since I'm a beginner in canvas, but know these 2 problems really concern me as I can't find any solutions for any of them. 😞
document.addEventListener('DOMContentLoaded', function () {
var canvas = document.querySelector('canvas');
var canvasx = document.getElementById('hr');
var c = canvas.getContext('2d');
canvas.width = canvasx.clientWidth;
canvas.height = canvasx.clientHeight;
var x = 80;
var y = 30;
var dx = 6;
var dy = 2;
var radius = 15;
var gravity = Math.random();
var friction = 0.9;
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, canvas.width, canvas.height);
c.beginPath();
c.arc(x, y, radius, 0, Math.PI * 2, false);
c.strokeStyle = "#eeede7";
c.stroke();
c.beginPath();
c.moveTo(50, canvas.height);
c.lineTo(1870, canvas.height);
c.lineWidth = 3;
c.fillStyle = "#77dff1";
c.fill();
c.strokeStyle = "#77dff1";
c.stroke();
update = function () {
if (y + 20 > canvas.height) {
dy = -dy * friction;
}
else {
dy += gravity;
}
y += dy;
x += dx;
}
function bounce() {
if ((x + radius + 50) > canvas.width || (x - radius - 50) < 0) {
dx = -dx;
}
}
update();
bounce();
}
animate();
function over() {
var px = event.pageX;
if (x - px < 0 || x - px > 0)
{
dx = -dx;
}
}
function bounceBall()
{
}
canvas.addEventListener('mouseover', over, false);
canvas.addEventListener('click', bounceBall, false)
}, false);
#hr {
position: relative;
bottom: 5%;
width: 100%;
margin: 0 auto;
}
hr {
border-color: #77dff1;
max-width: 90%;
}
canvas {
width: 100%;
}
<div id="hr">
<canvas></canvas>
<script src="js/canvas.js"></script>
</div>