ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 운동량 보존 법칙에 따른 공 충돌 모션 (Ball collision animation by code)
    알고리즘 2008.09.23 11:01



    refer to : http://cafe.naver.com/flashdev.cafe (김종헌 왕 초짜를 위한 액션)
    다시 정리하고 몇가지 추가해 보았다.
    ** 혹시 좀더 깊이 움직임에 대해 알아보고 싶다면, Basic 2D Vectors를 살펴보세요 **


    전제

     

    • 힘은 물체의 운동 상태(속력이나 방향)을 변화시킨다.
    • 미는 힘이 있으면 반드시 크기가 같은 힘을 받게 되어있다.
    • 힘에 의한 효과(가속도 = 속도의 변화)는 힘에 비례하고 질량에 반비례한다.
    • 힘이 작용하지 않을 때 충돌 전과 충돌 후의 운동량은 항상 같다.

     


    운동량 보존 법칙의 수식적 풀이



    가속도 :  단위 시간당 속도의 변화량

    a = △v/△t = (v' - v)/△t



    가속도의 법칙 : 가속도는 작용한 힘에 비례하고 질량에 반비례한다.

    a = F/m, F=m*a



    운동량 : 물체의 질량과 속도의 곱으로 나타내는 물리량의 하나, 밖에서 힘이 작용되지 않는 한, 물체 또는 물체가 몇개 모여서 된, 하나의 물체계(物體系)가 가지는 운동량의 합은 변하지 않는다.


    질량이 ma 이고 속도가 va 인 물체 A와 질량이 mb이고 속도가 vb인 물체 B 가 일직선 상에서 운동하여 충돌한 후 각각 va', vb'인 일직선상의 운동을 한다고 가정한다.



    물체가 충돌할 때 물체 사이에 작용하는 힘은 작용반작용의 관계이므로 -Fa = Fb로 쓸 수 있다.
    이를 가속도 법칙에 대입해 풀어쓰면,

    -ma*(va' - va)/△t = mb*(vb' - vb)/△t
    -ma*va'+ ma*va = mb*vb' - mb*vb
    ma*va + mb*vb = ma*va' + mb*vb'

    위 식의 좌변은 충돌전 두 물체의 운동량의 합이고 우변은 충돌 후 두 물체의 운동량의 합이다.


    이로써, 운동량 보존의 법칙을 수식으로 확인할 수 있다.



    반발계수의 수식적 풀이



    반발계수 :

    • 충돌하는 물체들의 반발 정도를 나타내는 반발계수는 속도나 질량에 관계없고, 두물체를 구성하는 물질에 따라 결정된다. 고로, 물체마다 고유의 반발계수를 가지고 있는 셈이다.
    • 충돌전 운동 에너지는 충돌하면서 탄성에너지와 열에너지, 소리에너지 등으로 변하지만, 두 물체가 반발되면서 탄성 에너지는 다시 운동 에너지로 변하게 된다. 이때, 얼마만큼 탄성에너지가 보존되어 다시 운동에너지로 변환되는지 반발계수로 표현하게 된다.
    • 충돌한 후 가까워지는 속도와 멀어지는 속도가 같다면, 충돌하면서 운동에너지가 전부 탄성에너지로 변했다가 그대로 운동에너지로 전환된 셈이다. 즉, 반발계수 e=1이 된다.
    • 반대로, 운동에너지가 열에너지나 소리에너지 등으로 모두 전환될 경우 두 물체는 붙어버리게 되어 반발 계수 e=0 이 된다.
      따라서, 반발계수는 0≤e≤1의 범위에 있게 된다.


    위 예제 그림에서 충돌 후에 서로 멀어지는 속도(충돌 후의 상대속도, vb' - va')와 충돌 전에 가까워지는 속도(충돌 전의 상대속도, va - vb)의 비를 반발계수 e로 표시한다.


    e=(vb' - va')/(va - vb) = -(va' - vb')/(va-vb)





    충돌 후의 속도 계산을 위한 수식적 풀이




    위에서 나온 두 가지 방정식을 보면 아래와 같습니다.

    ma*va + mb*vb = ma*va' + mb*vb' (1번식)
    e=(vb' - va')/(va - vb) = -(va' - vb')/(va-vb) (2번식)


    위 2번식을 vb'에 대한 식으로 바꾸어 1번식에 대입하면, va'에 대한 식을 구할 수 있다.

    ma*va + mb*vb = ma*va' + mb*(e*va - e*vb + va')
    ma*va + mb*vb = ma*va' + mb*e*va - mb*e*vb + mb*va'
    (ma + mb)*va' = ma*va  + mb*vb - mb*e*va + mb*e*vb
    (ma + mb)*va' = (ma-mb*e)*va + (mb+mb*e)*vb

    va' = (ma-mb*e)/(ma+mb)*va + (mb+mb*e)/(ma+mb)*vb



    마찬가지로, va'를 2번식에 대입하면, vb' 에 대한 식도 구할 수 있다.

    vb' = (ma+ma*e)/(ma+mb)*va + (mb-ma*e)/(ma+mb)*vb



    따라서, 일직선 상 충돌 전의 질량과 속도를 알면 충돌 후의 속도를 구할 수 있음을 알 수 있다.




    평면상 (2차원) 충돌계산



    일직선 상의 충돌 공식을 평면으로 확대해본다.



    위 두 공이 평면상에 충돌하였을 때 2 차원 움직임을 1차원 (일직선) 상의 움직임으로만 계산할 수 있도록 벡터분해해야 한다. 즉, 속도가 va인 물체 A는 vax, vay 로, 속도가 vb인 물체 B는 vbx, vby로 나눌 수 있다.
    그런데, 두 물체를 일직선상에 놓이게 하려면 새로운 좌표계를 가상으로 설정해야 한다. 물체 A와 B의 중심점을 이은 선을 x'계로 한 가상좌표계를 만들어보자.



    가상으로 설정한 x'선상이 x 선상과 이루는 각을 θ 라고 하면,
    물체 A의 x'선상의 속도는 vax' = vax*cosθ + vay*sinθ 이고,
    물체 B의 x'선상의 속도는 vbx' = vbx*cosθ + vby*sinθ 이다.

    위 식을 일직선상 충돌 공식에 대입해 보자.

    vaxp (충돌 후 x'선상 a의 속도)

    vaxp = (ma-mb*e)/(ma+mb)*(vax*cosθ + vay*sinθ)
    + (mb+mb*e)/(ma+mb)*(vbx*cosθ + vby*sinθ)


    vbxp (충돌 후 x'선상 b의 속도)

    vbxp = (ma+ma*e)/(ma+mb)*(vax*cosθ + vay*sinθ)
    + (mb-ma*e)/(ma+mb)*(vbx*cosθ + vby*sinθ)


    이렇게 x'선상의 충돌에 의한 속도 변화를 공식으로 표현할 수 있다.

    다음은 y'선상의 속도 변화인데, 이는 충돌이 x'선상에서 일어나기 때문에 y' 선상의 속도는 충돌에 의한 속도 변화는 없게 된다.



    물체 A의 y'선상의 속도 va'y' 는

    vayp = vay*cosθ - vax*sinθ


    물체 B의 y'선상의 속도 vb'y' 는

    vbyp = vby*cosθ - vbx*sinθ


    마지막으로, 가상의 좌표계를 원래 좌표계로 바꾸면 2차원 충돌 후 속도를 알 수 있게 된다.



    물체의 x 방향 운동 vax 와 y 방향 운동 vay는

    vax = vaxp*cosθ - vayp*sinθ
    vay = vaxp*sinθ + vayp*cosθ



    마찬가지로 물체 B의 운동은

    vbx = vbxp*cosθ - vbyp*sinθ
    vby = vbxp*sinθ + vbyp*cosθ



    이로써, 평면상의 충돌 후 속도에 대한 정리가 끝났다.
    바로 code (processing)로 적용해 보자.

    Ball ballA; //Ball A
    Ball ballB; //Ball B
    float e = 1; //elastic modulus
    
    void setup() {
      
      size(400,400);
      background(255,255,255);
      ellipseMode(CENTER);
      
      ballA = new Ball(random(10, 30));
      ballA.clr = #55ff00;
      ballA.x = 100;
      ballA.y = 100; 
      ballB = new Ball(random(10, 30));
      ballB.clr = #00ccff;
      ballB.x = 200;
      ballB.y = 200;
      
    }
    
    void draw() {
      
      background(255,255,255);
      ballA.update();
      ballB.update();
      
      float dx = ballA.x - ballB.x;
      float dy = ballA.y - ballB.y;
      float dab = abs(sqrt(dx*dx + dy*dy));
      
      if(dab <= ballA.r+ballB.r) {
        
        float sinTheta = dy / abs(sqrt(dx*dx + dy*dy));
        float cosTheta = dx / abs(sqrt(dx*dx + dy*dy));
        float vxAp = (ballA.m - e*ballB.m)/(ballA.m + ballB.m)*(ballA.vx*cosTheta + ballA.vy*sinTheta) +
              (ballB.m + e*ballB.m)/(ballA.m + ballB.m)*(ballB.vx*cosTheta + ballB.vy*sinTheta);
        float vxBp = (ballA.m + e*ballA.m)/(ballA.m + ballB.m)*(ballA.vx*cosTheta + ballA.vy*sinTheta) +
              (ballB.m - e*ballA.m)/(ballA.m + ballB.m)*(ballB.vx*cosTheta + ballB.vy*sinTheta);
        float vyAp = ballA.vx*(-sinTheta) + ballA.vy*cosTheta;
        float vyBp = ballB.vx*(-sinTheta) + ballB.vy*cosTheta;
        
        ballA.vx = vxAp*cosTheta + vyAp*(-sinTheta);
        ballA.vy = vxAp*sinTheta + vyAp*cosTheta;
        ballB.vx = vxBp*cosTheta + vyBp*(-sinTheta);
        ballB.vy = vxBp*sinTheta + vyBp*cosTheta;
        
        //if two ball is overlaped, seperate from each other.
        float angleAB = atan2(dy,dx);
        float angleBA = atan2(-dy,-dx); 
        float moveToDistance = abs(ballA.r + ballB.r) - dab;
        ballA.x = ballA.x + moveToDistance * cos(angleAB);
        ballB.x = ballB.x + moveToDistance * cos(angleBA);
        
      }
    
      if(ballA.x + ballA.vx < ballA.r || ballA.x + ballA.vx > width - ballA.r){
          if(ballA.x + ballA.vx < ballA.r){
              ballA.x = ballA.r;
          } else {
              ballA.x = width - ballA.r;
          }
          ballA.vx *= -1;
      }
      
      if(ballA.y + ballA.vy< ballA.r || ballA.y + ballA.vy > height - ballA.r){
          if(ballA.y + ballA.vy< ballA.r){
              ballA.y = ballA.r;
          } else {
              ballA.y = height - ballA.r;
          }
          ballA.vy *= -1;
      }
      
      if(ballB.x + ballB.vx< ballB.r || ballB.x + ballB.vx > width - ballB.r){
          if(ballB.x  + ballB.vx< ballB.r){
              ballB.x = ballB.r;
          } else {
              ballB.x = width - ballB.r;
          } 
          ballB.vx *= -1;
      }
      
      if(ballB.y + ballB.vy< ballB.r || ballB.y + ballB.vy > height - ballB.r){
          if(ballB.y  + ballB.vy < ballB.r){
              ballB.y = ballB.r;
          } else {
              ballB.y = height - ballB.r;
          }
          ballB.vy *= -1;
      }
      
      ballA.x += ballA.vx;
      ballA.y += ballA.vy;
      ballB.x += ballB.vx;
      ballB.y += ballB.vy;
      
    }
    
    // Ball
    class Ball {
        float m;//Mass
        float x;
        float y;
        float vx;
        float vy;
        float r;
        color clr;
        
        Ball(float mass) {
          this.m = mass;
          this.r = 2 * m;
          this.vx = random(-10, 10);
          this.vy = random(-10, 10);
        }
        
        void update() {
          fill(clr);
          ellipse(x, y, r*2, r*2);
        }
    }
    

    댓글 19

    • 대단한새끼 2013.03.20 22:53

      세상엔 대단한인간들이 너무많아

    • BlogIcon 권도 2014.11.20 16:52

      정말 정리 잘했네요, 오늘 몇시간째 찾고 있었는데 퍼갈께요 감사합니다. 문제시 자삭할께요

    • rkdgusrnrlrl 2015.03.01 12:42

      1차원 충돌시 두 물체의 질량이 같고 반발계수가 1인 경우-모든 운동에너지가 다른 에너지로 바뀌지 않고 다시 운동에너지로 전환되는 경우(ma=mb, e=1;)
      식을 도출해보면
      vb' = 1/va, va' = 1/vb가 됩니다.
      이렇게 되면 . 어느 속도도 역으로 가지 않으니 같은 방향이되고 vb'가 va'보다 크므로 곧 다시 충동이 발생합니다
      다시 충동이 발생했을 때 식의 위의 식데 대입해보면
      vb'' = vb, va''=va가 됩니다. 이런 방식으로 계속 충동이 일어나고 첫충돌에 속도가 감속되었다 다음 충돌에 원래 속도를 회복하게 됩니다. 상식적으로 이해하기 어려워 질문 남깁니다. 혹시 답변이 가능하시면 rkdgurnrlrl@gmail.com으로 메일 남겨주시면 바로 확인 가능합니다. 뭐 댓글을 달아주시면 시간나면 확인해보록 하겠습니다.

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2015.03.01 21:59 신고

        안녕하세요. 질량이 같고, 반발계수가 1인 경우를 아래 충돌 후 속도 값을 구하는 공식에 대입하는 과정에서 오류가 있으신 것 같아요.
        va' = (ma-mb*e)/(ma+mb)*va + (mb+mb*e)/(ma+mb)*vb

        질량이 같으니 그냥 질량을 m 이라고 바꿔보겠습니다.
        va' = 0 / m * 2 / va + m * 2 / m * 2 * vb;
        va' = vb;

        로 정리됩니다. 즉 서로의 속도만 교환했으니 서로 멀어지는 것으로 끝날 것입니다.

    • rkdgusrnrlrl 2015.03.04 21:04

      위의 식에보면2차원 충돌시 1차원 충돌 공식인 va' = (ma-mb*e)/(ma+mb)*va + (mb+mb*e)/(ma+mb)*vb 에 대입하므로
      vaxp = (ma-mb*e)/(ma+mb)*(vax*cosθ + vay*sinθ)
      + (mb+mb*e)/(ma+mb)*( vbx*cosθ + vby*sinθ) 이 맞는게 아닌가요??
      그리고 도표 상으로보면 vay = vaxp*sinθ - vayp*cosθ 보다 vay = vaxp*sinθ +vayp*cosθ 가 맞지 않나요
      도표상으로 보면 이동되어질 y축의 좌표는 vaxp*sinθ 값보다 큼에도 불구하고vayp*cosθ 만큼을 차감한다면 이동되져야 할 y 값에 이르지 못할 것 같습니다.
      그리고 지난 답변 감사드립니다. 덕분에 1차원 배열에서 쉽게 벗어날 수 있었습니다.
      좋은 설명 덕분에 마지막 관문에 이르르지 않았나 싶습니다.ㅎㅎ

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2015.03.04 23:23 신고

        안녕하세요~. 덕분에 오류를 수정했어요.
        말씀하신 부분이 맞습니다.
        포스트 수정 중에 지워졌던 부분을 너무 쉽게 채워넣었나봐요.
        덕분에 좀더 정확한 내용을 담을 수 있게 되었군요~!!! 고맙습니다.

    • rkdgusrnrlrl 2015.03.05 23:31

      감사합니다. 덕분에 목표에 달성할수 있게 되었습니다. 저는 javascript로 짰는데 비슷한 스크립트 언어라 쉽게 이해 가능했지 싶습니다. 다만 몇가지 문제에 봉착했는데 제가 사용한 방식은 실제 공이 움직이는 것이 아닌 공이 그려질 좌표를 계속 수정해 그려주는 것을 매 초마다 반복하는 작업인데 공일 충돌할 시점을 명확하게 잡아내는데 어려움이 있습니다. 두 원점을 잇는 선이 두 원의 반지름의 합보다 작을 경우인데 딱 그 점을 잡기가 어렵더군요
      원이 20의 반지름을 같는데(두원의 크기도 같습니다.) 두원점을 있는 선이 40이 되면 부딪히는 순간일 땐데 딱 40이 맞는 경우가 드뭅니다. 하나는 좌표를 1씩 움직이지만 나머지 하나는 3씩 움직이고 또 충돌해 값이 틀어져 버리면 그 것 대로 또 정확히 40되는 지점을 찾기 어렵습니다.
      개인적인 구상은 40이 되지 직전에 두원점이 정확히 40이 되는 지점을 구하는 공식이 만들어 그 순간을 억지로 만들어준 다음해당 공식을(충돌후 속도와 방향을 변경해주는) 활용하면 좀더 정확하지 않을 까 싶습니다.
      지금 완성된 걸로 보면 미세하게 이상한 점들이 보입니다.

      일주일이 걸쳐 만들어서 보람이 있는데 완벽하지 않아 아쉬움이 들지만, 해당 포스트가 없었다면 시도도 어려웠을 것 같아 코딩을 마무리 지으며 감사 댓글 역시 남겨 봅니다. 앞으로도 좋은 포스팅 기대하겠습니다.

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2015.03.06 07:15 신고

        저도 감사합니다. 덕분에 오랜만에 머리쓰는 것 같았어요~ㅋ 제기하신 문제까지 생각하셨다니 대단하시네요. 실제 두 물체가 부딪히는 순간과 화면상에 이 순간을 보여줄 타이밍이 있는지는 다른 문제죠. 충돌 직전에 다음 프레임에서 충돌 후 어느 위치로 갈지 알 수 있으면 좀더 정확한 움직임을 줄 수 있을 거에요. 아직 코드 정리가 마무리된 사이트는 아닌데, 이런 문제들을 좀더 고민해보시려면 http://brownsoo.github.io/2DVectors/ 을 참고해보세요.

    • ㅋㅋㅋ지존 2015.11.15 17:08

      이 글보고 많은것을 배웠습니다
      전에는 이것 못구현했는데 운동량보존법칙은 알고있었어도 반발계수 개념을 모르고 있어서리..
      그리고 결국 해냈습니다!! 고맙습니다.

    • ㅋㅋㅋ지존 2015.11.22 01:21

      아 그런데 이게 두 물체의 모양이 완벽한 "원"이라는 것이잖아요.
      두 물체의 모양이 원이 아닌 사각형이라던지 삼각형이라던지 하면 운동량 보존법칙을 어떻게 적용해야 하는건가요?
      물체가 닿는 부분의 표면의 법선벡터를 고려해야하는데 아직 답을 못찾았습니다...

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2015.11.23 01:28 신고

        물체의 형태에 따라 아주 다양한 방법들이 있을 텐데요.. 사실 저도 잘은 모릅니다만, 지금 제가 정리하고 있는 간단한 문서가 있으니 참고해보시면 어떨까요? 딱 드는 생각은 사각형이나 삼각형에서는 면과 꼭지점을 다르게 계산해주면 좋을 것 같습니다. 제가 만들고 있는 사이트는 http://brownsoo.github.io/2DVectors/ 입니다.

    • 반발계수10 2016.12.18 14:46

      감사합니다 저도 이것 관련 공부중이라 도움이 되었네요. 그런데, 궁금한게 생겼습니다.
      2차원 충돌 시뮬레이션 코딩중인데, 극복하기 어려운 문제가 발생되었습니다.

      가령 두 공의 속도가 충분할 때, 서로 충돌 직전이지만 충돌처리는 되지 않는 프레임이 있고
      그 다음 프레임에서는 서로의 좌표가 중첩되는 좌표가 허락이 되면서, 서로의 충돌 각도가 실제와 다르게 처리되는 문제가 있으며
      심지어, 두 공의 상대 속도가 서로의 지름1+지름2 거리 이상을 가지면, 서로 충돌하지 않고 워프처리가 되는 경우도 생기게 되더군요.

      이 문제를 해결하기 위해 고심을 해보았는데,
      임의의 공에 대해 진행경로상의 모든 좌표에 다른 공이 존재하는가 미리 검사하여 해당 프레임의 시분할을
      매 순간 충돌 시점으로 재설정 하는 방법.... 은 상대방 공도 이동중이므로 너무 복잡헤서 코딩 포기!

      다음으로 생각한건,
      시뮬레이션의 최대 속도를 공의 반지름에 대한 적정 허용 수준까지만 리미트 두어 적용하면 그나마 오차가 줄어들어 괞찮은데
      문제는 오차 범위 설정에 따라 처리속도가 너무 느려진다는 겁니다.

      예를 들어 공의 반지름이 10 일때, 프레임당 공의 최대속도를 2 로 잡아두면 한 프레임당 0px ~ 2px 씩 이동하므로..
      시뮬레이션 자체가 많이 느려지죠.....

      이러한 내용을 고민중인데, 저에게 도움이 될 만한 내용을 주실 수 있는지요?

      아 참, 그러고 보니, 당구 프로그램 같은데서는 이러한 문제를 어떻게 극복한 것일까요?
      당구소스 찾아봐야 겠네요

      소스 잘 보았습니다 감사드립니다/

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2016.12.21 18:13 신고

        이 포스트의 내용은 말씀하신 허점이 있어요. 충돌에 관해 제가 좀더 자세히 정리한 글이 있으니 참고 해보세요.
        http://brownsoo.github.io/2DVectors/

    • 반발계수10 2016.12.26 09:14

      오!!!
      일부 내용을 일단 보고왔는데 제가 찾던 내용이 다 있는것 같네요 !

      저는 그동안 복수개의 공이 서로 움직일 때의 충돌에서 충돌시점을 고심했었고

      이에 대해.... 두 공의 진행경로를 좌표에 잡고, 서로의 반지름 거리가 음수(-)일 때는
      반지름 축지선이 0 되는 지점에 대한 시간을 역계산 하여 찾으면 될거라고 생각중이였죠. ㅋㅋ

      하지만 뭔가 복잡해서 시뮬레이션 코딩 손 놓고 있었어요!

      님께서 작성한 정리글에 그 내용이 다 있는것 같습니다 !

      손 놓고있었던 충돌시점으로 재설정하는 시뮬레이션을 다시 도전해보려 합니다!

      감사합니다.

    • 반발계수10 2016.12.29 11:44

      휴... 님 덕분에 많은 부분이 이해가 되었고, 논리적으로 완성도 높게 생각할 수 있게 되었네요.
      어쨌건, 벽면 충돌이건 볼:볼 이건 서로의 프레임당 속도에 상관없이 충돌시점을 잡아낼 수 있었으며
      그로인해 충돌 처리를 할 수 있었습니다.

      그런데, 아직 한가지 해결이 안된 내용이 있습니다.
      이건 님 정리글에서도 거론하지 않은 내용이라서 님의 의견을 좀 듣고싶어 왔습니다.

      글로 설명이 잘 될지 모르겠지만.
      서로 속도와 크기가 다른 두 개 이상의 A, B, C, D ... n개의 공이 있습니다.

      이 많은 공들을 처리하기 위해서는 프로그램에서
      반복문 혹은 재귀호출을 이용하여 공 A 부터 공n 까지 순차적으로 충돌검사를 해야 할 것입니다.

      가령 루프문을 아래처럼 구동할 수 있을겁니다.

      for( i = 0, i < Balls.length-1; i ++ ) {
      for( j = i+1, j < Balls.length ; j ++ ) {
      ....
      .... Balls[i] - Balls[j] 에 대한 충돌처리
      ....
      }
      }

      즉, A~n개의 공들에 대한 충돌처리에 일련의 순서가 정해지는 겁니다.
      .....
      .... 충돌 처리순서 A-B, A-C, A-D ...... A-n
      .... 충돌 처리순서 B-C, B-D, B-E ...... B-n
      .... 충돌 처리순서 C-D, C-E, C-F ...... C-n
      ....

      이 때, 공 A와 B가 프레임 처리시간 이상의 속도를 부여받고 서로 다가오고 있다고 가정하면,
      프로그램에서는 이번~다음 프레임 사이에서 두 공이 서로 충돌할 것이 분명하므로
      이번 사이클이 넘어가기 전에 충돌계산을 시켜서 서로의 진행 방향과 좌표를 반대로 처리합니다.

      이런 식으로 모든 공을 차례차례 처리하면 되는데.....

      문제는, 제 3의 (부피가 작은) 공 C 가,
      두 공의 사이를 빠르게 지나는 과정에 공 A를 스치면서 지나치기로 되어있었다면
      실제의 충돌 순서는 A-C 가 일단 충돌 후, A 의 속도가 약간 감속된 다음에 A-B 가 충돌해야 합니다.

      그러나..
      컴퓨터의 CS 연산은 순차연산밖에 안되잖아요?
      결국 프로그램에서는 공 A에 대해 A-B 처리 이후 A-C, A-D, A-E .... A-n 순서로 처리하게 되므로
      A의 속도가 감속되지 않는 상태로 A-B 의 처리가 되어, 실제와는 다른 결과를 내게 되더군요.

      또한, 이것과 비슷한 문제는 더 있습니다.

      코너에 몰려있는 공 A와 충돌하려는 공 B의 경우에서,
      코너에 몰린 공A가 벽 안쪽을 향하고 있다면 프로그램에서는 분명 공A의 진행 방향을 벽 반대로 먼저 처리한 이후
      A-B의 충돌처리를 해야 하는데,
      공A의 속도가 매우 빠르다면 벽에 충돌 이후 벽 반대로 처리과정에서 A의 포지션이 B를 훌쩍 넘어갈 수 있습니다.

      그렇게 된다면, 공B 입장에서는 공 A와는 서로 멀어지고 있는 상태일 것이므로
      결국 '과거'에 충돌한 공으로 계산되는데 이에 대한 처리가 논리적으로 가능하지 않더군요.

      왜냐면, 좌표상의 계산만 따진다면 코너가 아닌 정상적인 패드에서
      서로 멀어지고 있는 공들은 무수히 많을 수 있습니다.


      이런 저런 문제들을 생각해보다 내린 결론은 한가지입니다.

      실제 현실에서는 공의 번호나 배치 순서와 상관없이 충돌 순서는 무작위로 발생이 되는데
      컴퓨터 가상좌표에서는 공의 배열 인덱싱에 따라 '순서'에 의해 처리를 하면서
      중간에 끼어드는 '뒷 순서'공을 미리 계산하지 않는 게 문제인 것 같아요.

      이 갭을 극복하기 위해서는 모든 공들에 대해 매 프레임마다 '가장 먼저' 발생되는 충돌을
      잡아내야 한다는 뜻인데.

      결국 모든 공들에 대해 차례로 충돌 예상 시간을 먼저 구한 후,
      가장 빠르게 충돌될 공에 대해 충돌처리를 하면 될 것 같은데.

      이것도 녹록지 않은게 충돌처리 직후에는
      제 3의 공의 궤도를 지나쳐서 놓아야 하는 경우에는 이번 프레임 가기 전에
      제 3의 공과 다시 계산해야 하는 연쇄적 계산이 수반되므로
      프레임마다 매 순간 충돌 사건에 대해 모든 공들의 시점을 충돌시점으로 되돌려서 프레임 시간 자체를 분할해야 하는거....


      그래서 얻은 결론은

      1. 모든 공에 대해 차례로 충돌시점을 미리 검사 후
      2. 가장 빠르게 발생 될 충돌을 계산하여 해당 공의 속도 변화를 처리하고
      3. 모든 공의 운동좌표를 (2)의 충돌시점으로 되돌린 후 다시 (1) 과정으로 순환...


      휴....
      어떤가요?

      혹시 제 생각에 모순이 있거나
      혹은 제가 생각하는 것 보다 더 논리적이고 간단한 방법이 있거나... 하면 고견 주시면 감사하겠습니다.

      아참, 인터넷 외에는 일면식 없는 사이지만
      연말연시 따듯하게 잘 보내시구요~!

      • Favicon of https://blog.hansoolabs.com BlogIcon hansoo.labs 한수댁 2017.01.03 22:27 신고

        와~~ 대단하시네요. 오히려 제가 배우게 됩니다.
        제가 정리한 내용을 제한된 조건에서 원리와 해법을 찾아보려고 했던거죠. 얘기하신대로, 현실에서는 1.0 이라는 시간안에 2차, 3차 그리고 더 많은 충돌이 벌어질 수 있지요.
        음.. 그렇다고 무한하게 시간을 되돌려가며 연쇄 충돌을 계산하기도 좀 그렇군요.
        시뮬레이션에는 한계가 있어야 하는 것인지 생각도 듭니다. 혹시 더 깊은 해법을 찾으시면 공유해주세요. 꾸벅!

Designed by Tistory.