Please see this Fiddle: https://jsfiddle.net/sfarbota/wd5aa1wv/2/
I am trying to make the ball bounce inside the circle at the correct angles without losing speed. I think I have the collision detection down, but I am facing 2 issues:
This is partially based off of the answer given here: https://stackoverflow.com/a/12053397/170309 but I had to translate from Java and also skipped a few lines from their example that seemed irrelevant.
Here is the code:
JavaScript:
function getBall(xVal, yVal, dxVal, dyVal, rVal, colorVal) {
var ball = {
x: xVal,
lastX: xVal,
y: yVal,
lastY: yVal,
dx: dxVal,
dy: dyVal,
r: rVal,
color: colorVal,
normX: 0,
normY: 0
};
return ball;
}
var canvas = document.getElementById("myCanvas");
var xLabel = document.getElementById("x");
var yLabel = document.getElementById("y");
var dxLabel = document.getElementById("dx");
var dyLabel = document.getElementById("dy");
var vLabel = document.getElementById("v");
var normXLabel = document.getElementById("normX");
var normYLabel = document.getElementById("normY");
var ctx = canvas.getContext("2d");
var containerR = 200;
canvas.width = containerR * 2;
canvas.height = containerR * 2;
canvas.style["border-radius"] = containerR + "px";
var balls = [
//getBall(canvas.width / 2, canvas.height - 30, 2, -2, 20, "#0095DD"),
//getBall(canvas.width / 3, canvas.height - 50, 3, -3, 30, "#DD9500"),
//getBall(canvas.width / 4, canvas.height - 60, -3, 4, 10, "#00DD95"),
getBall(canvas.width / 2, canvas.height / 5, -1.5, 3, 40, "#DD0095")
];
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < balls.length; i++) {
var curBall = balls[i];
ctx.beginPath();
ctx.arc(curBall.x, curBall.y, curBall.r, 0, Math.PI * 2);
ctx.fillStyle = curBall.color;
ctx.fill();
ctx.closePath();
curBall.lastX = curBall.x;
curBall.lastY = curBall.y;
curBall.x += curBall.dx;
curBall.y += curBall.dy;
if (containerR <= curBall.r + Math.sqrt(Math.pow(curBall.x - containerR, 2) + Math.pow(curBall.y - containerR, 2))) {
curBall.normX = (curBall.x + curBall.r) - (containerR);
curBall.normY = (curBall.y + curBall.r) - (containerR);
var normD = Math.sqrt(Math.pow(curBall.x, 2) + Math.pow(curBall.y, 2));
if (normD == 0)
normD = 1;
curBall.normX /= normD;
curBall.normY /= normD;
var dotProduct = (curBall.dx * curBall.normX) + (curBall.dy * curBall.normY);
curBall.dx = -2 * dotProduct * curBall.normX;
curBall.dy = -2 * dotProduct * curBall.normY;
}
xLabel.innerText = "x: " + curBall.x;
yLabel.innerText = "y: " + curBall.y;
dxLabel.innerText = "dx: " + curBall.dx;
dyLabel.innerText = "dy: " + curBall.dy;
vLabel.innerText = "v: " + curBall.dy / curBall.dx;
normXLabel.innerText = "normX: " + curBall.normX;
normYLabel.innerText = "normY: " + curBall.normY;
}
}
setInterval(draw, 10);
HTML:
<canvas id="myCanvas"></canvas>
<div id="x"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>
<div id="v"></div>
<div id="normX"></div>
<div id="normY"></div>
CSS:
canvas { background: #eee; }
The ball that doesn't bounce is made of a special kind of rubber, called butyl rubber. Butyl rubber is synthetic, or manmade, rubber that absorbs the kinetic energy from the ball falling. Butyl rubber used in athletic shoes can help absorb the impact of your feet as they hit the ground.
A hard surface, like concrete or hardwood, hardly absorbs any, so most of the kinetic energy of the fall goes into bouncing the ball back up. A soft surface, like grass or carpet, absorbs more energy from the fall, so there's less left to push the ball back up, and it bounces pretty badly.
During a collision, some of the ball's energy is converted into heat. As no energy is added to the ball, the ball bounces back with less kinetic energy and cannot reach quite the same height.
The Physics of a Bouncing Ball While the ball is on the move, kinetic energy--the energy of motion--is at work. As the energy-driven ball hits the floor, the physical forces in play flatten and deform the shape of the ball, dispersing and compressing the molecules that make up the ball.
My math is rusty, so I'm not quite sure how you could compute the new trajectory of the ball using just a dot product, but I'm sure you can compute it with the relevant trig functions: use atan2
to compute the angle to the collision point and the current trajectory angle, use those two to compute the new angle, and a pair of sin
and cos
multiplied by the speed to get the new x/y speeds.
jsFiddle: https://jsfiddle.net/jacquesc/wd5aa1wv/6/
The important part is:
var dx = curBall.x - containerR;
var dy = curBall.y - containerR;
if (Math.sqrt(dx * dx + dy * dy) >= containerR - curBall.r) {
// current speed
var v = Math.sqrt(curBall.dx * curBall.dx + curBall.dy * curBall.dy);
// Angle from center of large circle to center of small circle,
// which is the same as angle from center of large cercle
// to the collision point
var angleToCollisionPoint = Math.atan2(-dy, dx);
// Angle of the current movement
var oldAngle = Math.atan2(-curBall.dy, curBall.dx);
// New angle
var newAngle = 2 * angleToCollisionPoint - oldAngle;
// new x/y speeds, using current speed and new angle
curBall.dx = -v * Math.cos(newAngle);
curBall.dy = v * Math.sin(newAngle);
}
Also note I switched from setInterval
to requestAnimationFrame
, which will make sure there's no more than one update per frame. Ideally you would want to compute the movement based on the actual time elapsed since the last update rather than rely on it being always the same.
Using dot products:
jsFiddle: https://jsfiddle.net/jacquesc/wd5aa1wv/9/
var dx = curBall.x - containerR;
var dy = curBall.y - containerR;
var distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
if (distanceFromCenter >= containerR - curBall.r) {
var normalMagnitude = distanceFromCenter;
var normalX = dx / normalMagnitude;
var normalY = dy / normalMagnitude;
var tangentX = -normalY;
var tangentY = normalX;
var normalSpeed = -(normalX * curBall.dx + normalY * curBall.dy);
var tangentSpeed = tangentX * curBall.dx + tangentY * curBall.dy;
curBall.dx = normalSpeed * normalX + tangentSpeed * tangentX;
curBall.dy = normalSpeed * normalY + tangentSpeed * tangentY;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With