I would like to remove the balls already generated in the canvas on the click and decrease the counter on the bottom, but my function does not work. Here is my code concerning the part of the ball removal.
Is it possible to use a div to get the same result and to facilitate the removal of the balls? thank you
ball.onclick = function removeBalls(event) {
var x = event.clientX;
var y = event.clientY;
ctx.clearRect(x, y, 100, 50);
ctx.fillStyle = "#000000";
ctx.font = "20px Arial";
ctx.fillText("Balls Counter: " + balls.length - 1, 10, canvas.height - 10);
}
below I enclose my complete code
// GLOBAL VARIBLES
var gravity = 4;
var forceFactor = 0.3; //0.3 0.5
var mouseDown = false;
var balls = []; //hold all the balls
var mousePos = []; //hold the positions of the mouse
var ctx = canvas.getContext('2d');
var heightBrw = canvas.height = window.innerHeight;
var widthBrw = canvas.width = window.innerWidth;
var bounciness = 1; //0.9
window.onload = function gameCore() {
function onMouseDown(event) {
mouseDown = true;
mousePos["downX"] = event.pageX;
mousePos["downY"] = event.pageY;
}
canvas.onclick = function onMouseUp(event) {
mouseDown = false;
balls.push(new ball(mousePos["downX"], mousePos["downY"], (event.pageX - mousePos["downX"]) * forceFactor,
(event.pageY - mousePos["downY"]) * forceFactor, 5 + (Math.random() * 10), bounciness, random_color()));
ball
}
function onMouseMove(event) {
mousePos['currentX'] = event.pageX;
mousePos['currentY'] = event.pageY;
}
function resizeWindow(event) {
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
}
function reduceBounciness(event) {
if (bounciness == 1) {
for (var i = 0; i < balls.length; i++) {
balls[i].b = bounciness = 0.9;
document.getElementById("btn-bounciness").value = "⤽ Bounciness";
}
} else {
for (var i = 0; i < balls.length; i++) {
balls[i].b = bounciness = 1;
document.getElementById("btn-bounciness").value = " ⤼ Bounciness";
}
}
return bounciness;
}
function reduceSpeed(event) {
for (var i = 0; i < balls.length; i++) {
balls[i].vx = velocityX = 20 + c;
balls[i].vy = velocityY = 20 + c;
}
}
function speedUp(event) {
for (var i = 0; i < balls.length; i++) {
balls[i].vx = velocityX = 120 + c;
balls[i].vy = velocityY = 120 + c;
}
}
function stopGravity(event) {
if (gravity == 4) {
for (var i = 0; i < balls.length; i++) {
balls[i].g = gravity = 0;
balls[i].vx = velocityX = 0;
balls[i].vy = velocityY = 0;
document.getElementById("btn-gravity").value = "►";
}
} else {
for (var i = 0; i < balls.length; i++) {
balls[i].g = gravity = 4;
balls[i].vx = velocityX = 100;
balls[i].vy = velocityY = 100;
document.getElementById("btn-gravity").value = "◾";
}
}
}
ball.onclick = function removeBalls(event) {
var x = event.clientX;
var y = event.clientY;
ctx.clearRect(x, y, 100, 50);
ctx.fillStyle = "#000000";
ctx.font = "20px Arial";
ctx.fillText("Balls Counter: " + balls.length - 1, 10, canvas.height - 10);
}
document.getElementById("btn-gravity").addEventListener("click", stopGravity);
document.getElementById("btn-speed-up").addEventListener("click", speedUp);
document.getElementById("btn-speed-down").addEventListener("click", reduceSpeed);
document.getElementById("btn-bounciness").addEventListener("click", reduceBounciness);
document.addEventListener("mousedown", onMouseDown);
document.addEventListener("mousemove", onMouseMove);
window.addEventListener('resize', resizeWindow);
}
// GRAPHICS CODE
function circle(x, y, r, c) { // x position, y position, r radius, c color
//draw a circle
ctx.beginPath(); //approfondire
ctx.arc(x, y, r, 0, Math.PI * 2, true);
ctx.closePath();
//fill
ctx.fillStyle = c;
ctx.fill();
//stroke
ctx.lineWidth = r * 0.1; //border of the ball radius * 0.1
ctx.strokeStyle = "#000000"; //color of the border
ctx.stroke();
}
function random_color() {
var letter = "0123456789ABCDEF".split(""); //exadecimal value for the colors
var color = "#"; //all the exadecimal colors starts with #
for (var i = 0; i < 6; i++) {
color = color + letter[Math.round(Math.random() * 15)];
}
return color;
}
function selectDirection(fromx, fromy, tox, toy) {
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.moveTo(tox, toy);
}
//per velocità invariata rimuovere bounciness
function draw_ball() {
this.vy = this.vy + gravity * 0.1; // v = a * t
this.x = this.x + this.vx * 0.1; // s = v * t
this.y = this.y + this.vy * 0.1;
if (this.x + this.r > canvas.width) {
this.x = canvas.width - this.r;
this.vx = this.vx * -1 * this.b;
}
if (this.x - this.r < 0) {
this.x = this.r;
this.vx = this.vx * -1 * this.b;
}
if (this.y + this.r > canvas.height) {
this.y = canvas.height - this.r;
this.vy = this.vy * -1 * this.b;
}
if (this.y - this.r < 0) {
this.y = this.r;
this.vy = this.vy * 1 * this.b;
}
circle(this.x, this.y, this.r, this.c);
}
// OBJECTS
function ball(positionX, positionY, velosityX, velosityY, radius, bounciness, color, gravity) {
this.x = positionX;
this.y = positionY;
this.vx = velosityX;
this.vy = velosityY;
this.r = radius;
this.b = bounciness;
this.c = color;
this.g = gravity;
this.draw = draw_ball;
}
// GAME LOOP
function game_loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (mouseDown == true) {
selectDirection(mousePos['downX'], mousePos['downY'], mousePos['currentX'], mousePos['currentY']);
}
for (var i = 0; i < balls.length; i++) {
balls[i].draw();
}
ctx.fillStyle = "#000000";
ctx.font = "20px Arial";
ctx.fillText("Balls Counter: " + balls.length, 10, canvas.height - 10);
}
setInterval(game_loop, 10);
* {
margin: 0px; padding: 0px;
}
html, body {
width: 100%; height: 100%;
}
#canvas {
display: block;
height: 95%;
border: 2px solid black;
width: 98%;
margin-left: 1%;
}
#btn-speed-up, #btn-speed-down, #btn-bounciness, #btn-gravity{
padding: 0.4%;
background-color: beige;
text-align: center;
font-size: 20px;
font-weight: 700;
float: right;
margin-right: 1%;
margin-top:0.5%;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Power Balls</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<canvas id="canvas"></canvas>
<input type="button" value="⤼ Bounciness" id="btn-bounciness"></input>
<input type="button" onclick="c+=10" value="+ Speed" id="btn-speed-up"></input>
<input type="button" value="◾" id="btn-gravity"></input>
<input type="button" onclick="c+= -10" value=" - Speed" id="btn-speed-down"></input>
<script src="js/main.js"></script>
</body>
</html>
I see two problems:
ball.onclick will not get triggered, as ball it is not a DOM object. Instead, you need to work with canvas.onclick. Check whether the user clicked on a ball or on empty space to decide whether to delete or create a ball.
To delete the ball, ctx.clearRect is not sufficient. This will clear the ball from the currently drawn frame, but the object still remains in the array balls and will therefore be drawn again in the next frame via balls[i].draw();. Instead, you need to remove the clicked ball entirely from the array, e. g. by using splice.
Otherwise, nice demo! :)
This cannot be done because the canvas is an immediate mode API. All you can do is draw over the top but this is not reliable.
https://en.wikipedia.org/wiki/Immediate_mode_(computer_graphics)
You will have to store each item in a separate data structure, then re-draw whenever a change occurs. In your case there is an array of balls, so you should remove the one that was clicked, before redrawing the entire array.
Each time you remove something, clear the canvas then re-draw each ball.
There are optimisations you can make if this is too slow.
Related
I have created a simple game using Canvas in Javascript where the player has to avoid obstacles by jumping over or shrinking. The obstacle images, which are animated moving towards the left, flickers at some sort of interval. The interval increases in parallel with the gameSpeed variable, making me wonder if this might have something to do with either the spawnObstacle() or the update() functions. Another reason why I believe so is because neither the drawn playerImg.png nor the drawn Score/Highscore ever flickers. I have tried several different solutions found online, but none seem to solve my problem.
Here is my index.js and index.html below:
import {registerNewHighscore, makeList} from "./globalHs.js";
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const newGameBtn = document.getElementById("newGame");
const seeInstrBtn = document.getElementById("instrBtn");
const seeHsBtn = document.getElementById("hsBtn");
const goBackBtn = document.getElementById("backBtn");
let score;
let highscore;
let scoreText;
let highscoreText;
let player;
let gravity;
let obstacles = [];
let gameSpeed;
let keyPressed;
let isKeyPressed = false
let active = true;
let rotation = 0;
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
function createImage(path){
let image = new Image();
image.src = path;
return image;
}
const obsImg = createImage("img/chatbubble.png");
const rockImg = createImage("img/rock.png");
const roadblockImg = createImage("img/roadblock.png");
const playerImg = createImage("img/logoPlayer.png");
newGameBtn.addEventListener('click', function() {
document.getElementById("newGame").style.display = "none";
document.getElementById("header").style.display = "none";
document.getElementById("instr").style.display = "none";
document.getElementById("main").style.display = "block";
document.getElementById("instrBtn").style.display = "none";
document.getElementById("hsBtn").style.display = "none";
document.getElementById("hsBoard").style.display = "none";
start();
});
seeInstrBtn.addEventListener('click', function(){
document.getElementById("header").style.display = "none";
document.getElementById("instrBtn").style.display = "none";
document.getElementById("newGame").style.display = "none";
document.getElementById("instr").style.display = "block";
document.getElementById("instr").style.visibility = "visible";
document.getElementById("backBtn").style.display = "block";
document.getElementById("hsBtn").style.display = "none";
document.getElementById("hsBoard").style.display = "none";
document.getElementById("backBtn").style.top = "50%";
});
seeHsBtn.addEventListener('click', function(){
document.getElementById("header").style.display = "none";
document.getElementById("hsBtn").style.display = "none";
document.getElementById("newGame").style.display = "none";
document.getElementById("instrBtn").style.display = "none";
document.getElementById("instr").style.display = "none";
document.getElementById("hsBoard").style.display = "block";
document.getElementById("backBtn").style.display = "block";
document.getElementById("backBtn").style.top = "70%";
makeList();
});
goBackBtn.addEventListener('click', function() {
goBack();
});
function goBack() {
document.getElementById("backBtn").style.display = "none";
document.getElementById("instr").style.display = "none";
document.getElementById("header").style.display = "block";
document.getElementById("newGame").style.display = "block";
document.getElementById("instrBtn").style.display = "block";
document.getElementById("hsBtn").style.display = "block";
document.getElementById("hsBoard").style.display = "none";
};
document.addEventListener('keydown', function(evt) {
if (isKeyPressed) return;
isKeyPressed = true;
keyPressed = evt.code;
});
document.addEventListener('keyup', function(evt) {
if (evt.code !== keyPressed) return; // only respond to the key already pressed
isKeyPressed = false;
keyPressed = null;
});
function randomIntInRange (min, max){
return Math.round(Math.random() * (max - min) + min);
}
class Player{
constructor (x, y, r, w, h, playerImg){
this.playerImg = playerImg;
this.x = x;
this.y = y;
this.r = r;
this.w = r*2;
this.h = r*2;
this.dy = 0;
this.jumpForce = 18;
this.originalRad = r;
this.grounded = false;
this.jumpTimer = 0;
/* this.newRotation = 0; */
}
animate () {
if (['Space', 'KeyW'].includes(keyPressed)) {
this.jump();
} else{
this.jumpTimer = 0;
}
if (['ShiftLeft', 'KeyS'].includes(keyPressed)){
/* this.newRotation = rotation * 2; */
this.r = this.originalRad / 2;
this.w = this.originalRad;
this.h = this.originalRad;
} else {
/* this.newRotation = rotation; */
this.r = this.originalRad;
this.w = this.r * 2;
this.h = this.r * 2;
}
this.y += this.dy;
if (this.y + this.r < canvas.height){
this.dy += gravity;
this.grounded = false;
} else{
this.dy = 0;
this.grounded = true;
this.y = canvas.height - this.r;
}
this.draw();
}
jump () {
if (this.r != this.originalRad) return;
if (this.grounded && this.jumpTimer == 0){
this.jumpTimer = 1.5;
this.dy = -this.jumpForce;
} else if (this.jumpTimer > 0 && this.jumpTimer < 15){
this.jumpTimer++;
this.dy = -this.jumpForce - (this.jumpTimer / 50);
}
}
draw () {
ctx.translate(this.x, this.y);
ctx.rotate(rotation);
ctx.translate(-(this.x), -(this.y));
ctx.drawImage(this.playerImg, (this.x-this.r), (this.y-this.r), this.w, this.h);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
}
class Obstacle {
constructor (x, y, w, h, obsImg){
this.obsImg = obsImg;
this.x = x,
this.y = y,
this.w = w;
this.h = h;
this.dx = -gameSpeed;
obsImg.width = this.w;
obsImg.height = this.h;
}
update (){
this.x += this.dx;
this.dx = -gameSpeed;
}
draw () {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(this.x, this.y, this.w, this.h,);
ctx.drawImage(this.obsImg, this.x, this.y, this.w*1.1, this.h);
ctx.closePath();
}
/////// CIRCLE
/*draw () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, (2 * Math.PI), false)
ctx.fillStyle = this.c;
ctx.fill();
ctx.closePath();
}*/
/////// ELLIPSE
/*draw () {
ctx.beginPath();
ctx.ellipse(this.x, this.y, this.radX, this.radY, 0, 0, 2 * Math.PI);
ctx.fillStyle = this.c;
ctx.fill();
ctx.stroke();
}*/
}
class Rock {
constructor (x, y, w, h, rockImg){
this.rockImg = rockImg;
this.x = x,
this.y = y,
this.w = w;
this.h = h;
this.dx = -gameSpeed;
rockImg.width = this.w;
rockImg.height = this.h;
}
update (){
this.x += this.dx;
this.dx = -gameSpeed;
}
draw () {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(this.x+20, this.y+40, this.w-20, this.h-40);
ctx.drawImage(this.rockImg, this.x-20, this.y, this.w*1.5, this.h*1.5);
ctx.closePath();
}
}
class Roadblock {
constructor (x, y, w, h, roadblockImg){
this.roadblockImg = roadblockImg;
this.x = x,
this.y = y,
this.w = w;
this.h = h;
this.dx = -gameSpeed;
roadblockImg.width = this.w;
roadblockImg.height = this.h;
}
update (){
this.x += this.dx;
this.dx = -gameSpeed;
}
draw () {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(this.x, this.y+15, this.w, this.h,);
ctx.drawImage(this.roadblockImg, this.x, this.y, this.w, this.h*1.15);
ctx.closePath();
}
}
class Text{
constructor(t, x, y, a, c, s){
this.t = t;
this.x = x;
this.y = y;
this.a = a;
this.c = c;
this.s = s;
}
draw () {
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.font = this.s + "px";
ctx.textAlign = this.a;
ctx.fillText(this.t, this.x, this.y);
ctx.closePath();
}
}
function getDistance(player, obstacle) {
var distX = Math.abs(player.x - (obstacle.x + obstacle.w / 2));
var distY = Math.abs(player.y - (obstacle.y + obstacle.h / 2));
if (distX > (obstacle.w / 2 + player.r)) { return false; }
if (distY > (obstacle.h / 2 + player.r)) { return false; }
if (distX <= (obstacle.w)) { return true; }
if (distY <= (obstacle.h)) { return true; }
var dx = distX - obstacle.w / 2;
var dy = distY - obstacle.h / 2;
return (dx * dx + dy * dy <= (player.r*player.r));
}
let initialSpawnTimer = 200;
let spawnTimer = initialSpawnTimer;
function spawnObstacle (){
let sizeX;
let sizeY;
let type = randomIntInRange(0, 2);
let obstacle = new Obstacle(
canvas.width + sizeX,
canvas.height - sizeX,
sizeX,
sizeY,
obsImg
);
if (type == 0){
sizeX = randomIntInRange(100, 160);
sizeY = sizeX / 2;
obstacle = new Rock(
canvas.width + sizeX,
canvas.height - sizeY,
sizeX,
sizeY,
rockImg
);
} else if (type == 1){
sizeX = randomIntInRange(80, 160);
sizeY = sizeX / 2;
obstacle = new Obstacle(
canvas.width + sizeX,
canvas.height - sizeX,
sizeX,
sizeY,
obsImg
);
obstacle.y -= player.originalRad + randomIntInRange(-50, 150);
} else if (type == 2){
sizeX = 150;
sizeY = sizeX / 2;
obstacle = new Roadblock(
canvas.width + sizeX,
canvas.height - sizeY,
sizeX,
sizeY,
roadblockImg
);
}
obstacles.push(obstacle);
}
function start () {
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.font = "40px Courier New";
active = true;
gameSpeed = 6;
gravity = 1;
score = 0;
highscore = 0;
if (localStorage.getItem('highscore')){
highscore = localStorage.getItem('highscore');
}
player = new Player(100, 0, 50, 100, 100, playerImg);
scoreText = new Text("Score: " + score, 45, 45, "left", "#212121", "40");
highscoreText = new Text("Highscore: " + highscore, 45,
90, "left", "gold", "40");
window.requestAnimationFrame(update);
}
let lastTime;
function update (time) {
if (lastTime != null) {
const delta = time - lastTime;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
spawnTimer--;
if (spawnTimer <= 0){
spawnObstacle();
spawnTimer = initialSpawnTimer - gameSpeed * 8;
if (spawnTimer < 60){
spawnTimer = randomIntInRange(40, 80);
}
}
for (let i = 0; i < obstacles.length; i++){
let o = obstacles[i];
o.draw();
o.update();
if (o.x + o.y < 0){
obstacles.splice(i, 1);
}
if (getDistance(player, o)) {
active = false;
obstacles = [];
spawnTimer = initialSpawnTimer;
gameSpeed = 6;
window.localStorage.setItem('highscore', highscore);
score -= 1;
highscore -= 1;
if (score >= highscore){
registerNewHighscore(highscore+1);
}
goBack();
}
}
lastTime = time;
if (active){
window.requestAnimationFrame(update);
}
player.animate();
score++;
scoreText.t = "Score: " + score;
scoreText.draw();
if (score > highscore){
highscore = score;
highscoreText.t = "Highscore: " + highscore;
}
highscoreText.draw();
rotation+=Math.PI/180 * 2 + gameSpeed * 0.01;
gameSpeed += 0.002;
}
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kindly Waiting Game V2</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="world">
<div id="header">The Kindly Game</div>
<div id="newGame" onmouseover="this.style.backgroundColor = 'goldenrod'"
onmouseout="this.style.backgroundColor= 'gold'">New Game</div>
<div id="instrBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
= 'gold'">Instructions</div>
<div id="hsBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
= 'gold'">Highscores</div>
<div id="instr" style="display: none">Avoid the <br>chatbubbles, <br>roadblocks, <br>and rocks!<br><br>
Instructions: <br>Space/W: Jump <br>ShiftLeft/S: Shrink</div>
<div id="hsBoard" style="display: none">Highscores:</div>
<div id="backBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
= 'gold'">Back</div>
<div id="main"></div>
<div id="score"></div>
<div id="cloudLarge"></div>
<div id="cloudMedium"></div>
<div id="cloudSmall"></div>
<div id="cloudSmall2"></div>
<div id="ufo"></div>
<div id="airplane"></div>
<div id="ye"></div>
</div>
<canvas id="game" width="640" height="400"></canvas>
<script src="globalHs.js" type="module"></script>
<script src="index.js" type="module"></script>
</body>
</html>
Your issue is caused by this block of code inside your update() function:
if (o.x + o.y < 0){
obstacles.splice(i, 1);
}
Now while I don't know the exact logic why you're checking the sum of the obstacles' horizontal and vertical position, you're actually removing something from an array with the .splice() method. As this is happening inside a for-loop you're actually modifying the length of the array while it might still loop over it.
You can fix this by looping over the array from the last to the first element:
for (let i = obstacles.length-1; i>=0; i--) {
let o = obstacles[i];
o.draw();
o.update();
if (o.x + o.y < 0) {
obstacles.splice(i, 1);
}
if (getDistance(player, o)) {
active = false;
obstacles = [];
spawnTimer = initialSpawnTimer;
gameSpeed = 6;
window.localStorage.setItem('highscore', highscore);
score -= 1;
highscore -= 1;
if (score >= highscore) {
registerNewHighscore(highscore + 1);
}
goBack();
}
}
I'm making a simple brick breaker game using Java Script, and my console is showing me error while displaying blocks on to the canvas, they are being drawn onto the canvas but all the other objects are not working and console is showing
index.js:173 Uncaught TypeError: Cannot read property 'show' of undefined
at update (index.js:173)
at index.js:177
if you comment out from line no:172 and 173, which is a for loop that tells the canvas to draw them on it
everything's is working fine.
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;
}
}
function Block(x,y,w,h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.show= function(color){
ctx.beginPath();
ctx.roundedRectangle(this.x,this.y,this.w,this.h,7);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
};
};
var player = new Player(canvas.width/2-50,canvas.height*0.95,100,20);
var ball = new Ball(canvas.width/2-5, player.y-20,15);
var rigthPressed = false;
var leftPressed = false;
var blocks = [];
var rowCount = 5;
var columnCount = 6;
var noInRow = 6;
var blockCount = (rowCount*columnCount)+1;
var blockRow = 0;
var blockCol = 0;
var ballSpeedX = 5;
var ballSpeedY = -10;
for(let i = 1; i < blockCount; i++){
blocks.push(new Block(blockCol*60+25,blockRow*60+30,50,50));
blockCol++;
if(i % noInRow == 0){
blockRow++;
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){
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 update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ball.show();
ball.move(ballSpeedX,ballSpeedY);
player.show();
objMovement();
for(let i=0;i<blockCount;i++){
blocks[i].show("violet");
}
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>
When you create blocks you start from 1
for (let i = 1; i < blockCount; i++) {
blocks.push(new Block(blockCol * 60 + 25, blockRow * 60 + 30, 50, 50));
...
so when you update you need to consider that
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.show();
ball.move(ballSpeedX, ballSpeedY);
player.show();
objMovement();
for (let i = 0; i < blockCount -1; i++) { // or for (let i = 0; i < blocks.length; i++) {
blocks[i].show("violet");
}
window.requestAnimationFrame(update);
}
I created a small simulation have balls spawning when the mouse is left clicked on the canvas, then the balls start falling. The script is written in javascript:
var ctx = canvas.getContext("2d");
var vy = 0;
var a = 0.5;
var bouncing_factor = 0.9;
var balls = [];
class Ball {
constructor(x, y, r){
this.x = x;
this.y = y;
this.r = r;
}
show () {
ctx.fillStyle="red";
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fill();
}
bounce () {
var ground = canvas.height - this.r;
if(this.y > ground && Math.abs(vy) < a + 1) {
cancelAnimationFrame(draw);
}
else if (this.y < ground) {
vy += a;
this.y += vy;
}
else {
vy = -vy * bouncing_factor;
this.y = ground - 1;
}
}
}
function spawn(event) {
var r = (Math.random()*20)+10;
var b = new Ball(event.clientX, event.clientY, r);
balls.push(b);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < balls.length; i++) {
balls[i].show();
balls[i].bounce();
}
}
setInterval(draw,10);
The problem here is that if you run this script, it works fine for the first ball but when you spawn another ball; it follows the bouncing as the first one.
Any help will be highly appreciated.
I've made a few changes in your code: The speed vx and the acceleration a are now part of the ball object. The speed starts at 3 and the acceleration will decrease every time when the ball hits the ground. The ball stops bouncing when the acceleration this.a < .1
const canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var bouncing_factor = 0.9;
var balls = [];
class Ball {
constructor(x, y, r){
this.x = x;
this.y = y;
this.r = r;
this.vy = 3;
this.a = 0.5;
}
show () {
ctx.fillStyle="red";
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fill();
}
bounce () {
var ground = canvas.height - this.r;
if (this.y < ground) {
this.vy += this.a;
this.y += this.vy;
}
else {
this.vy = -this.vy * bouncing_factor;
this.y = ground - 1;
this.a *=.95;
}
if(Math.abs(this.a) < .1){this.a=0; this.vy=0}
}
}
function spawn(event) {
var r = (Math.random()*20)+10;
var b = new Ball(event.clientX, event.clientY, r);
balls.push(b);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < balls.length; i++) {
balls[i].show();
balls[i].bounce();
}
}
setInterval(draw,10);
canvas.addEventListener("click", spawn)
<canvas id="myCanvas" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
I have a problem with using the interp to improve my drawing function. For some reason the realX returns undefined every 3 frames. I'm using this guide to implement this functionality. The end goal is to make the collision system work so it would reflect bullets on an incomming shield (basicly how the bullets currently interact with the canvas borders. This is part of the code:
//INT
var canvas = document.getElementById('ctx'),
cw = canvas.width,
ch = canvas.height,
cx = null,
bX = canvas.getBoundingClientRect().left,
bY = canvas.getBoundingClientRect().top,
mX,
mY,
lastFrameTimeMs = 0,
maxFPS = 60,
fps = 60,
timestep = 1000/fps,
lastTime = (new Date()).getTime(),
currentTime = 0,
delta = 0,
framesThisSecond = 0,
lastFpsUpdate = 0,
running = false,
started = false,
frameID = 0;
var gametimeStart = Date.now(),
frameCount = 0,
score = 0,
player,
enemyList = {},
boostList = {},
bulletList = {},
pulseList = {},
shieldList = {};
//CREATE , create object
var Unit = function(){
this.x;
this.y;
this.realX;
this.realY;
this.type = "unit";
this.hp = 0;
this.color;
this.collision = function(arc){
return this.x + this.r + arc.r > arc.x
&& this.x < arc.x + this.r + arc.r
&& this.y + this.r + arc.r > arc.y
&& this.y < arc.y + this.r + arc.r
};
this.position = function(delta){
boundX = document.getElementById('ctx').getBoundingClientRect().left;
boundY = document.getElementById('ctx').getBoundingClientRect().top;
this.realX = this.x;
this.realX = this.y;
this.realR = this.r;
this.x += this.spdX * delta / 1000;
this.y += this.spdY * delta / 1000;
if (this.x < this.r || this.x + this.r > cw){
this.spdX = -this.spdX;
if (Math.random() < 0.5) {
this.spdY += 100;
} else {
this.spdY -= 100;
}
}
if (this.y < this.r || this.y + this.r > ch){
this.spdY = -this.spdY;
if (Math.random() < 0.5) {
this.spdX += 100;
} else {
this.spdX -= 100;
}
}
};
this.draw = function(interp){
ctx.save();
ctx.fillStyle = this.color;
ctx.beginPath();
//THIS DOESN'T WORK, the code is working perfectly without it
this.x = (this.realX + (this.x - this.realX) * interp);
this.y = (this.realY + (this.y - this.realY) * interp);
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ctx.arc(this.x,this.y,this.r,0,2*Math.PI,false);
ctx.fill();
ctx.restore();
};
}
//Player
var Player = function(){
//Coördinates
this.x = 50;
this.y = 50;
this.r = 10;
//Stats
this.spdX = 30;
this.spdY = 5;
this.atkSpd = 1;
this.hp = 100;
//Other
this.name;
this.color = "green";
this.type = "player";
this.angle = 1;
this.attack = function attack(enemy){
enemy.hp -= 10;
};
};
Player.prototype = new Unit();
//Boost
var Boost = function(){
//Coördinates
this.x = 300;
this.y = 300;
this.r = 5;
};
Boost.prototype = new Unit();
//Bullet
var Bullet = function(){
//Coördinates
this.x = player.x;
this.y = player.y;
this.r = 3;
//Stats
var angle = player.angle*90;
this.spdX = Math.cos(angle/180*Math.PI)*200;
this.spdY = Math.sin(angle/180*Math.PI)*200;
//Other
if (player.angle === 4)
player.angle = 0;
player.angle++;
};
Bullet.prototype = new Unit();
var player = new Player();
var boost = new Boost();
var bullet = new Bullet();
//UPDATE
update = function(delta){
bullet.position(delta);
if(player.collision(boost)){
alert("collide");
}
};
//DRAW
draw = function(interp){
ctx.clearRect(0,0,cw,ch);
fpsDisplay.textContent = Math.round(fps) + ' FPS';
boost.draw(interp);
bullet.draw(interp);
player.draw(interp);
};
//LOAD
if (!document.hidden) { console.log("viewed"); }
function panic() { delta = 0; }
function begin() {}
function end(fps) {
if (fps < 25) {
fpsDisplay.style.color = "red";
}
else if (fps > 30) {
fpsDisplay.style.color = "black";
}
}
function stop() {
running = false;
started = false;
cancelAnimationFrame(frameID);
}
function start() {
if (!started) {
started = true;
frameID = requestAnimationFrame(function(timestamp) {
draw(1);
running = true;
lastFrameTimeMs = timestamp;
lastFpsUpdate = timestamp;
framesThisSecond = 0;
frameID = requestAnimationFrame(mainLoop);
});
}
}
function mainLoop(timestamp) {
if (timestamp < lastFrameTimeMs + (1000 / maxFPS)) {
frameID = requestAnimationFrame(mainLoop);
return;
}
delta += timestamp - lastFrameTimeMs;
lastFrameTimeMs = timestamp;
begin(timestamp, delta);
if (timestamp > lastFpsUpdate + 1000) {
fps = 0.25 * framesThisSecond + 0.75 * fps;
lastFpsUpdate = timestamp;
framesThisSecond = 0;
}
framesThisSecond++;
var numUpdateSteps = 0;
while (delta >= timestep) {
update(timestep);
delta -= timestep;
if (++numUpdateSteps >= 240) {
panic();
break;
}
}
draw(delta / timestep);
end(fps);
frameID = requestAnimationFrame(mainLoop);
}
start();
if (typeof (canvas.getContext) !== undefined) {
ctx = canvas.getContext('2d');
ctx.font = '30px Arial';
}
//CONTROLLES
//Mouse Movement
document.onmousemove = function(mouse){
var mouseX = mouse.clientX - bX;
var mouseY = mouse.clientY - bY;
if(mouseX < player.width/2)
mouseX = player.width/2;
if(mouseX > cw-player.width/2)
mouseX = cw-player.width/2;
if(mouseY < player.height/2)
mouseY = player.height/2;
if(mouseY > ch-player.height/2)
mouseY = ch-player.height/2;
player.x = mX = mouseX;
player.y = mY = mouseY;
}
//Pauze Game
var pauze = function(){
if(window.event.target.id === "pauze"){
stop();
}
if(window.event.target.id === "ctx"){
start();
}
};
document.addEventListener("click", pauze);
html {
height: 100%;
box-sizing: border-box;
}
*,*:before,*:after {
box-sizing: inherit;
}
body {
position: relative;
margin: 0;
padding-bottom: 6rem;
min-height: 100%;
font-family: "Helvetica Neue", Arial, sans-serif;
}
main {
margin: 0 auto;
padding-top: 64px;
max-width: 640px;
width: 94%;
}
main h1 {
margin-top: 0;
}
canvas {
border: 1px solid #000;
}
<canvas id="ctx" width="500" height="500"></canvas>
<div id="fpsDisplay" style="font-size:50px; position:relative; margin: 0 auto;"></div>
<p id="status"></p>
<button id="pauze">Pauze</button>
Declare, define, use.
The problem is you are using a variables that are undefined.
You have 3 objects, Player, Boost, and Bullet each of which you assign the object Unit to their prototypes.
You declare and define unit
function Unit(){
this.x;
this.y;
this.realX;
this.realY;
// blah blah
this.draw = function(){
// blah blah
this.draw = function (interp) {
ctx.save();
ctx.fillStyle = this.color;
ctx.beginPath();
// this.realX and this.realY are undefined
this.x = (this.realX + (this.x - this.realX) * interp);
this.y = (this.realY + (this.y - this.realY) * interp);
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
ctx.fill();
ctx.restore();
};
}
Then Player
var Player = function () {
//Coördinates
this.x = 50;
this.y = 50;
this.r = 10;
//Stats
// etc....
};
Then add a Unit to the prototype.
Player.prototype = new Unit();
This gives Player the unit properties x, y, realX, realY But realX and realY are undefined. Same with the two other objects Boost, Bullet
Any existing properties with the same name stay as is and keep their values. Any properties not in Player will get them from Unit, but in unit you have only declared the properties realX, realY you have not defined them.
You need to define the properties by giving them a value before you use them. You can do this in Unit
function Unit(){
this.x = 0; // default value I am guessing
this.y = 0;
this.realX = 10;
this.realY = 10;
Then in Player
function Player(){
this.x = 50; // these will keep the value they have
this.y = 50;
// realX and realY will have the defaults when you add to the prototype
// Or you can define them here
this.realX = 20;
this.realY = 20;
Before you can use a variable you must define what it is or check if it is defined and take the appropriate action.
// in draw function.
if(this.realX === undefined){
this.realX = 100; // set the default if not defined;
}
BTW you should get rid of the alerts they are very annoying when you have them appear one after the other without a chance to navigate away from the page.
Hi I try to create an animation with a circle. The function drawRandom(drawFunctions) should pic one of the three drawcircle functions and should bring it on the canvas. Now the problem is that this function become executed every second (main loop) and the circle change his colour. How can I fix that?
window.onload = window.onresize = function() {
var C = 1; // canvas width to viewport width ratio
var el = document.getElementById("myCanvas");
var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight;
var canvasWidth = viewportWidth * C;
var canvasHeight = viewportHeight;
el.style.position = "fixed";
el.setAttribute("width", canvasWidth);
el.setAttribute("height", canvasHeight);
var x = canvasWidth / 100;
var y = canvasHeight / 100;
var ballx = canvasWidth / 100;
var n;
window.ctx = el.getContext("2d");
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// draw triangles
function init() {
ballx;
return setInterval(main_loop, 1000);
}
function drawcircle1()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
}
function drawcircle2()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'blue';
ctx.fill();
}
function drawcircle3()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 105, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'orange';
ctx.fill();
}
function draw() {
var counterClockwise = false;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//first halfarc
ctx.beginPath();
ctx.arc(x * 80, y * 80, y * 10, 0 * Math.PI, 1 * Math.PI, counterClockwise);
ctx.lineWidth = y * 1;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw stop button
ctx.beginPath();
ctx.moveTo(x * 87, y * 2);
ctx.lineTo(x * 87, y * 10);
ctx.lineWidth = x;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * 95, y * 2);
ctx.lineTo(x * 95, y * 10);
ctx.lineWidth = x;
ctx.stroke();
function drawRandom(drawFunctions){
//generate a random index
var randomIndex = Math.floor(Math.random() * drawFunctions.length);
//call the function
drawFunctions[randomIndex]();
}
drawRandom([drawcircle1, drawcircle2, drawcircle3]);
}
function update() {
ballx -= 0.1;
if (ballx < 0) {
ballx = -radius;
}
}
function main_loop() {
draw();
update();
collisiondetection();
}
init();
function initi() {
console.log('init');
// Get a reference to our touch-sensitive element
var touchzone = document.getElementById("myCanvas");
// Add an event handler for the touchstart event
touchzone.addEventListener("mousedown", touchHandler, false);
}
function touchHandler(event) {
// Get a reference to our coordinates div
var can = document.getElementById("myCanvas");
// Write the coordinates of the touch to the div
if (event.pageX < x * 50 && event.pageY > y * 10) {
ballx += 1;
} else if (event.pageX > x * 50 && event.pageY > y * 10 ) {
ballx -= 1;
}
console.log(event, x, ballx);
draw();
}
initi();
draw();
}
Take a look at my code that I wrote:
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
window.addEventListener("load", function()
{
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});