如何让球在正确的方向上反弹而不是直线

时间:2015-04-28 04:44:05

标签: javascript canvas

原谅我在这里完全没有作为JS的新手。 我创建了一个非常简单的画布,其中一个矩形由keydownevents控制,另一个矩形在rAF中围绕画布移动。

我现在想要的是,如果移动的球击中用户矩形以碰撞并反弹但是以正确的角度反弹它不会像我尝试过的那样反击并且失败了。

我知道我需要计算它在用户rect上的位置,但我不知道从哪里开始或如何 有人能指出我正确的方向还是给我一小段做什么?

我会忽略空间的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();    
}

1 个答案:

答案 0 :(得分:5)

要计算你可以做的反射 -

首先,根据打击垫角度将法线定义为矢量:

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

然后使用dot-product计算入射矢量的入射角:

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);
}
<canvas width=500></canvas>