为什么这个球在一个圆圈内不能正常弹跳?

sfa*_*ota 5 javascript math physics

请看这个小提琴:https://jsfiddle.net/sfarbota/wd5aa1wv/2/

我试图让球以正确的角度在圆圈内反弹而不会失去速度.我想我有碰撞检测,但我面临两个问题:

  1. 每次弹跳球都会减速,直到最终停止.
  2. 弹跳的角度似乎是不正确的.

这部分是基于这里给出的答案:https://stackoverflow.com/a/12053397/170309但我不得不从Java翻译,并且从他们看起来无关紧要的例子中跳过了几行.

这是代码:

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);
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

CSS:

canvas { background: #eee; }
Run Code Online (Sandbox Code Playgroud)

jca*_*ron 5

我的数学很生疏,所以我不太确定如何仅使用点积来计算球的新轨迹,但我确定您可以使用相关的三角函数来计算它:用于atan2计算到球的角度碰撞点和当前轨迹角度,使用这两个来计算新角度,以及一对sincos乘以速度得到新的 x/y 速度。

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

重要的部分是:

    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);
    }
Run Code Online (Sandbox Code Playgroud)

另请注意,我从 切换setIntervalrequestAnimationFrame,这将确保每帧不超过一个更新。理想情况下,您希望根据自上次更新以来经过的实际时间计算运动,而不是依赖它始终相同。

更新

使用点积:

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;
    }
Run Code Online (Sandbox Code Playgroud)