I've created a little bouncing boxes demo in javascript using the canvas. Each box (for want of a better word) class handles it's own updates and rendering. However, my problem lies in clearing the canvas before drawing the next frame. It clears fine when the boxes are moving but leaves artifacts behind after it hits a wall. Here's a fiddle - https://jsfiddle.net/dzuvL7ph/1/
Here's my code;
function init(){
window.box1 = new Box(50, 'red', 0, 0);
window.box2 = new Box(50, 'blue', 400, 20);
requestAnimationFrame(update);
}
function update() {
window.box1.update();
window.box2.update();
requestAnimationFrame(update);
requestAnimationFrame(render);
}
function render() {
window.box1.render();
window.box2.render();
}
// -------------------------------------------------------------------------------------------------------
// --
// -------------------------------------------------------------------------------------------------------
var Box = function(dimensions, color, x, y){
this.width = dimensions;
this.height = dimensions;
this.x = x;
this.y = y;
this.velocityX = 10;
this.velocityY = 10;
this.color = color;
this.context = document.getElementById('canvas').getContext('2d');
this.update = function(){
this.x += this.velocityX;
this.y += this.velocityY;
this.collisionCheck();
};
this.render = function(){
this.context.fillStyle = this.color;
this.context.clearRect(this.x - this.velocityX, this.y - this.velocityY, this.width, this.height);
this.context.fillRect(this.x, this.y, this.width, this.height);
};
this.collisionCheck = function(){
if(this.y > 600 - this.height || this.y < 0)
this.velocityY *= -1;
if(this.x > 800 - this.width || this.x < 0)
this.velocityX *= -1;
};
};
I assume it's to do with this.context.clearRect(this.x - this.velocityX, this.y - this.velocityY, this.width, this.height); but I can't figure out what I'm doing wrong.
It's a little weird you're doing a cleanup for each box. Normally, you'd wipe the whole canvas on each frame: https://jsfiddle.net/yb58fd47/
WHY it happens
Your update function checks for collisions, which can in turn reverse the speed component(s). But, your render function relies on that speed component to accurately represent the last speed. If you don't want to clear the whole canvas, you can keep track of the previous actual speed the box had (I added velocityXLast and velocityYLast):
function init(){
window.box1 = new Box(50, 'red', 0, 0);
window.box2 = new Box(50, 'blue', 120, 20);
requestAnimationFrame(update);
}
function update() {
window.box1.update();
window.box2.update();
requestAnimationFrame(update);
requestAnimationFrame(render);
}
function render() {
window.box1.render();
window.box2.render();
}
// -------------------------------------------------------------------------------------------------------
// --
// -------------------------------------------------------------------------------------------------------
var Box = function(dimensions, color, x, y){
this.width = dimensions;
this.height = dimensions;
this.x = x;
this.y = y;
this.velocityX = 10;
this.velocityY = 10;
this.color = color;
this.context = document.getElementById('canvas').getContext('2d');
this.update = function(){
this.x += this.velocityX;
this.y += this.velocityY;
this.velocityXLast = this.velocityX;
this.velocityYLast = this.velocityY;
this.collisionCheck();
};
this.render = function(){
this.context.fillStyle = this.color;
this.context.clearRect(this.x - this.velocityXLast, this.y - this.velocityYLast, this.width, this.height);
this.context.fillRect(this.x, this.y, this.width, this.height);
};
this.collisionCheck = function(){
if(this.y > 150 - this.height || this.y < 0)
this.velocityY *= -1;
if(this.x > 400 - this.width || this.x < 0)
this.velocityX *= -1;
};
};
init();
canvas {border: 1px solid black}
<canvas id="canvas" width="400" height="150"></canvas>
Related
This question already has answers here:
Flickering images in canvas animation
(2 answers)
Closed 1 year ago.
I try to make an animation using the following code:
var obst = new Obstacle(x, y)
obst.build()
function animate() {
ctx.clearRect(0, 0, innerWidth, innerHeight);
requestAnimationFrame(animate);
obst.update(-1, 0)
player1.build();
}
but obst var doesn't appear (but player1 appears)
class for obst:
class Obstacle {
constructor(x, y) {
this.x = x;
this.y = y;
}
build() {
var img = new Image();
img.src = 'assests/obst.png';
img.onload = () => {
ctx.drawImage(img, this.x, this.y, 60, 60);
}
}
update(x, y) {
this.x += x;
this.y += y;
this.build()
}
}
When I run the code without clearrect() syntax it shows as it should be.
Full code:
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, innerWidth, innerHeight);
obst.update(-1, 0)
player1.build();
}
class Player {
constructor(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
build() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
ctx.fillStyle = this.color;
ctx.fill();
}
update(x, y) {
this.x += x
this.y += y
this.build()
}
}
class Obstacle {
constructor(x, y) {
this.x = x;
this.y = y;
}
build() {
var img = new Image();
img.src = 'assests/obst.png';
img.onload = () => {
ctx.drawImage(img, this.x, this.y, 60, 60);
}
}
update(x, y) {
this.x += x;
this.y += y;
this.build()
}
}
var obst = new Obstacle(canvas.width, canvas.height / 2 - 30)
obst.build()
var player1 = new Player(canvas.width / 2, canvas.height / 2, 30, 'blue');
player1.build();
animate();
The async task of loading the image should be removed from the update loop. Have the objects follow a pattern where building and updating / drawing are independent, and where building is async and happens once...
class Obstacle {
constructor(x, y) {
this.x = x;
this.y = y;
}
// don't use the object until this promise resolves
async build() {
var img = new Image();
img.src = 'assests/obst.png';
return new Promise(resolve => img.onload = resolve)
}
// call the method that draws current state "draw"
draw() {
ctx.drawImage(img, this.x, this.y, 60, 60);
}
// call the method that updates current state and draws "update"
update(x, y) {
this.x += x;
this.y += y;
this.draw()
}
}
class Player {
constructor(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
// for symmetry, and maybe someday player will need to do async work here
async build() {
return Promise.resolve();
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
ctx.fillStyle = this.color;
ctx.fill();
}
update(x, y) {
this.x += x
this.y += y
this.draw()
}
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, innerWidth, innerHeight);
obst.update(-1, 0)
player1.update(0, 1);
}
async function play() {
const obst = new Obstacle(canvas.width, canvas.height / 2 - 30)
await obst.build();
const player1 = new Player(canvas.width / 2, canvas.height / 2, 30, 'blue');
await player1.build();
animate();
}
play()
I'm creating a canvas game and I'm working on the shooting aspect of the game. I've created a player that can move and a shooting function but I have two issues:
I would like to click in the canvas and have a projectile send to the click location. What is happening is that the click trigger location is disconnected from the canvas so that if I click around the top left of the page I can aim where I shoot.
I have have enabled movement with the player but I'm not sure how to link the movement of the player and update the origin of the projectile. I have set up movement functions for both player and projectile but I'm not sure where I'm going wrong.
Any help would be greatly appreciated!
https://codepen.io/blacksunmachine/pen/mdmxLxg
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
canvas.width = innerWidth / 2; // this takes up the whole page horizontally
canvas.height = innerHeight / 2; // this takes up the whole page verticallly
class Player {
constructor(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
draw() {
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fillStyle = this.color;
context.fill();
}
moveLeft() {
this.x -= 30;
if (this.x <= 0) {
this.x = 0;
}
}
moveRight() {
this.x += 30;
if (this.x >= canvas.width) {
this.x = canvas.width;
}
}
moveUp() {
this.y -= 30;
if (this.y <= 0) {
this.y = 0;
}
}
moveDown() {
this.y += 30;
if (this.y >= canvas.height) {
this.y = canvas.height;
}
}
}
// player position
let newPlayer1 = new Player(canvas.width / 2, canvas.height / 2, 10, "blue");
class Projectile {
constructor(x, y, radius, color, velocity) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocity = velocity;
}
draw() {
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fillStyle = this.color;
context.fill();
}
update() {
this.draw();
this.x = this.x + this.velocity.x;
this.y = this.y + this.velocity.y;
}
moveLeft() {
this.x -= 30;
if (this.x <= 0) {
this.x = 0;
}
}
moveRight() {
this.x += 30;
if (this.x >= canvas.width) {
this.x = canvas.width;
}
}
moveUp() {
this.y -= 30;
if (this.y <= 0) {
this.y = 0;
}
}
moveDown() {
this.y += 30;
if (this.y >= canvas.height) {
this.y = canvas.height;
}
}
}
function animate() {
requestAnimationFrame(animate);
context.clearRect(0, 0, canvas.width, canvas.height);
newPlayer1.draw();
projectiles.forEach((projectile) => {
projectile.update();
});
}
const projectile = new Projectile(
canvas.width / 2,
canvas.height / 2,
10,
"red",
{
x: -1,
y: -1,
}
);
const projectiles = [];
// need to link shooting with movement of the character
addEventListener("click", (event) => {
console.log(event)
const angle = Math.atan2(
event.clientY - canvas.height / 2,
event.clientX - canvas.width / 2
); // detemine direction of click
console.log(angle); // show where is being clicked
const velocity = {
// speed and direction of click
x: Math.cos(angle) * 20,
y: Math.sin(angle) * 20,
};
// from original position (in the middle of the screen) move a projectile with velocity
projectiles.push(
new Projectile(canvas.width / 2, canvas.height / 2, 10, "red", velocity)
);
});
// difference in X and Y when moving the character - not using this at the moment
// let deltaX = 0;
// let deltaY = 0;
// movement keys for player 1
window.addEventListener("keydown", handleKeyDown);
function handleKeyDown(event) {
switch (
event.keyCode // position of letters might change when on different layouts - same in every language
) {
case 37: // left
newPlayer1.moveLeft();
projectile.moveLeft()
break;
case 38: // up
newPlayer1.moveUp();
projectile.moveUp()
break;
case 39: // right
newPlayer1.moveRight();
projectile.moveRight()
break;
case 40: // down
newPlayer1.moveDown();
projectile.moveDown()
break;
default:
console.log("you cannot move player 1 like that");
}
console.log(event);
}
animate();
Here is the code of what i understood in your question.
canvas.addEventListener('click',(e)=>{
try{
let x=e.clientX;
let y=e.clientY;
let angle=Math.atan2(y-player.y-(player.rad/2),x-player.x-(player.rad/2));
x=player.x+(player.rad/2);
y=player.y+(player.rad/2);
let vx=Math.cos(angle)*15;
let vy=Math.sin(angle)*15;
projectileArray.push(new Projectile(x,y,vx,vy));
}catch(e){}
});
Variables explaination
player.rad is the radius of player
Here player is the object of the Player class
This function fires when you click on the canvas
The 15 value which i added in the line 8 and 9 of my code is just the velocity of the bullet
new Projectile(x,y,cx,vy) is the bullet which takes the current x, y position which is the players position and vx, vy after resolving the components of x and y of the click position from the player
projectileArray is just an array containing the bullets fired for rendering all of the bullets on the canvas
Actually this code is what i created for making my own shooting game. If you want to see how it works just click on this link to play it.
Shooter Game
This link is provided strictly for you to understand how it works and not for publicising my game.
Problem: The color of block should be grey, but instead inherits the color-property of ball which is red. block2 should be blue, but inherits the color of block which is grey. How do I fix this problem? I have just started to learn how to program so i do not have much experience with things like this:/
Code:
function Player(x, y, w, h, color) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = color;
this.dy = 3;
this.draw = function() {
c.fillRect(this.x, this.y, this.w, this.h);
c.fillStyle = this.color;
c.stroke();
c.closePath();
};
this.update = function() {
if (keyW === true) {
block.y -= block.dy;
}
if (keyS === true) {
block.y += block.dy;
}
if (arrowUp === true) {
block2.y -= block2.dy;
}
if (arrowDown === true) {
block2.y += block2.dy;
}
if (this.y + this.h > canvas.height) {
this.y = canvas.height - this.h;
}
if (this.y < 0) {
this.y = 0;
}
this.draw();
};
}
block = new Player(10, (canvas.height / 2) - 50, 250, 100, "grey");
block2 = new Player(canvas.width - 30, (canvas.height / 2) - 50, 20, 100, "blue");
function Ball(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.dx = 3;
this.dy = 0;
this.draw = function() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.fillStyle = this.color;
c.fill();
c.closePath();
};
this.update = function() {
this.x -= this.dx;
if (this.y + this.radius > canvas.height) {
this.y = canvas.height - this.radius;
}
if (this.y - this.radius < 0) {
this.y = this.radius;
}
if (this.x + this.radius > canvas.width) {
this.dx = -this.dx;
}
if (this.x - this.radius < 0) {
this.dx = -this.dx;
}
this.draw();
};
}
ball = new Ball(canvas.width / 2 - 50, canvas.height / 2, 10, "red");
You are filling a rect inside Player update method before changing the color, so it uses previous fillStyle value which is red.
Instead of:
c.fillRect(x, y, w, h);
c.fillStyle = this.color;
Do:
c.fillStyle = this.color;
c.fillRect(x, y, w, h);
I'm creating a javascript game where I need a collision between the ball and rectangle to trigger a reset of the game. For now I just have an alert there for testing purposes. I'm having trouble getting the collision detection working.
This is what I've got so far but it isn't working
function startGame() {
keeper = new obstacle(40, 20, "#666", 130, 180);
theBall = new component("#000", 80, 10);
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 300;
this.canvas.height = 250;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function(e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function(e) {
myGameArea.key = false;
})
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(color, x, y, type) {
this.type = type;
this.speed = 1.5;
this.angle = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.beginPath();
ctx.arc(this.x, this.y, 10, 0, 2 * Math.PI);
ctx.fillStyle = color;
ctx.fill();
ctx.restore();
}
this.newPos = function() {
this.x -= this.speed * Math.sin(this.angle);
this.y += this.speed * Math.cos(this.angle);
}
}
function obstacle(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.collideWith = function(theBall){
var collide = true;
var myTop =this.y;
var theBallBottom = theBall.y + (theBall.radius)
if (myTop < theBallBottom)
{
collide = false;
}
return collide;
}
}
function updateGameArea() {
if (keeper.crashWith(theBall)) {
alert("Collision");
} else {
myGameArea.clear();
theBall.newPos();
theBall.update();
keeper.speedX = 0;
keeper.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {keeper.speedX = -1; }
if (myGameArea.key && myGameArea.key == 39) {keeper.speedX = 1; }
keeper.newPos();
keeper.update();
}
}
The game can be found here http://alexhollingsworth.co.uk/game.php
You named your method collideWith, but you are calling it crashWith in the update method. Plus this will only detect collision on y axis, but I assume this is intended.
I made an if statement that can detect collisions in JavaScript, here it is:
if circle x < rect x + circle width && circle x + rect width > rect x && circle y < rect y + circle height && rect height + circle y > rect y {
This works by putting the ball into an 'imaginary box', then senses if any of the edges of the 'imaginary box' come in contact with any of the edges of the rectangle. I hope this helped.
I'm trying to make particles that accelerate using JavaScript and the HTML5 Canvas, but I cannot get them to accelerate, they just move at a constant speed. Does anyone know why?
document.addEventListener("DOMContentLoaded", init);
function init() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
angle = Math.random() * (2 * Math.PI);
pArray = [];
for (i = 0; i<25; i++) {
angle = Math.random() * (2*Math.PI);
pArray[i] = new Particle(Math.cos(angle), Math.sin(angle));
}
setInterval(loop, 50);
}
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (x = 0; x < pArray.length; x++) {
pArray[x].draw();
}
}
function Particle(xVel, yVel) {
this.xVel = xVel;
this.yVel = yVel;
this.x = canvas.width/2;
this.y = canvas.height/2;
this.draw = function() {
this.x += xVel;
this.y -= yVel;
this.yVel += 1;
ctx.beginPath();
ctx.arc(this.x, this.y, 1, 0, Math.PI * 2);
ctx.fillStyle = "rgb(0, 255, 0)";
ctx.fill();
}
}
Your draw function is using the yVel passed to the constructor.
try with this.y += this.yVel;
It looks like your draw function is using the xVel and yVel from the constructor instead of from the particle instance. Try changing this.y += yVel to this.y += this.yVel.
You can create extra variable with name speed and then speed up the balls like this:
function Particle(xVel, yVel) {
this.xVel = xVel;
this.yVel = yVel;
this.speed = 1;
this.x = canvas.width/2;
this.y = canvas.height/2;
this.draw = function() {
this.x += this.speed * this.xVel;
this.y += this.speed * this.yVel;
this.speed++;
ctx.beginPath();
ctx.arc(this.x, this.y, 1, 0, Math.PI * 2);
ctx.fillStyle = "rgb(0, 255, 0)";
ctx.fill();
}
}
Here is example on jsfiddle https://jsfiddle.net/3nnm2omm/