【问题标题】:How to have ball bounce off paddle in right directions not as a straight line如何让球在正确的方向而不是直线上从桨上反弹
【发布时间】:2017-11-29 04:09:15
【问题描述】:

请原谅我对 JS 非常陌生。 我创建了一个非常简单的画布,其中一个矩形由 keydownevents 控制,而一个矩形只是在 rAF 中围绕画布移动。

如果移动的球击中用户矩形以碰撞并反弹回来,但以正确的角度反弹,我现在希望它不会像我尝试过但失败的那样反向击中。

我知道我需要计算它在用户矩形上的位置,但我不知道从哪里开始或如何开始 有人可以指出我正确的方向或给我一个小sn-p该做什么吗?

我将省略 Keycode 函数,因为它们不需要。

function init() {

    canvas = document.getElementById("canvasdemo");
    ctx = canvas.getContext("2d");

    canvasWidth = canvas.width;
    canvasHeight = canvas.height;

    drawSquare();
}

function drawSquare() {

  ctx.clearRect(0, 0, canvasWidth, canvasHeight);
  // user controlled square
  ctx.fillStyle = "blue";
  ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
  requestAnimationFrame(drawSquare); 


  if (rect.x + rect.vx > canvasWidth - 20 || rect.x + rect.vx < 0)
    rect.vx = -rect.vx;
  if (rect.y + rect.vy > canvasHeight - 20 || rect.y + rect.vy < 0)
    rect.vy = -rect.vy;

  rect.x += rect.vx;  
  rect.y += rect.vy;   

  // Moving Square 1 (left-right):

  requestAnimationFrame(squareTwo);

   if (dir1 == "right") {
      if (xpos < canvasWidth - 35) {
        xpos += 2;
      }else{
        dir1 = "left";
      }
    }

    if (dir1 == "left") {
      if (xpos>0) {
        xpos -= 2;
      }else{
        dir1 = "right";
      }
   }
}
// Second square
 function squareTwo() {

    ctx.fillStyle = "black";
    ctx.fillRect(xpos, ypos, 35, 35);
    ctx.fill();    
}

【问题讨论】:

  • 真正从不动的桨上反弹需要计算球撞击桨的入射角,并让它以相同但反射的角度反弹。如果您想根据桨移动的速度或方向来影响角度,那么这将变得更加复杂,并且将涉及更详细的物理。
  • rect在哪里定义?你的画布的宽度和高度是多少?
  • 什么有效,什么无效?你的问题到底是什么?
  • 好的 jfriend00 如果桨在进入移动桨之前是静态的,那么基础是什么?我将变量留在 Shashank 以节省空间,一切都被定义为主函数之外的全局变量。 var rect = { x: 250, y: 140, w: 20, h: 20, vx: 0, vy: 0 };画布 w = 550, h = 300
  • 桑德我的问题是我如何让一个移动的球以正确的角度从桨上反弹,而填充是 a) 静态和 b) 移动与用户控制

标签: javascript canvas


【解决方案1】:

要计算你可以做的反射 -

首先,将法线定义为基于焊盘角度的向量:

function getNormal(a) {
    return {
        x: Math.sin(a),    // this will be 90° offset from the
        y: -Math.cos(a)    // incoming angle
    }
}

然后使用点积计算与传入向量的入射角:

function reflect(n, v) {
    var d = 2 * dot(v, n);   // calc dot product x 2
    v.x -= d * n.x;          // update vectors reflected by
    v.y -= d * n.y;          // normal using product d
    return v
}

// helper, calc dot product for two vectors
function dot(v1, v2) {
    return v1.x * v2.x + v1.y * v2.y
}

然后你需要一个命中测试来触发反射。击中时,将传入矢量反射到与焊盘成 90° 切线的法线上。

一个简单的演示:

function getNormal(a) {
  return {
    x: Math.sin(a),
    y: -Math.cos(a)
  }
}

function reflect(n, v) {
  var d = 2 * dot(v, n);
  v.x -= d * n.x;
  v.y -= d * n.y;
  return v
}

function dot(v1, v2) {
  return v1.x * v2.x + v1.y * v2.y
}

// --- for demo only ---
var ctx = document.querySelector("canvas").getContext("2d"),
    balls = [], padAngle = 0, angleDlt = 0.005;

function Ball() {         // a Ball object for demo (replace or use existing)
  var me = this;
  init();
  this.update = function() {
    if (this.posX < 0 || this.posX > 500 || this.posY < -4 || this.posY > 150) init();
    this.posX += this.x;
    this.posY += this.y;
    ctx.rect(this.posX - 2, this.posY - 2, 4, 4);
  };
  
  function init() {
    me.posX = Math.random() * 100 + 200;
    me.posY = -4;
    me.x = Math.random() - 0.5;
    me.y = 1;
    me.hit = false;
  }
}

// init some balls
for(var i = 0; i < 4; i++) balls.push(new Ball());

// animate demo
(function loop() {

  ctx.clearRect(0, 0, 500, 150);
  
  // speeds up frames but preserves some accuracy
  for(var subframes = 0; subframes < 3; subframes++) {

    ctx.beginPath();
    
    // render pad
    padAngle += angleDlt;
    if (padAngle < -Math.PI * 0.2 || padAngle > Math.PI * 0.2) angleDlt = -angleDlt;
    drawPad(padAngle);
    
    // get normal
    var normal = getNormal(padAngle);
    
    // hit test using the pad's path - this is where we do the reflection
    for(var i = 0, ball; ball = balls[i++];) {
      if (!ball.hit && ctx.isPointInPath(ball.posX, ball.posY)) {
        ball.hit = true;
        reflect(normal, ball);
      }
    }

    // update balls
    for(var i = 0, ball; ball = balls[i++];) ball.update();
  }
  
  ctx.fill();
  requestAnimationFrame(loop)
})();

function drawPad(angle) {
  ctx.translate(250, 100);
  ctx.rotate(angle);
  ctx.rect(-50, -3, 100, 6);
  ctx.setTransform(1,0,0,1,0,0);
}
&lt;canvas width=500&gt;&lt;/canvas&gt;

【讨论】:

  • 感谢@Ken Fyrstenberg 非常详细的回复,我认为这超出了我的想象,但我猜也没有更简单的方法吗?
  • @joshuaaron 很难找到不处理向量的解决方案。但是,如果您研究链接的演示,您会发现您不必处理太多数学问题。大多数情况下,您将处理碰撞检测。你已经在你的代码中使用了向量,所以你快到了....
  • @KenFyrstenberg 我已经复制并开始研究代码,我一直在看演示,谢谢。
  • 函数反射(n, v) { var d = 2 * dot(v, n); v.x -= d * n.x; v.y -= d * n.y; return v } @KenFyrstenberg 你能解释一下这个部分/它是什么以及它为什么起作用吗?
  • @joshuaaron(对不起,我错过了这条评论)点积产生一个“内积”,它告诉你两个向量之间的角度关系(在这种情况下是法线和传入向量) 或余弦,即负数表示相反的方向,0 表示相同的方向等。将其乘以 2 将覆盖传入的增量 + 反转时的新增量(如果 1 它几乎会“冻结”)。然后使用法线与内积来否定和改变向量的方向。
猜你喜欢
  • 1970-01-01
  • 2013-11-30
  • 2022-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-12
  • 1970-01-01
相关资源
最近更新 更多