篮球物理碰撞检测和反弹物理

Posted

技术标签:

【中文标题】篮球物理碰撞检测和反弹物理【英文标题】:Basketball Physics Collision Detection and Bounce Physics 【发布时间】:2018-01-28 06:40:46 【问题描述】:

我正在制作一个篮球物理模拟器。我正在使用参数方程来计算球的路径。我很难检测与篮筐前部、篮板、杆和球场地板(帆布底部)的碰撞。另外,我想让球在击中这些物体时反弹,但我很难。任何人都可以为这个问题提供一些帮助吗?

这里是sn-p的代码:

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.transform(1, 0, 0, -1, 0, canvas.height)
ctx.translate(canvas.width / 2, canvas.height / 2);

var speed = 5;
var gravity = 16;
var bounce = 10;

var mouseX = 0;
var mouseY = 0;

var stage = 1;

var x = 0;
var y = 0;
var xOrgn = 0;
var yOrgn = 0;
var xClk = 175;
var yClk = 100;
var mag = 0;
var ang = 0;
var xVel = 0;
var yVel = 0;
var time = 0;

function drawBall() 
  ctx.beginPath();
  ctx.arc(x, y, 12, 0, Math.PI * 2);
  ctx.fillStyle = "#FF8C00";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x, y + 12);
  ctx.lineTo(x, y - 12);
  ctx.strokeStyle = 'black';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x - 12, y);
  ctx.lineTo(x + 12, y);
  ctx.strokeStyle = 'black';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x - 8, y - 8);
  ctx.bezierCurveTo(x - 2, y - 4, x - 2, y + 4, x - 8, y + 8);
  ctx.strokeStyle = 'black';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x + 8, y - 8);
  ctx.bezierCurveTo(x + 2, y - 4, x + 2, y + 4, x + 8, y + 8);
  ctx.strokeStyle = 'black';
  ctx.stroke();


function drawHoop() 
  ctx.beginPath();
  ctx.rect(228, -160, 12, 172);
  ctx.fillStyle = "#191919";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.rect(222, -12, 6, 80);
  ctx.fillStyle = "#666666";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.rect(171, -6, 51, 6);
  ctx.fillStyle = "#e50000";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.arc(171, -3, 3, 0, Math.PI * 2);
  ctx.fillStyle = "#e50000";
  ctx.fill();
  ctx.closePath();


function drawCursor() 
  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(mouseX - 12, mouseY);
  ctx.lineTo(mouseX + 12, mouseY);
  ctx.strokeStyle = '#00cd00';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(mouseX, mouseY - 12);
  ctx.lineTo(mouseX, mouseY + 12);
  ctx.strokeStyle = '#00cd00';
  ctx.stroke();
  ctx.closePath();


function calcVel() 
  mag = Math.sqrt((Math.pow(xClk - xOrgn, 2) + Math.pow(yClk - yOrgn, 2)) / 4);
  ang = Math.atan((yClk - yOrgn) / (xClk - xOrgn));
  xVel = mag * Math.cos(ang);
  yVel = mag * Math.sin(ang);


function draw() 
  ctx.clearRect(-(canvas.width / 2), -(canvas.height / 2), canvas.width, canvas.height);
  ctx.canvas.addEventListener('mousemove', function(event) 
    mouseX = event.clientX - ctx.canvas.offsetLeft - canvas.width / 2;
    mouseY = -event.clientY + ctx.canvas.offsetTop + canvas.height / 2;
  );
  drawBall();
  drawHoop();
  if (stage === 1) 
    x = mouseX;
    y = mouseY;
    ctx.canvas.addEventListener('click', function(event) 
      xOrgn = x;
      yOrgn = y;
      stage = 2;
    );
   else if (stage === 2) 
    drawCursor();
    ctx.canvas.addEventListener('click', function(event) 
      xClk = mouseX;
      yclk = mouseY;
      calcVel();
      time = 0;
      stage = 3;
    );
   else if (stage === 3) 
    x = xVel * time + xOrgn;
    y = -gravity * Math.pow(time, 2) + yVel * time + yOrgn;
    time = time + speed * 0.01;
  


setInterval(draw, 10);
canvas 
  background: white;
<canvas id="myCanvas" width="480" height="320"></canvas>

这是 jsfiddle 中的代码:JSfiddle

【问题讨论】:

可能的帮助:跟踪新旧 x,y 值,然后查看球是否“穿过”篮筐或篮板。因为您将 x,y 值增加超过1,你不能检查是否相等:球可能在一次平局中高于篮筐,在下一次平局中低于篮筐。 【参考方案1】:

您只需要在“阶段 3”中添加碰撞检测。由于所有内容的位置和大小都是硬编码的,因此逐项添加碰撞检测非常简单。 (但如果你把这变得更复杂,我会添加一些变量或对象,以便更容易地将事物的位置作为变量等)。

当球击中篮板时,我添加了水平弹跳,以及从地板上垂直弹跳。其他都类似。

(请注意,使这些成为可能的是迭代地更新 x 和 y(以及 xVel、yVel)而不是从确定的方程。结果是相同的,但计算和动态行为更容易)。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.transform(1, 0, 0, -1, 0, canvas.height)
ctx.translate(canvas.width / 2, canvas.height / 2);

var speed = 5;
var gravity = 16;
var bounce = 10;

var mouseX = 0;
var mouseY = 0;

var stage = 1;

var x = 0;
var y = 0;
var xOrgn = 0;
var yOrgn = 0;
var xClk = 175;
var yClk = 100;
var mag = 0;
var ang = 0;
var xVel = 0;
var yVel = 0;
var time = 0;

function drawBall() 
  ctx.beginPath();
  ctx.arc(x, y, 12, 0, Math.PI * 2);
  ctx.fillStyle = "#FF8C00";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x, y + 12);
  ctx.lineTo(x, y - 12);
  ctx.strokeStyle = 'black';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x - 12, y);
  ctx.lineTo(x + 12, y);
  ctx.strokeStyle = 'black';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x - 8, y - 8);
  ctx.bezierCurveTo(x - 2, y - 4, x - 2, y + 4, x - 8, y + 8);
  ctx.strokeStyle = 'black';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(x + 8, y - 8);
  ctx.bezierCurveTo(x + 2, y - 4, x + 2, y + 4, x + 8, y + 8);
  ctx.strokeStyle = 'black';
  ctx.stroke();


function drawHoop() 
  ctx.beginPath();
  ctx.rect(228, -160, 12, 172);
  ctx.fillStyle = "#191919";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.rect(222, -12, 6, 80);
  ctx.fillStyle = "#666666";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.rect(171, -6, 51, 6);
  ctx.fillStyle = "#e50000";
  ctx.fill();
  ctx.closePath();

  ctx.beginPath();
  ctx.arc(171, -3, 3, 0, Math.PI * 2);
  ctx.fillStyle = "#e50000";
  ctx.fill();
  ctx.closePath();


function drawCursor() 
  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(mouseX - 12, mouseY);
  ctx.lineTo(mouseX + 12, mouseY);
  ctx.strokeStyle = '#00cd00';
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.moveTo(mouseX, mouseY - 12);
  ctx.lineTo(mouseX, mouseY + 12);
  ctx.strokeStyle = '#00cd00';
  ctx.stroke();
  ctx.closePath();


function calcVel() 
  mag = Math.sqrt((Math.pow(xClk - xOrgn, 2) + Math.pow(yClk - yOrgn, 2)) / 4);
  ang = Math.atan((yClk - yOrgn) / (xClk - xOrgn));
  xVel = mag * Math.cos(ang);
  yVel = mag * Math.sin(ang);


function draw() 
  ctx.clearRect(-(canvas.width / 2), -(canvas.height / 2), canvas.width, canvas.height);
  ctx.canvas.addEventListener('mousemove', function(event) 
    mouseX = event.clientX - ctx.canvas.offsetLeft - canvas.width / 2;
    mouseY = -event.clientY + ctx.canvas.offsetTop + canvas.height / 2;
  );
  drawBall();
  drawHoop();
  if (stage === 1) 
    x = mouseX;
    y = mouseY;
    ctx.canvas.addEventListener('click', function(event) 
      xOrgn = x;
      yOrgn = y;
      stage = 2;
    );
   else if (stage === 2) 
    drawCursor();
    ctx.canvas.addEventListener('click', function(event) 
      xClk = mouseX;
      yclk = mouseY;
      calcVel();
      time = 0;
      stage = 3;
    );
   else if (stage === 3) 
    //x = xVel * time + xOrgn;
    // update x from it's own value so we can vary the xVel.
    x += xVel * speed * 0.01;

    //y = -gravity * Math.pow(time, 2) + yVel * time + yOrgn;
    // update yVel and y iteratively instead of this determined calculation
    //y = -gravity * Math.pow(time, 2) + yVel * time + yOrgn;
    yVel -= gravity * 0.1;
    y += yVel * speed * 0.01;

    // do a collision check: the backboard.
    if (x > 222 - 12 && y > -12 && y < 62) 
	    xVel *= -1;
    
    // with floor
    if(y <= -142) 
        y = -142;
        yVel *= -1;
    
    time = time + speed * 0.01;
  


setInterval(draw, 10);
canvas 
  background: white;
&lt;canvas id="myCanvas" width="480" height="320"&gt;&lt;/canvas&gt;

您可以类似地处理您想要的任何其他碰撞

【讨论】:

感谢您的帮助。我将使用变量来表示对象的位置。这对我很有用! 很高兴它的工作!我所说的变量的意思是,如果您可以输入类似if (x &gt; backboard.x) 或更好的if (backboard.intersects(x,y)) 之类的内容,那就太好了。您可以像以前一样使用变量(这是一个很好的第一步!),但如果您可以这样做,它将更具可读性并且“幻数”更少。

以上是关于篮球物理碰撞检测和反弹物理的主要内容,如果未能解决你的问题,请参考以下文章

cocos creator基础-(二十)物理引擎碰撞检测

Sprite Kit 碰撞检测和物理

物理AABB物理碰撞检测

2022-04-20 Unity入门7——物理系统之碰撞检测

使两个物理对象不碰撞,但在 Unity 中检测碰撞

如何在没有物理的情况下检测碰撞