How to apply gravity to bouncing balls in javascript - - javascript

I want to move from having a ball bounce back and forth around a canvas to having some gravity and eventually dropping.
I know i need to change the velocity when it reaches the bottom but i have no idea how this should be done and coded.
I am a completely new JS student, with no physics background - how hard is this going to be? I'm quite happy to learn etc. I tried having balls collide and come off at correct angles but that seems way above me for now.
var canvas,
ctx,
cx = 100,
cy = 150,
vx = 0,
vy = 5,
radius = 30;
function init() {
canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
circle();
}
function circle() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(circle);
if (cx + radius > canvas.width || cx - radius < 0)
vx = -vx;
if (cy + radius > canvas.height || cy - radius < 0)
vy = -vy;
cx += vx;
cy += vy;
}
I've taken out cx movement just for up/down animation and the circle draw codes for space
What would be the next step?
Will i be multiplying its current velocity by a number like 0.8 on collision and where/how?
Forgive basicness/horrible written code - gotta start somewhere!

You were very close, think of the gravity as a constant downwards velocity increment, so in each step you need to add that to your vy calculation.
"I know i need to change the velocity when it reaches the bottom"`
That is not true because gravity affects objects ALL the time. When you touch the bottom, things like material dampening and surface friction can happen.
var canvas,
ctx,
cx = 100,
cy = 100,
vx = 2,
vy = 5,
radius = 5,
gravity = 0.2,
damping = 0.9,
traction = 0.8,
paused = false;
;
function init() {
canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
canvas.width = 300;
canvas.height = 150;
circle();
}
function circle() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!paused)
requestAnimationFrame(circle);
if (cx + radius >= canvas.width) {
vx = -vx * damping;
cx = canvas.width - radius;
} else if (cx - radius <= 0) {
vx = -vx * damping;
cx = radius;
}
if (cy + radius >= canvas.height) {
vy = -vy * damping;
cy = canvas.height - radius;
// traction here
vx *= traction;
} else if (cy - radius <= 0) {
vy = -vy * damping;
cy = radius;
}
vy += gravity; // <--- this is it
cx += vx;
cy += vy;
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'green';
ctx.fill();
}
init();
// fancy/irrelevant mouse grab'n'throw stuff below
canvas.addEventListener('mousedown', handleMouseDown);
canvas.addEventListener('mouseup', handleMouseUp);
function handleMouseDown(e) {
cx = e.pageX - canvas.offsetLeft;
cy = e.pageY - canvas.offsetTop;
vx = vy = 0;
paused = true;
}
function handleMouseUp(e) {
vx = e.pageX - canvas.offsetLeft - cx;
vy = e.pageY - canvas.offsetTop - cy;
paused = false;
circle();
}
canvas {border: 1px solid black; cursor: crosshair;}
p {margin: 0;}
<canvas id="gameCanvas"></canvas>
<p>Throw the ball by holding and releasing the left mouse button on the canvas (reverse slingshot)</p>

Related

How to make a ball bounce again on click, even while in midair?

I need to make a game where a ball drops and cannot hit the floor, and you have to make it bounce again before it hits the ground, but I don't know how to make the ball jump when the mouse clicks!
How do I make a reaction to thew mouse clicking on the screen?
var canvas, ctx, container;
canvas = document.createElement('canvas');
ctx = canvas.getContext("2d");
var ball;
// Velocity y - randomly set
var vy;
var gravity = 0.5;
var bounce = 0.7;
var xFriction = 0.1;
function init() {
setupCanvas();
vy = (Math.random() * -15) + -5;
ball = {
x: canvas.width / 2,
y: 100,
radius: 20,
status: 0,
color: "red"
};
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2, false);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath()
ballMovement();
}
setInterval(draw, 1000 / 35);
function ballMovement() {
ball.y += vy;
vy += gravity;
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
vx *= -1;
}
if (ball.y + ball.radius > canvas.height) {
ball.y = canvas.height - ball.radius;
vy *= -bounce;
vy = 0;
if (Math.abs(vx) < 1.1)
vx = 0;
xF();
}
}
function setupCanvas() { //setup canvas
container = document.createElement('div');
container.className = "container";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(container);
container.appendChild(canvas);
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 2;
}
You just have to make an event listener as follows
window.addEventListener('click', function(event) {
//code here
})
//but if you want it so it's just on the canvas
canvas.addEventListener('click', function(event) {
//code here
})

Ball stops bouncing after 4-5 seconds

Here is the javascript:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
ax = 50,
ay = 50,
avx = 5,
avy = 2,
radius = 50;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function drawArc() {
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(ax, ay, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
};
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawArc();
ax += avx;
ay -= avy;
avy -= 0.2;
if (ay + radius >= canvas.height) {
avy *= -0.8;
avx *= 0.9;
};
if (ax + radius >= canvas.width) {
avx = -avx;
};
if (ax - radius <= 0) {
avx = -avx;
};
}
setInterval(update, 10);
body, html {
margin: 0;
padding: 0;
}
#canvas {
background-color: black;
}
<canvas id="canvas"></canvas>
Here at Jsfiddle: https://jsfiddle.net/milosdacic/qh1ha085/
I don't know why this is happening, code seems fine.
I have done this thing before but now it won't work.
Any help will be appreciated.
Short answer is you are getting many hits because you are not moving the ball up away from the ground. The next frame it is still impacting the ground and as you reduce its speed it just bogs down..
Add this after line 29 of your fiddle.
ay = canvas.height - radius;
For more on bouncing a ball
I guess You wanted this:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
ax = 50,
ay = 50,
avx = 5,
avy = 2,
radius = 50;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function drawArc() {
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(ax, ay, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
};
var hitTheGround = 0;
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawArc();
ax += avx;
ay -= avy;
avy -= 0.2;
if (ay + radius >= canvas.height) {
avy *= -0.8;
avx *= 0.9;
}
if (ax + radius >= canvas.width) {
avx = -avx;
}
if (ax - radius <= 0) {
avx = -avx;
}
if(ay + radius >= canvas.height - 3) {
hitTheGround++;
}
else {
hitTheGround = 0;
}
if(hitTheGround == 100) { // if it jumps near the ground too frequently
return setTimeout(function() {clearInterval(interval)}, 1000);
}
}
var interval = setInterval(update, 10);
body, html {
margin: 0;
padding: 0;
}
#canvas {
background-color: black;
}
<canvas id="canvas"></canvas>
Little bit dirty fix to stop calling update after than it jumps (vibrates) near the ground N times.
Okay, first of, the code that works.
<html>
<head>
<style>
body, html {
margin: 0;
padding: 0;
}
#canvas {
background-color: black;
}
</style>
<script>
function drawArc() {
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(ax, ay, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
};
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawArc();
ax += avx;
ay -= avy;
avy -= 0.2;
if (ay + radius >= canvas.height) {
avy *= -0.8;
avx *= 0.9;
};
if (ax + radius >= canvas.width) {
avx = -avx;
};
if (ax - radius <= 0) {
avx = -avx;
};
}
function onload()
{
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
ax = 50;
ay = 50;
avx = 5;
avy = 2;
radius = 50;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
setInterval(update, 10);
}
</script>
<style>
</style>
</head>
<body onload="onload()">
<canvas id="canvas"></canvas>
</body>
</html>
Second, what the problem is/was. I think the biggest issue I had was that the code you offered had variables and actions in the global context which were dependent on the page elements which had not yet been created. This meant that the variables for canvas and cxt couldn't be created because the canvas tag hadn't been created yet. I put all of that into a function I could call when the page had loaded and everything worked fine from there. Now when that function is called the canvas already exists and you can use it to create the context and do everything else.
Third based on the question title you want to know why it stopped bouncing. I think you have correctly created a bouncing ball which bounces off the walls and floors, but suffers a lost of energy due to gravity and a non-elastic collision with the ground. Personally, I have not worked through the code, but it looks great. Why would you change it. If you want it to keep bouncing forever, then the collisions with the walls and floors need to be 100% elastic--this means that you don't lose any energy and your ball bounces as high as it ever was before. In your interaction with the FLOOR you use the code below. This has a dampening effect and eventually drops your ball to zero energy.
avy *= -0.8;//could be -1
avx *= 0.9;//could be 1
That, however, creates another problem. The code you are using needs dampening or the ball just bounces higher on every bounce. This is because you are accelerating on this line and GAINING ENERGY.
avy -= 0.2;
One of your other answers suggested you reduce the dampening, but not remove it entirely by changing this one line. You would have to tune this to make it have the behavior you want.
avy *= -0.8;//-0.8 is too little? -1 is too much
LAST EDIT, I PROMISE. I actually had a lot of fun with this. The problem is not so trivial that you can just put in psuedo-physics and get a good simulation of a ball bouncing. Even when you put in all the right equations you still get a small perpetual bounce because that is what happens in real life. When the ball is moving slowly enough and is close enough to the floor the other forces (not gravity, but strong, weak and electro-magnetic attraction) dominate and cause the ball to stop moving. So, I took one more swipe at it and improved the physics a lot. It's not perfect, but at some point you have to ask is the FIDELITY of the simulation more important than smoothing the behavior to match what I want to see. Hope this helps.
<html>
<head>
<style>
body, html {
margin: 0;
padding: 0;
}
#canvas {
background-color: black;
}
</style>
<script>
function drawArc() {
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(xPos, yPos, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
};
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawArc();
var dx = xVel*timeStep;
var dy = yVel*timeStep;
if(yVel<radius && yVel>-radius && canvas.height-(yPos+radius) < .1)
{
yVel = 0;
yPos = canvas.height-radius;
dy=0;
//friction affects xVel
xVel *= fFloor;
}
else if (yPos + dy + radius >= canvas.height) {
//you will be below the floor; there is a bounce
//find the rest of the falling interval
var remainingY = canvas.height-(yPos+radius);
//find the rest of the time step
var remainingTime = remainingY / yVel;
//add acceleration for that time
yVel += gravity * remainingTime
//friction affects xVel
xVel *= fFloor;
//elasticity affects yVel
yVel *= eFloor;
//now you are bouncing up
//what is time up
remainingTime = timeStep - remainingTime;
//set final position
yPos = canvas.height + (yVel*remainingTime) - radius;
//add acceleration for that time
yVel += gravity * remainingTime;
}
else
{
//do not hit the floor, falling the whole time
yPos += dy;
yVel += gravity * timeStep;
}
if (xPos + dx + radius >= canvas.width)
{
//hit a wall; there is a bounce
//find the rest of the interval
var remainingX = canvas.width-(xPos+radius);
//find the rest of the time step
var remainingTime = remainingX / xVel;
//no horizontal acceleration
//friction affects yVel
yVel *= fWall;
//elasticity affects xVel
xVel *= eWall;
//now you are bouncing back
//what is time up
remainingTime = timeStep - remainingTime;
//set final position
xPos = canvas.width + (xVel*remainingTime) - radius;
//no horizontal acceleration
}
else if (xPos + dx - radius <= 0) {
//hit a wall; there is a bounce
//find the rest of the interval
var remainingX = (xPos - radius);
//find the rest of the time step
var remainingTime = remainingX / xVel;
//no horizontal acceleration
//friction affects yVel
yVel *= fWall;
//elasticity affects xVel
xVel *= eWall;
//now you are bouncing back
//what is time up
remainingTime = timeStep - remainingTime;
//set final position
xPos = xVel*remainingTime+radius;
//no horizontal acceleration
}
else {
//never hit a wall; flying the whole time
xPos += dx;
}
}
function onload()
{
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
radius = 15;
xPos = Math.random()*(canvas.width-(2*radius))+radius;
yPos = Math.random()*(canvas.height-(2*radius))+radius;
xVel = Math.random()*100-50;
yVel = Math.random()*100-50;
gravity = 9.8;
eWall = -1;
eFloor = -.8;
fFloor = .9;
fWall = .9;
interval = 10;
timeStep = .1;//apparent time step
setInterval(update, interval);
}
</script>
<style>
</style>
</head>
<body onload="onload()">
<canvas id="canvas"></canvas>
</body>
</html>

Making mouse position (0, 0) the center of the canvas

So I've recently been messing around with html5 canvas and trying to figure out how to make a particle system. While it does work I want to make vx = mouse coordinates(specifically x) but they start from the center.
What I mean by this is basically if the cursor is in the center of the canvas then vx would be equal to 0 and if the cursor is to the right from the center of the canvas it have positive mouse coordinates (and if the cursor is to the left from the center of canvas it have negative mouse coordinates).
Once that is accomplished I would just do p.speed += p.vx
Here is my javascript:
window.onload = function(){
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var W = window.innerWidth, H = window.innerHeight;
canvas.width = W;
canvas.height = H;
var particles = [];
var particle_count = 100;
for(var i = 0; i < particle_count; i++)
{
particles.push(new particle());
}
function particle()
{
this.vx = -1 + Math.random() * 2;
this.speed = {x: 0, y: -15+Math.random()*10};
this.location = {x: W/2, y: H/2};
this.radius = 5+Math.random()*10;
this.life = 20+Math.random()*10;
this.remaining_life = this.life;
this.r = Math.round(Math.random()*255);
this.g = Math.round(Math.random()*55);
this.b = Math.round(Math.random()*5);
}
function draw()
{
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, W, H);
ctx.globalCompositeOperation = "lighter";
for(var i = 0; i < particles.length; i++)
{
var p = particles[i];
ctx.beginPath();
p.opacity = Math.round(p.remaining_life/p.life*100)/100
var gradient = ctx.createRadialGradient(
p.location.x, p.location.y, 0, p.location.x, p.location.y, p.radius);
gradient.addColorStop(0, "rgba("+p.r+", "+p.g+", "+p.b+", "+p.opacity+")");
gradient.addColorStop(0.5, "rgba("+p.r+", "+p.g+", "+p.b+", "+p.opacity+")");
gradient.addColorStop(1, "rgba("+p.r+", "+p.g+", "+p.b+", 0)");
ctx.fillStyle = gradient;
ctx.arc(p.location.x, p.location.y, p.radius, Math.PI*2, false);
ctx.fill();
p.remaining_life--;
p.radius--;
p.location.x += p.speed.x += p.vx;
p.location.y += p.speed.y;
if(p.remaining_life < 0 || p.radius < 0) {
particles[i] = new particle();
}
}
}
setInterval(draw, 33); }
Here is my codepen.
You need to translate context to move origin to center.
You also need to reverse the translation on the mouse coordinates as they will still be relative to the upper-left corner (assuming the coordinates are corrected to be relative to canvas).
Example:
var cx = canvas.width * 0.5;
var cy = canvas.height * 0.5;
ctx.translate(cx, cy); // translate globally once
For each mouse coordinate compensate with the translated position:
var pos = getMousePosition(evt); // see below
var x = pos.x - cx;
var y = pos.y - cy;
To adjust mouse position:
function getMousePosition(evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
}
}

Bouncing ball in Canvas

I'm trying to make a ball bounce inside a canvas and it doesn't work. The ball is getting stuck against the "walls" and I don't understand why. Anyone know how I can fix this?
var can = document.querySelector("canvas");
var ctx = can.getContext("2d");
var canvasWidth = 500;
var canvasHeight = 400;
var radius = 30;
var pX = 60;
var pY = 50;
function draw() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.beginPath();
ctx.fillStyle = "red";
ctx.arc(pX, pY, radius, 0, Math.PI * 2, false);
ctx.fill();
ctx.closePath();
}
function animate() {
var vX = 3;
var vY = 3;
if (pY >= canvasHeight - radius) {
vY = -vY;
}
if (pX >= canvasWidth - radius) {
vX = -vX;
}
pX += vX;
pY += vY;
draw();
requestAnimationFrame(animate);
}
animate();
Your conditions for collision detection only consider the bottom wall and the right-side wall. You need to add conditions for the top wall and the left-side wall. Something like
if (pY >= canvasHeight - radius || pY <= radius) {
vY = -vY;
}
if (pX >= canvasWidth - radius || pX <= radius) {
vX = -vX;
}
You are seeing the strange behaviour with your collision detection because vX and vY are locally declared and initialized in animate. This means, every time animate is called, it will be re-initialized.
Simply move vX and vY declarations out of the animate function.
EDIT: JsFiddle if you want to see it in action: http://jsfiddle.net/y45fhko7/

How to move the square to the destination?

How to move the square to the destination? Square moves one pixel only when click the mouse? Sorry for my english.
window.onload = function(){
var x = 50;
var y = 50;
var c = document.getElementById("game");
var ctx = c.getContext("2d");
init();
draw();
function init()
{
document.addEventListener("click",paint,false);
}
function paint(e)
{
if(x<e.clientX) x++;
}
function draw()
{
ctx.clearRect(x-1,y,1,15);
ctx.fillStyle = "blue";
ctx.fillRect(x,y,15,15);
window.requestAnimationFrame(draw);
}
}
Here is one way to do it, adapted from this article that I wrote a few months back.
The following is the piece to get it working
var tx = targetX - x,
ty = targetY - y,
dist = Math.sqrt(tx*tx+ty*ty);
velX = (tx/dist)*thrust;
velY = (ty/dist)*thrust;
We need to get the difference between the current position and targeted position (clicked area), we then get the distance, and make the velocity for x and y equal to the difference divided by the total distance multiplied by the speed of the object.
Full working example and code
Live demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
width = 500,
height = 500,
mX = width/2,
mY = height/2;
canvas.width = width;
canvas.height = height;
canvas.addEventListener("click", function (e) {
mX = e.pageX;
mY = e.pageY;
});
var Ball = function (x, y, radius, color) {
this.x = x || 0;
this.y = y || 0;
this.radius = radius || 10;
this.speed = 5;
this.color = color || "rgb(255,0,0)";
this.velX = 0;
this.velY = 0;
}
Ball.prototype.update = function (x, y) {
// get the target x and y
this.targetX = x;
this.targetY = y;
// We need to get the distance this time around
var tx = this.targetX - this.x,
ty = this.targetY - this.y,
dist = Math.sqrt(tx * tx + ty * ty);
/*
* we calculate a velocity for our object this time around
* divide the target x and y by the distance and multiply it by our speed
* this gives us a constant movement speed.
*/
this.velX = (tx / dist) * this.speed;
this.velY = (ty / dist) * this.speed;
// Stop once we hit our target. This stops the jittery bouncing of the object.
if (dist > this.radius / 2) {
// add our velocities
this.x += this.velX;
this.y += this.velY;
}
};
Ball.prototype.render = function () {
ctx.fillStyle = this.color;
ctx.beginPath();
// draw our circle with x and y being the center
ctx.arc(this.x - this.radius / 2, this.y - this.radius / 2, this.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
};
var ball1 = new Ball(width / 2, height / 2, 10);
function render() {
ctx.clearRect(0, 0, width, height);
ball1.update(mX, mY);
ball1.render();
requestAnimationFrame(render);
}
render();

Categories