Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
234 views
in Technique[技术] by (71.8m points)

javascript - How to have ball bounce off paddle in right directions not as a straight line

Forgive my complete noobness here as very new to JS. I've created a really simple canvas with one rectangle being controlled by keydownevents and one just moving around the canvas in rAF.

I want to now have if the moving ball hits the users rectangle to collide and bounce back but to bounce at the right angle that it hits not just in reverse like i have tried and failed at.

I know i need to calculate where it hits on the users rect, but I have no idea where to start or how Can someone point me in the right direction or give me a small snippet of what to do?

I'll leave out the Keycode functions for space as they're not needed.

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();    
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

To calculate the reflection you can do -

First, define the normal as a vector based on the pads angle:

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

Then use dot-product to calculate the incident angle with the incoming vector:

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
}

Then you need a hit-test to trigger the reflection. When hit, reflect the incoming vector on the normal which is 90° tangent to the pad.

A simple demo:

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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...