Why is this ball inside the circle not bouncing properly?

See this script: https://jsfiddle.net/sfarbota/wd5aa1wv/2/

I am trying to make the ball bounce inside the circle at the right angle without losing speed. I think I have a collision detected, but I ran into two problems:

  • The ball slows down with each bounce until it stops.
  • The angles at which it bounces seem wrong.

This is partly based on the answer given here: https://stackoverflow.com/a/2126502/ ... but I had to translate from Java and also skipped a few lines from their example, which seemed inconsequential.

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; } 
+5
source share
1 answer

My math is rusty, so I'm not quite sure how you could calculate a new ball path using only a point product, but I'm sure you can calculate it using the appropriate trigger functions: use atan2 to calculate the angle to the collision point and the current angle paths, use these two to calculate the new angle, and the pair sin and cos times the speed to get the new speeds x / y.

jsFiddle: https://jsfiddle.net/jacquesc/wd5aa1wv/6/

Main part:

  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 that I switched from setInterval to requestAnimationFrame , which will contain no more than one update for each frame. Ideally, you would like to calculate the movement based on the actual time elapsed since the last update, and not rely on the fact that it is always the same.

Update

Use of point 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; } 
+3
source

Source: https://habr.com/ru/post/1242499/


All Articles