【问题标题】:Calculate random bounce angles计算随机反弹角度
【发布时间】:2013-07-26 20:40:49
【问题描述】:

我想让球在每次撞到墙上时改变反弹角度。

它会根据它撞到墙壁中间的距离而改变......

现在,当 X 和 Y 撞击表面时,我正在对 X 和 Y 的变化进行硬编码...我的目标是从当前 X 和 Y 获取度数,对度数应用更改(现在我在度数上添加一个随机数),然后计算 X 和 Y 的新增量值。我知道如何获取 newX 和 newY,但不知道如何获取增量值。

绿色是x的起始y(5,5)...蓝色是(4,4)的下一帧。

  • 因此我根据此计算出度数为45
  • 然后在度数上添加一个随机数。
  • 然后,我想获得新的 x 和 y 坐标。于是我关注了this method...

currX (5) - wallX (0) = distX (5)

currY (5) - wallY (0) = distY (5)

取我的角度的余弦 + 随机增量,我们会说 55 度,* distX

cos(55 degrees) = .5735....5735 x distX (5) = 2.86

我的角度的罪孽 * distY

sin(55 degrees) = .8191....8191 x distY (5) = 4.09

newX = cos result (2.86) + originX (5) = 7.86

newY = sin result (4.09) + originY (5) = 9.09

newX, newY = (7.86, 9.09)

好的...所以我有我的新坐标...

但这些并不等于我的 xy 的新增量值应该基于我的入射角。

代码 sn-p: 你可以看到我正在硬编码 x,y 增量(dragger.x += 2; )

        function tick() {
            var rand = Math.floor((Math.random()*10)+1);

            console.log("ticking..." + rand);
            if (dragger.x >= 400-20) {
                dragger.xDir = "right";         
            }
            if (dragger.x < 20) {
                dragger.xDir = "left";      
            }       
            if (dragger.y >= 150-20) {
                dragger.yDir = "up";
            }
            if (dragger.y < 20) {
                dragger.yDir = "down";
            }

            var oldX = dragger.y;
            var oldY = dragger.x;

            if (dragger.xDir == "left") {
                dragger.x += 2; 
            }
            else {
                dragger.x -= 2;
            }
            if (dragger.yDir == "up") {
                dragger.y -= 2;
            }
            else {
                dragger.y += 2;
            }
            //post update...
            var newX = dragger.y;
            var newY = dragger.x;                   

            var angle = getAngle(newX, oldX, newY, oldY)
            angle+=rand;

            $('#getAngle').empty();
            $('#getAngle').append("bounce angle (degrees): " + angle);


            //console.log(xDir);
            // update the stage:
            stage.update();
        }

        function getAngle(x2, x1, y2, y1) {             
            var deltaX = Math.abs(x2-x1);
            var deltaY = Math.abs(y2-y1);
            var radians = Math.atan2(deltaX, deltaY);
            var degrees = radians * (180/Math.PI);
            return degrees;
        }

【问题讨论】:

    标签: javascript angle


    【解决方案1】:

    由于它的特殊性,这是一个非常有趣的问题。

    用编程语言让球弹起来很容易。 Like this example.

    但很明显,您的问题不是“让它发挥作用”;您想要明确控制坐标和角度,以便您可以根据您的想法更改它们。

    因为我很容易受到nerd sniping 的攻击,所以我磨练了我的几何技能,并提出了以下伪代码片段(我从头开始制作这个以确保我完全控制):

    直觉

    伪代码

     theta    = starting angle
     a        = current x-coordinate of ball
     b        = current y-coordinate of ball
     quadrant = quadrant-direction to which ball is moving
    
     /> Determine number between 1 and 360: theta
     /> Calculate quadrant
    
       .> 0-90 :   quadrant 1:    horizontal: 90-a   vertical: b      alpha: 90 - theta
       .> 90-180:  quadrant 4:    horizontal: 90-a   vertical: 30-b   alpha: theta - 90
       .> 180-270: quadrant 3:    horizontal: a      vertical: 30-b   alpha: 270 - theta
       .> 270-360: quadrant 2:    horizontal: a      vertical: b      alpha: theta - 270
    
     /> Calculate distance to side       |
     /> Calculate distance to top/bottom |
    
       .> to side:       n(alpha) = horizontal/cos(alpha)
       .> to top/bottom: m(alpha) = vertical  /sin(alpha)
    
     /> Determine where ball is going to hit (n = side, m = top/bottom)
    
       .> n >= m  : bounces at top/bottom
       .> m >= n  : bounces at side
    
          .> switch (quadrant)
             .> 1 : n = right side     m = top      
             .> 2 : n = left side      m = top
             .> 3 : n = left side      m = bottom
             .> 4 : n = right side     m = bottom
    
     /> Calculate coordinates of hit
    
        /> Define new angle
    
          // Normally, angle of impact = angle of reflection
          // Let's define the angle of impact with respect to the origin (0,0)
    
       .> switch (quadrant)
             .> 1 : 
                     .> n >= m (at top/bottom) : x = a + vertical*tan(alpha)   y = 0                         theta = 180-theta
                     .> m >= n (at side)       : x = 90                        y = b - horizontal*tan(alpha) theta = 270+alpha
             .> 2 : 
                     .> n >= m (at top/bottom) : x = a - vertical/tan(alpha)   y = 0                         theta = 270-alpha
                     .> m >= n (at side)       : x = 0                         y = b - horizontal*tan(alpha) theta = 90-alpha
             .> 3 : 
                     .> n >= m (at top/bottom) : x = a - vertical/tan(alpha)   y = 30                        theta = 270+alpha
                     .> m >= n (at side)       : x = 0                         y = b + horizontal*tan(alpha) theta = 90+alpha
             .> 4 : 
                     .> n >= m (at top/bottom) : x = a + vertical/tan(alpha)   y = 30                        theta = 90-alpha
                     .> m >= n (at side)       : x = 90                        y = b + horizontal*tan(alpha) theta = 270-alpha
    
     /> Define new coordinates (for reusage of function)
    
        .> a = x
        .> b = y
    
       .> (optional) if you would like the angles to differ, enter extra term here:
    
             .> extra = ...
             .> theta = theta + extra
    

    实施此代码将使您能够轻松地处理度数,并且仍然能够确定坐标。

    它的工作原理如下:

    • 首先确定小球的初始位置(a,b)和初始方向(theta)

    • 现在程序将计算:

      • 球要打到哪里
      • 击球时球的坐标是什么
      • 新的反射角度是什么(这是您要更改的部分)

    然后它重新开始计算新的命中。

    在 JavaScript 中,代码如下所示:

    代码

    var width = 500;
    var height = 200;
    var extra = 0;
    var a;
    var b;
    var x;
    var y;
    var angle;
    var n;
    var m;
    var quadrant;
    var horizontal;
    var vertical;
    var alpha;
    var side;
    var topbottom;
    var sides;
    var i = 1;
    
      var txt=document.getElementById("info");
      txt.innerHTML="x: "+a+"<br>y: "+b+"<br>angle: "+angle+"<br>quadrant: "+quadrant;
    
    function buttonClick()
    {
      if (i == 1)
      {
        a = 75;
        b = 75;
        //determine first angle randonmly
        angle = Math.floor((Math.random()*360)+1);;
      } else
      {
        a = xcoord();
        b = ycoord();
      }
      var oldAngle = angle;  
      angle = findNewCoordinate(a, b, angle);
    
      sides = hitWhere();
    
      var txt=document.getElementById("info");
        txt.innerHTML="x: "+a+"<br>y: "+b+"<br>horizontal: "+horizontal+"<br>vertical: "+vertical+"<br>n: "+n+"<br>m: "+m+"<br>angle: "+oldAngle+"<br>alpha: "+alpha+"<br>quadrant: "+quadrant+"<br>side: "+topbottom+side+"<br>"+sides+"<br>"+i;
        i++;
    }
    
    function findNewCoordinate(a, b, angle)
    {
        if (angle >= 0 && angle < 90) { quadrant = 1; horizontal = width-a; vertical = b; alpha = (90 - angle); }
        else if (angle >= 90 && angle < 180) { quadrant = 4; horizontal = width-a; vertical = height-b; alpha = (angle-90);  }
        else if (angle >= 180 && angle < 270) { quadrant = 3; horizontal = a; vertical = height-b; alpha = (270-angle);  }
        else if (angle >= 270 && angle <= 360) { quadrant = 2; horizontal = a; vertical = b; alpha = (angle-270);  }
    
    
           var cosa = Math.cos(alpha * Math.PI / 180);
           var sina = Math.sin(alpha * Math.PI / 180);
           var tana = Math.tan(alpha * Math.PI / 180);
    
           var tant = Math.tan(angle * Math.PI / 180);
    
           n = horizontal/cosa;
           m = vertical/sina;
    
    
        switch (quadrant)
        {
            case 1:  
                if (m >= n) //hit at side
                {
                    y = b - horizontal*tana; 
                    x = width;               
                    angle = 270+alpha;       
                } else
                {
                    y = 0;                  
                    x = a + vertical*tant;   
                    angle = 180-angle;       
                } 
                side = "right side"; topbottom = "top";
                break;
            case 2:
                if (m >= n)  //hit at side
                {
                    y = b-horizontal*tana;   
                    x = 0;                   
                    angle = 90-alpha;        
                } else
                {
                    y = 0;                   
                    x = a - vertical/tana;   
                    angle = 270-alpha;       
                } 
                side = "left side"; topbottom = "top";
                break;
            case 3: side = "left side"; topbottom = "bottom";
                if (m >= n)  //hit at side
                {
                    x = 0;                   
                    y = b + tana*horizontal; 
                    angle = 90+alpha;        
                } else
                {
                    y = height;              
                    x = a - vertical/tana;   
                    angle = 270+alpha;       
                } break;
            case 4: side = "right side"; topbottom = "bottom";
                if (m >= n)  //hit at side
                {
                    y = b+horizontal*tana; 
                    x = width;             
                    angle = 270-alpha;     
                } else
                {
                    y = height;            
                    x = a + vertical/tana; 
                    angle = 90-alpha;      
                } break;
        }
    
        //add extra degrees to the angle (optional)
        angle += extra;
    
        context.beginPath();
        context.arc(a, b, 5, 0, Math.PI*2, true); 
        context.stroke();
        context.closePath();
        context.fill();
    
        drawLine(a,b,x,y);
    
        return angle;
    }
    

    重要

    请注意,制作弹跳程序的方法还有很多。但是,因为我在没有'shortcuts' 的情况下以几何方式解决了这个问题,所以我的程序的独特特性使您可以很容易地根据自己的喜好进行更改:

    • 您可以轻松地为反弹角度提供额外的角度(使用var extra)。
    • 您可以随时更改球的运动方式(反弹时、反弹后等)
    • 您可以显式访问球的坐标
    • 所有单位都是常规单位(以度数和坐标表示;因此易于理解和直观)。

    另外请注意,我没有使程序非常简洁,因为这根本不是我的目标。我想创建一个弹跳球程序,虽然很长,但它是其背后几何直觉的精确实现。

    Demo

    您可以在JSFiddle 中找到我的程序演示。 请注意,起始角度是随机确定的。因此重新启动程序会给出不同的角度。

    嗯,就是这样。

    祝你程序的其余部分顺利!

    【讨论】:

      【解决方案2】:

      我们知道

      distance = average velocity x time //if acceleration is constant
      

      因此

      time = distance / average velocity
      

      将这些知识应用于二维场(距离)意味着我们必须做两件事:

      • 应用毕达哥拉斯定理求新坐标的距离
      • 计算“新”速度

      在应用毕达哥拉斯定理之前,我们必须知道移动的方向:

      现在要找到到新坐标的距离,我们应用pythagoras theorem

      伪代码

      //Change in coordinates
      dx = Math.abs(newX - oldX);
      dy = Math.abs(newY - oldY);
      
      //Distance to travel
      distance = Math.sqrt( Math.pow(dx, 2) + Math.pow(dy,2) );
      
      //Units per increase
      // time = distance / average velocity
      
      velocity = ?;
      time = distance / velocity;
      
      //Now to find x+= .. and y+= .. we apply our knowledge of direction
      //Together with our knowledge of the time it takes
      
      case north east:        x += (dx / time);           y += (dy / time);
      
      case south east:        x += (dx / time);           y -= (dy / time);
      
      case north west:        x -= (dx / time);           y -= (dy / time);
      
      case south west:        x -= (dx / time);           y += (dy / time);
      

      现在注意xy 代表移动球的坐标。 这意味着我们必须重复x += ..y += .. 的值time 次才能到达新坐标。

      因此您可以执行以下操作:

      for (int i = 0; i < time; i ++)
      {
          switch (direction)
          {
                 case "north east":  x += (dx / time);  y += (dy / time); break;
      
                 case "south east":  x += (dx / time);  y -= (dy / time); break;
      
                 case "north west":  x -= (dx / time);  y -= (dy / time); break;
      
                 case "south west":  x -= (dx / time);  y += (dy / time); break;
          }
      }
      

      另请注意,velocity = ? 尚未由您指定。你可以让它有一个恒定的速度(摩擦力 = 0),或者你可以实现某种模型来模拟摩擦力。

      我希望这能回答你的问题。

      PS。这个答案实际上是我其他答案的派生,因为我已经在我的其他答案中指定了方向和像素距离,因此到x += ..y += .. 的步骤实际上非常小/简单。

      【讨论】:

        【解决方案3】:

        取决于它进入的角度......所以基本上是为了让球从墙上反弹,只需反转它进入的角度,例如如果使用速度,如果它是3,那么当它与墙壁碰撞时将它设为-3,因此球将以与墙壁碰撞之前相同的角度从墙壁反弹......

        我希望这会有所帮助......祝你好运

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-09
          • 2018-02-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多