Why are some of my angles incorrect when changing?

133 views Asked by At

I have programed a program with JavaScript and the DOM that shows a ball bouncing in a box; it is a canvas element. The only things that do not work correctly are the angles. This is not noticeable until the angles are small, when it is clear the ball did not bounce back correctly from the box. The angle may be coming to a box gradually, and then bounce very steeply back. This seems like perhaps instead of the angle-in=angle-out, the angle that was being headed from was what was output angle. This would be equivalent of an angle in and its compliment out. The problem seems to happen with only half the types of bounces: it might not happen on one wall coming in a direction, but would on another wall coming in a direction.

Illustration of possible problem : https://i.stack.imgur.com/WviEd.gif

I have posted all the code for ability for the test of the code, and a gradual angle is used, so the problem can be seen, but the angles that are the problem are in the checkAngle function.

<!doctype html>
<script src="code/chapter/15_game.js"></script>
<script src="code/game_levels.js"></script>
<script src="code/chapter/16_canvas.js"></script>

<canvas width="400" height="400"></canvas>
<script>
  var cx = document.querySelector("canvas").getContext("2d");

  var lastTime = null;
  function frame(time) {
    if (lastTime != null)
      updateAnimation(Math.min(100, time - lastTime) / 1000);
    lastTime = time;
    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
  var x = y = 200//, angle = 2 * Math.PI * Math.random();
  var angle = 2 * Math.PI / 40 + Math.PI;
  function checkAngle(angle) {
    if(x + 10 >= 400) {
      if(angle <= Math.PI)
        return angle = (Math.PI/2) + ((Math.PI / 2) - reduceAngle(angle));
      else if(angle > Math.PI)
        return angle = (3 * Math.PI / 2) - ((Math.PI / 2) - reduceAngle(angle));
    }else if(x - 10 <= 0) {
      if(angle <= Math.PI)
        return angle = Math.PI/2 - reduceAngle(angle);
      else if(angle > Math.PI)
        return angle = 3* Math.PI/2 + (Math.PI/2  - reduceAngle(angle));
    }else if(y - 10 <= 0) {
      if(angle >= 3 * Math.PI /2)
        return angle = Math.PI/2 - reduceAngle(angle);
      else if(angle < 3 * Math.PI/2)
        return angle = Math.PI - (Math.PI / 2 - reduceAngle(angle));
    }else if(y + 10 >= 400) {
      if(angle <= Math.PI/2)
        return angle = 2*Math.PI - (Math.PI / 2 - reduceAngle(angle));
      else if(angle > Math.PI/2)
        return angle = Math.PI + (Math.PI / 2 - reduceAngle(angle));
    }else
      return angle;
  }
  function reduceAngle(angle) {
    if(angle < Math.PI / 2) {
      return angle;
    }else{
      angle = angle - (Math.PI / 2);
      return reduceAngle (angle);
    }
  }
  function updateAnimation(step) {
    cx.clearRect(0, 0, 400, 400);
    cx.lineWidth = 4;
    cx.strokeRect(0, 0, 400, 400);
    angle = checkAngle(angle);
    x += Math.cos(angle) * step * 200;
    y += Math.sin(angle) * step * 200;
    cx.lineWidth = 2;
    cx.beginPath();
    cx.arc(x, y, 20, 0, 7);
    cx.stroke();
  }
</script>

2

There are 2 answers

2
Polyducks On

Judging by what I've seen in your drawings and demonstrations, the angles are being taken off the wrong axis. If approaching at 20 degrees, you'd expect the ball to leave at 180-20 degrees. Instead what it's doing is leaving at 90+20 degrees.

While I can't find the precise place in your code that makes this error, I felt I had to point this out in the hopes that someone can improve upon it.

0
AudioBubble On

When dealing with reflections in a non-rotated box you don't really need to deal with angles when reflecting. Just define an initial vector based on angle.

var vector = {
      x: speed * Math.cos(angle),
      y: speed * Math.sin(angle)
    };

Then you simply check bounds for x and y separately and inverse the slope-value for each axis:

  if (x - radius <= 0 || x + radius>= 400) {
    vector.x = -vector.x;      // reflect x
  }
  if (y - radius<= 0 || y + radius> 400) {
    vector.y = -vector.y;      // reflect y
  }

You can always adjust the angle on the fly by adding another vector with the delta-angle.

If your bounce box would not be 0° rotated, then check out this answer for vector-reflection.

For example

Using your code as a basis, this would be implemented like this:

var cx = document.querySelector("canvas").getContext("2d");
var lastTime = null;
var x, y;

// calculate an initial vector
var angle = 20 / 180 * Math.PI;   // angle -> radians
var speed = 5;
var vector = {
      x: speed * Math.cos(angle),
      y: speed * Math.sin(angle)
    };

x = y = 200;

function frame(time) {
  if (lastTime != null)
    updateAnimation(Math.min(100, time - lastTime) / 1000);
  lastTime = time;
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);


function checkAngle() {
  var dlt = 10 + 2 + 4;        //include radius and line-widths;
  if (x - dlt <= 0 || x + dlt >= 400) {
    vector.x = -vector.x;      // reflect x
  }
  if (y - dlt <= 0 || y + dlt > 400) {
    vector.y = -vector.y;      // reflect y
  }
}

function updateAnimation(step) {
  cx.clearRect(0, 0, 400, 400);
  cx.lineWidth = 4;
  cx.strokeRect(0, 0, 400, 400);
  
  x += vector.x;    // use our vector
  y += vector.y;
  checkAngle();     // test for hits
  
  cx.lineWidth = 2;
  cx.beginPath();
  cx.arc(x, y, 20, 0, 7);
  cx.stroke();
}
<canvas width="400" height="400"></canvas>