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'm making a game where the point of it is that you have to catch the falling 'eggs' in your basket, and the sensitivity of the paddle gradually goes up, making it harder and harder, and I'm coding in the if statement that checks if the egg went into the basket, but it's not working. I've tried quite a bit, and the if statement runs when the 'egg' gets to the bottom of the screen, checking if it is between the edges of the paddle. My code is here.
var canvas = document.getElementById("ok");
var ctx = canvas.getContext("2d");
var loggingMachine = document.getElementById("loggy");
var score = 0;
const Rg = Math.floor(Math.random() * 480);
function ball(x) {
this.x = x;
this.y = 0;
this.width = 20;
this.height = 20;
}
var firstBall = new ball(Rg);
function newBall() {
if (firstBall.y < 480) {
ctx.clearRect(0, 0, 480, 480);
ctx.fillRect(firstBall.x, firstBall.y, firstBall.width, firstBall.height);
firstBall.y++;
} else {
if (
firstBall.x - 10 > player.x &&
firstBall.x - 10 < player.x - player.width
) {
alert("yawwo");
}
firstBall.y = 0;
firstBall.x = Math.floor(Math.random() * 480);
}
}
function paddle(x) {
this.x = x;
this.y = 470;
this.width = 70;
this.height = 20;
}
var player = new paddle(50);
function renderPlayer() {
ctx.fillRect(player.x, player.y, player.width, player.height);
document.addEventListener("keydown", logKey);
function logKey(e) {
if (e.code === "ArrowLeft") {
player.x = player.x - 0.01;
} else if (e.code === "ArrowRight") {
player.x = player.x + 0.01;
}
loggingMachine.innerText = score;
}
}
function tick() {
newBall(Rg);
renderPlayer();
setTimeout(tick, 5);
}
tick();
<!DOCTYPE html>
<canvas id="ok" width="480" height="480"></canvas>
<p id="loggy"></p>
<script src="script.js"></script>
Here you go: your collision detection needed a little fine polishing.
See the Example below.
Happy Coding
var canvas = document.getElementById("ok");
var ctx = canvas.getContext("2d");
var loggingMachine = document.getElementById("loggy");
var score = 0;
const Rg = Math.floor(Math.random() * 480);
function ball(x) {
this.x = x;
this.y = 0;
this.width = 20;
this.height = 20;
}
var firstBall = new ball(Rg);
function newBall() {
if (firstBall.y < 480) {
ctx.clearRect(0, 0, 480, 480);
ctx.fillRect(firstBall.x, firstBall.y, firstBall.width, firstBall.height);
firstBall.y++;
} else {
const playerStart = player.x;
const playerEnd = player.x + player.width;
const ballStart = firstBall.x;
const ballEnd = firstBall.x + firstBall.width;
const test1 = ballEnd - playerStart;
const test2 = ballStart - playerEnd;
if (
test1 >= 0 && test2 <= 0
) {
score++;
}
firstBall.y = 0;
firstBall.x = Math.floor(Math.random() * 480);
}
}
function paddle(x) {
this.x = x;
this.y = 470;
this.width = 70;
this.height = 20;
}
var player = new paddle(50);
function logKey(e) {
if (e.code === "ArrowLeft") {
player.x = player.x - 15;
} else if (e.code === "ArrowRight") {
player.x = player.x + 15;
}
}
document.addEventListener("keydown", logKey);
function renderPlayer() {
ctx.fillRect(player.x, player.y, player.width, player.height);
loggingMachine.innerText = score;
}
function tick() {
newBall(Rg);
renderPlayer();
setTimeout(tick, 5);
}
tick();
<!DOCTYPE html>
<canvas id="ok" width="480" height="480"></canvas>
<p id="loggy"></p>
<script src="script.js"></script>
Your conditions wasn't right, because you need to check if ball's position is between pad's position, so:
if (
// Add 10 to ensure at least half the ball is touching left edge of the pad
firstBall.x + 10 > player.x &&
// Subtract 10 to ensure at least half the ball is touching right edge of the pad
// Add width to the pad's position to calculate its right edge
firstBall.x - 10 < player.x + player.width
) {
alert("yawwo");
// Increment score and update on screen
score ++;
loggingMachine.innerText = score;
}
firstBall.y = 0;
firstBall.x = Math.floor(Math.random() * 480);
var canvas = document.getElementById("ok");
var ctx = canvas.getContext("2d");
var loggingMachine = document.getElementById("loggy");
var score = 0;
const Rg = Math.floor(Math.random() * 480);
function ball(x) {
this.x = x;
this.y = 0;
this.width = 20;
this.height = 20;
}
var firstBall = new ball(Rg);
function newBall() {
if (firstBall.y < 480) {
ctx.clearRect(0, 0, 480, 480);
ctx.fillRect(firstBall.x, firstBall.y, firstBall.width, firstBall.height);
firstBall.y++;
} else {
if (
firstBall.x + 10 > player.x &&
firstBall.x - 10 < player.x + player.width
) {
alert("yawwo");
score ++;
loggingMachine.innerText = score;
}
firstBall.y = 0;
firstBall.x = Math.floor(Math.random() * 480);
}
}
function paddle(x) {
this.x = x;
this.y = 470;
this.width = 70;
this.height = 20;
}
var player = new paddle(50);
function renderPlayer() {
ctx.fillRect(player.x, player.y, player.width, player.height);
document.addEventListener("keydown", logKey);
function logKey(e) {
if (e.code === "ArrowLeft") {
player.x = player.x - 0.01;
} else if (e.code === "ArrowRight") {
player.x = player.x + 0.01;
}
}
}
function tick() {
newBall(Rg);
renderPlayer();
setTimeout(tick, 5);
}
tick();
<!DOCTYPE html>
<canvas id="ok" width="480" height="480"></canvas>
<p id="loggy">0</p>
<script src="script.js"></script>
In the below code, pressing C or V should change speed of that ball.
Press V ~10 times and after 5-10 seconds press an arrow button. Where is my mistake?
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
function circle(x, y, radius, fillCircle) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fillCircle) {
ctx.fill();
} else {
ctx.stroke();
}
};
function ball() {
this.speed = 5;
this.x = width / 2;
this.y = height / 2;
this.xSpeed = this.speed;
this.ySpeed = 0;
this.size = 5;
};
ball.prototype.move = function() {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x < 0) {
this.x = width;
} else if (this.x > width) {
this.x = 0;
}
if (this.y > height) {
this.y = 0;
} else if (this.y < 0) {
this.y = height;
}
};
ball.prototype.draw = function() {
circle(this.x, this.y, this.size, true);
};
ball.prototype.setDirection = function(direction) {
if (direction === "up") {
this.xSpeed = 0;
this.ySpeed = -this.speed;
} else if (direction === "down") {
this.xSpeed = 0;
this.ySpeed = this.speed;
} else if (direction === "right") {
this.xSpeed = this.speed;
this.ySpeed = 0;
} else if (direction === "left") {
this.xSpeed = -this.speed;
this.ySpeed = 0;
} else if (direction === "stop") {
this.xSpeed = 0;
this.ySpeed = 0;
}
};
ball.prototype.doCommand = function(direction) {
if (direction === "X") {
this.size += 2;
} else if (direction === "Z") {
this.size -= 2;
} else if (direction === "C") {
this.speed -= 2;
} else if (direction === "V") {
this.speed += 2;
}
if (this.speed < 0) {
this.speed = 1;
}
if (this.size < 0) {
this.size = 1;
}
}
var Ball = new ball();
var commands = ["Z", "X", "C", "V"];
var keyActions = {
32: "stop",
37: "left",
38: "up",
39: "right",
40: "down",
90: "Z",
88: "X",
67: "C",
86: "V"
};
$("body").keydown(function(event) {
var direction = keyActions[event.keyCode];
for (var n = 0; n < commands.length; n++) {
if (direction === commands[n]) {
Ball.doCommand(direction);
} else {
Ball.setDirection(direction);
}
};
});
setInterval(function() {
ctx.clearRect(0, 0, width, height);
Ball.draw();
Ball.move();
ctx.strokeRect(0, 0, width, height);
}, 30);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
I have updated your code.
First thing in keydown Handler we don't need for loop. I have changed this.
I have updated Do Command as well. Here is the problem: you are not updating xspeed & yspeed in the doCommand I did that change for c & v.
I hope this is what you are expecting.
Please find the updated code.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"><title>Drawing some circles...</title>
</head>
<body>
<script src = "https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id = "canvas" width="400" height="400"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
function circle(x,y,radius,fillCircle)
{
ctx.beginPath();
ctx.arc(x,y,radius,0,Math.PI*2,false);
if(fillCircle)
{
ctx.fill();
}
else
{
ctx.stroke();
}
};
function ball()
{
this.speed = 5;
this.x = width/2;
this.y = height/2;
this.xSpeed = this.speed;
this.ySpeed = 0;
this.size = 5;
};
ball.prototype.move = function()
{
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x<0)
{
this.x = width;
}
else if(this.x>width)
{
this.x = 0;
}
if (this.y>height)
{
this.y = 0;
}
else if(this.y<0)
{
this.y = height;
}
};
ball.prototype.draw = function()
{
circle(this.x,this.y,this.size,true);
};
ball.prototype.setDirection = function(direction)
{
if(direction ==="up")
{
this.xSpeed = 0;
this.ySpeed = -this.speed;
}else if(direction ==="down")
{
this.xSpeed = 0;
this.ySpeed = this.speed;
}else if(direction ==="right")
{
this.xSpeed = this.speed;
this.ySpeed = 0;
}else if(direction ==="left")
{
this.xSpeed = -this.speed;
this.ySpeed = 0;
}else if(direction ==="stop")
{
this.xSpeed = 0;
this.ySpeed = 0;
}
};
ball.prototype.doCommand = function(direction)
{
if(direction ==="X")
{
this.size +=2;
}else if(direction ==="Z"){
this.size -=2;
}else if(direction ==="C"){
this.speed -=2;
}else if(direction ==="V"){
this.speed +=2;
}
if(this.speed<0)
{
this.speed = 1;
}
if(this.size<0)
{
this.size = 1;
}
if(direction ==="V" || direction ==="C") {
if(this.xSpeed!=0) {
this.xSpeed = this.speed;
}
if(this.ySpeed!=0) {
this.ySpeed = this.speed;
}
}
}
var Ball = new ball();
var commands = ["Z","X","C","V"];
var keyActions = {
32:"stop",
37:"left",
38:"up",
39:"right",
40:"down",
90:"Z",
88:"X",
67:"C",
86:"V"
};
$("body").keydown(function (event)
{
var direction = keyActions[event.keyCode];
if(commands.includes(direction))
{
Ball.doCommand(direction);
}
else{
Ball.setDirection(direction);
}
});
setInterval(function()
{
ctx.clearRect(0,0,width,height);
Ball.draw();
Ball.move();
ctx.strokeRect(0,0,width,height);
}, 30);
</script>
</body>
</html>
I recently made a JS Pong game. It works well, but the ball rarely gets stuck at the bottom or top. It looks like it is halfway through the wall and constantly bouncing. Video of the issue happening. You can try the game here. I do not know why this issue is happening because the logic seems right and works 90% of the time correctly. Here are the main two functions of my program:
function moveAll() {
if (showingWinScreen) {
return;
}
computerMovement();
ballX += ballSpeedX;
ballY += ballSpeedY;
if (ballY <= 10) {
ballSpeedY = -ballSpeedY;
} else if (ballY >= HEIGHT - 10) {
ballSpeedY = -ballSpeedY;
}
if (ballX >= WIDTH - 10) {
if ((ballY > paddleY) && (ballY < paddleY + 100)) {
ballSpeedX = -ballSpeedX;
var deltaY = ballY - paddleY - 50;
ballSpeedY = deltaY / 5;
} else {
player1Score++;
ballReset();
}
} else if (ballX <= 10) {
if ((ballY > mouseY - 50) && (ballY < mouseY + 50)) {
ballSpeedX = -ballSpeedX;
deltaY = ballY - mouseY;
ballSpeedY = deltaY / 6;
} else {
player2Score++;
ballReset();
}
}
}
function drawAll() {
if (showingWinScreen) {
colorRect(0, 0, WIDTH, HEIGHT, "black");
canvas.fillStyle = "yellow";
canvas.fillText("Click to continue!", 300, 300);
if (player1Score == WINNING_SCORE) {
canvas.fillText("You won!", 360, 500);
} else if (player2Score == WINNING_SCORE) {
canvas.fillText("The computer beat you!", 280, 500);
}
return;
}
colorRect(0, 0, WIDTH, HEIGHT, "black");
drawNet();
makeCircle(ballX, ballY, 10, 0, Math.PI * 2, "red");
colorRect(790, paddleY, 10, 100, "cyan");
colorRect(0, mouseY - 50, 10, 100, "yellow");
canvas.fillStyle = "white";
canvas.fillText(player1Score + " " + player2Score, 360, 100);
}
Thank you for your help!
I think there's only one case in which this could happen: when, in a colliding frame, you decrease the speed.
When the speed remains the same, no matter what, your ball will always bounce back to the previous' frames position:
var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
Ball(50, 50, 0, 5, 5, "red"),
Ball(100, 50, 0, 5, 10, "blue"),
Ball(150, 50, 0, 5, 15, "green"),
Ball(200, 50, 0, 5, 20, "yellow")
];
var next = () => {
updateFrame(balls);
drawFrame(balls);
}
var loop = () => {
requestAnimationFrame(() => {
next();
loop();
});
}
next();
function Ball(x, y, vx, vy, r, color) {
return {
x: x,
y: y,
vx: vx,
vy: vy,
r: r,
color: color
}
};
function updateBall(b) {
b.x += b.vx;
b.y += b.vy;
if (b.y <= b.r ||
b.y >= cvs.height - b.r) {
b.vy *= -1;
}
};
function drawBall(b) {
ctx.beginPath();
ctx.fillStyle = b.color;
ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
ctx.fill();
}
function updateFrame(balls) {
balls.forEach(updateBall);
}
function drawFrame(balls) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>
But when the speed changes, things get stuck:
var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
Ball(50, 50, 0, 10, 5, "red"),
Ball(100, 50, 0, 10, 10, "blue"),
Ball(150, 50, 0, 10, 15, "green"),
Ball(200, 50, 0, 10, 20, "yellow")
];
var next = () => {
updateFrame(balls);
drawFrame(balls);
}
var loop = () => {
requestAnimationFrame(() => {
next();
loop();
});
}
next();
function Ball(x, y, vx, vy, r, color) {
return {
x: x,
y: y,
vx: vx,
vy: vy,
r: r,
color: color
}
};
function updateBall(b) {
b.x += b.vx;
b.y += b.vy;
if (b.y <= b.r ||
b.y >= cvs.height - b.r) {
b.vy *= -0.5;
}
};
function drawBall(b) {
ctx.beginPath();
ctx.fillStyle = b.color;
ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
ctx.fill();
}
function updateFrame(balls) {
balls.forEach(updateBall);
}
function drawFrame(balls) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>
In your case, I'm thinking this can only happen when there's a paddle collision AND a wall collision simultaneously.
A quick-to-implement solution would be to check if the new position is valid before translating the ball position. If you don't want the precise location, you can place the ball at the point of collision. Note that this will produce a slightly off frame.
E.g.:
var newY = ballY + ballSpeedY;
// Top wall
if(newY <= 10) {
ballY = 10;
ballSpeedY = -ballSpeedY;
}
// Bottom wall
else if(newY >= HEIGHT-10){
ballY = HEIGHT - 10;
ballSpeedY = -ballSpeedY;
}
// No collision
else {
ballY = newY;
}
Update: a more detailed description of what can happen
Let's say your ball collides with the top border of your canvas and with your paddle in the same frame.
First, you move the ball to the colliding position: ballY += ballSpeedY; Say your ballY is 4, and your ballSpeedY is -5, you'll position the ball to -1, inside the wall.
If this were to be the only collision, you should be okay. You flip the speed (ballSpeedY = -ballSpeedY), so in the next frame, your ball should be back at -1 + 5 = 4, so ballY will be 4 again, and your ball will move towards 4 + 5 = 9 in the next frame.
Now a problem arises, when in the -1 positioned frame, you collide with the paddle as well! When the paddle hits the ball, you modify the ballspeed: ballSpeedY = deltaY / 5;. If this turns out to be < 1, your ball won't be able to exit the wall in the next frame. Instead of -1 + 5 = 4, your ball will, for example, move to: -1 + 0.5 = -0.5.
Now, your ball won't be able to get back in to play, since the next frame will, again, calculate a collision and flip the speed. This results in the bouncy, trembling effect you see when the ball gets stuck.
A naive but pretty decent solution, is to only update the position of the ball to a valid position. I.e.: never to a colliding coordinate.
var animate = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 60)
};
var canvas = document.createElement("canvas");
var width = 400;
var height = 600;
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
var player = new Player();
var computer = new Computer();
var ball = new Ball(200, 300);
var keysDown = {};
var render = function () {
context.fillStyle = "#FF00FF";
context.fillRect(0, 0, width, height);
player.render();
computer.render();
ball.render();
};
var update = function () {
player.update();
computer.update(ball);
ball.update(player.paddle, computer.paddle);
};
var step = function () {
update();
render();
animate(step);
};
function Paddle(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.x_speed = 0;
this.y_speed = 0;
}
Paddle.prototype.render = function () {
context.fillStyle = "#0000FF";
context.fillRect(this.x, this.y, this.width, this.height);
};
Paddle.prototype.move = function (x, y) {
this.x += x;
this.y += y;
this.x_speed = x;
this.y_speed = y;
if (this.x < 0) {
this.x = 0;
this.x_speed = 0;
} else if (this.x + this.width > 400) {
this.x = 400 - this.width;
this.x_speed = 0;
}
};
function Computer() {
this.paddle = new Paddle(175, 10, 50, 10);
}
Computer.prototype.render = function () {
this.paddle.render();
};
Computer.prototype.update = function (ball) {
var x_pos = ball.x;
var diff = -((this.paddle.x + (this.paddle.width / 2)) - x_pos);
if (diff < 0 && diff < -4) {
diff = -5;
} else if (diff > 0 && diff > 4) {
diff = 5;
}
this.paddle.move(diff, 0);
if (this.paddle.x < 0) {
this.paddle.x = 0;
} else if (this.paddle.x + this.paddle.width > 400) {
this.paddle.x = 400 - this.paddle.width;
}
};
function Player() {
this.paddle = new Paddle(175, 580, 50, 10);
}
Player.prototype.render = function () {
this.paddle.render();
};
Player.prototype.update = function () {
for (var key in keysDown) {
var value = Number(key);
if (value == 37) {
this.paddle.move(-4, 0);
} else if (value == 39) {
this.paddle.move(4, 0);
} else {
this.paddle.move(0, 0);
}
}
};
function Ball(x, y) {
this.x = x;
this.y = y;
this.x_speed = 0;
this.y_speed = 3;
}
Ball.prototype.render = function () {
context.beginPath();
context.arc(this.x, this.y, 5, 2 * Math.PI, false);
context.fillStyle = "#000000";
context.fill();
};
Ball.prototype.update = function (paddle1, paddle2) {
this.x += this.x_speed;
this.y += this.y_speed;
var top_x = this.x - 5;
var top_y = this.y - 5;
var bottom_x = this.x + 5;
var bottom_y = this.y + 5;
if (this.x - 5 < 0) {
this.x = 5;
this.x_speed = -this.x_speed;
} else if (this.x + 5 > 400) {
this.x = 395;
this.x_speed = -this.x_speed;
}
if (this.y < 0 || this.y > 600) {
this.x_speed = 0;
this.y_speed = 3;
this.x = 200;
this.y = 300;
}
if (top_y > 300) {
if (top_y < (paddle1.y + paddle1.height) && bottom_y > paddle1.y && top_x < (paddle1.x + paddle1.width) && bottom_x > paddle1.x) {
this.y_speed = -3;
this.x_speed += (paddle1.x_speed / 2);
this.y += this.y_speed;
}
} else {
if (top_y < (paddle2.y + paddle2.height) && bottom_y > paddle2.y && top_x < (paddle2.x + paddle2.width) && bottom_x > paddle2.x) {
this.y_speed = 3;
this.x_speed += (paddle2.x_speed / 2);
this.y += this.y_speed;
}
}
};
document.body.appendChild(canvas);
animate(step);
window.addEventListener("keydown", function (event) {
keysDown[event.keyCode] = true;
});
window.addEventListener("keyup", function (event) {
delete keysDown[event.keyCode];
});
http://jsfiddle.net/kHJr6/2/