Keyword
-
Collision detection
-
Bounds
ball은 시간이 흐름에 따라 지정된 방향으로 이동한다. 그리고, 정해진 공간을 벗어나더라도 이를 알지 못하고 계속 이동해 버려 결국에는 공간을 벗어나 보이지 않게 된다.
경계가 있는 세상이란 정해진 공간의 외곽에는 보이지 않는 벽으로 구성되어 있고, 벽에 ball이 부딪칠 경우 멈추거나 튕겨 나와야 한다.
사각의 경계가 있는 세상에서 ball이 벽에 부딪히는 경우는 아래 그림과 같을 것이다.
이를 앞에서 설명한 위치 변화로 표현하면 다음과 같다.
-
왼쪽 벽 : (dx, dy) → (-dx, dy)
-
아래쪽 벽 : (dx, dy) → (dx, -dy)
-
오른쪽 벽 : (dx, dy) → (-dx, dy)
-
위쪽 벽 : (dx, dy) → (dx, -dy)
설명에 따라 ball이 벽에 부딪혔을 경우 ball이 가지고 있던 단위 변화량만 변경해서 주면 된다.
이제 결정이 필요하다. 누가 이 작업을 해줄 것인가? ball? world?
ball 스스로가 변경 작업을 해야 한다면 충돌감지를 판단할 수 있는 정보가 제공되어야 할 것이고, world가 변경 작업을 도와준다면 ball의 위치를 항상 감시해야 할 것이다.
두 가지 방법 모두 만들어 보도록 하자.
-
경계영역 정보를 가진다.
-
경계영역을 벗어나려면 튕겨 난다.
-
경계영역에서 튕겨 나올때는 동일한 힘으로 튕겨져 나온다.
-
경계영역은 사각 영역으로 제한한다.
-
벽에 튕기는 것은 다음의 경우로 분류된다.
-
왼쪽이나 오른쪽 벽에 부딪힐 경우, X의 이동 방향이 변경된다. 즉, X축의 변화량 \(dx\)가 변경된다.
-
ball의 왼쪽 끝부분이 경계영역을 벗어나면 왼쪽 벽에 부딪힌 것이다.
-
ball의 오른쪽 끝부분이 경계영역을 벗어나면 오른쪽 벽에 부딪힌 것이다.
-
-
위쪽이나 아래쪽 벽에 부딪힐 경우, Y의 이동 방향이 변경된다. 즉, Y축의 변화량 \(dy\)가 변경된다.
-
ball의 위쪽 끝부분이 경계영역을 벗어나면 위쪽 벽에 부딪힌 것이다.
-
ball의 아래쪽 끝부분이 경계영역을 벗어나면 아래쪽 벽에 부딪힌 것이다.
-
-
-
Source Code
-
물음
-
단위 시간을 길게 주면 ball이 경계영역에 벗어나는 시점에 튕지 않고, 일부는 영역을 벗어났다 튕겨 들어오는 것을 볼 수 있다.
-
왜 그럴까?
-
해결 방법은?
-
-
앞에서의 순간 경계 벗어나는 문제를 분석해 보자.
예를 들어, 오른쪽 경계에 부딪힌 후 튕겨 난 경우는 아래 그림과 같다.
-
방향별로 튕겨질때의 보정 좌표 계산은 다음과 같다.
-
오른쪽 경계 충돌
\[\begin{align*} X_R & = X_2 - r\\ x_2 &= x_1 + |d_x|\\ x_3 &= X_R - (|d_x| - (X_R - x_1))\\ &= 2X_R - x_1 - |d_x|\\ &= 2{(X_2 - r)} - x_1 - |d_x|\\ &= 2{(X_2 - r)} - x_2\\ y_3 &= y_1 + dy\\ \end{align*}\] -
왼쪽 경계 충돌
\[\begin{align*} X_L & = X_1 + r\\ x_2 & = x_1 - |d_x|\\ x_3 &= X_L + (|d_x| - (x_1 - X_L))\\ &= 2X_L - x_1 + |d_x|\\ &= 2{(X_1 + r)} - x_1 + |d_x|\\ &= 2{(X_1 + r)} - (x_1 - |d_x|)\\ &= 2{(X_1 + r)} - x_2\\ y_3 &= y_1 + d_y\\ \end{align*}\] -
위쪽 경계 충돌
\[\begin{align*} Y_T & = Y_2 - r\\ y_2 &= y_1 + |d_y|\\ y_3 &= Y_T - (|d_y| - (Y_T - y_1))\\ &= 2Y_T - y_1 - |d_y|\\ &= 2{(Y_2 - r)} - y_1 - |d_y|\\ &= 2{(Y_2 - r)} - y_2\\ x_3 &= x_1 + d_x\\ \end{align*}\] -
아래쪽 경계 충돌
\[\begin{align*} Y_B & = Y_1 + r\\ y_2 & = y_1 - |d_y|\\ y_3 &= Y_B + (|d_y| - (y_1 - Y_B))\\ &= 2Y_B - y_1 + |d_y|\\ &= 2{(Y_1 + r)} - y_1 + |d_y|\\ &= 2{(Y_1 + r)} - (y_1 - |d_y|)\\ &= 2{(Y_1 + r)} - y_2\\ x_3 &= x_1 + d_x\\ \end{align*}\]
-
-
경계 영역을 벗어난 경우, 추가적인 처리를 통해 위치를 보정하라.
-
Rectangle의 contains로 경계 검사를 할 때, 해당 점이 경계 위에 존재할 때 어떻게 처리할지 결정해야 한다.
-
위 식에서 변화량 \((|d_x|, |d_y|)\) 는 절댓값을 나타냄을 주의하라.(다행히 최종 계산에는 사용되지 않음)
-
Source Code
Keyword
-
Collision detection
-
Bounds
BoundedBall은 경계영역을 설정하고 해당 영역을 벗어날 경우, 튕겨져 나온다.
그럼, ball이 하나 이상 존재할 때 다른 ball이 차지하고 있는 공간은 어떻게 해야 할까?
또한, 경계영역은 ball에 허용되는 반면 다른 ball이 차지한 공간의 경우 허용되지 않는 영역이다. 따라서, 공간에 대해 허용 영역이 안인지 밖이지 구별이 필요하다.
-
흰색 ball을 기준으로 한다.
-
파란색은 앞에서 정의하고 있는 world가 된다.
-
붉은색 ball은 중첩이 허용되지 않는 다른 물체가 된다.
-
붉은색으로 표시된 영역은 흰색 ball에 허용되지 않는 영역이다.
-
world를 기존으로 할 경우 내부 영역이 허용 영역이고, 다른 ball을 기준으로 할 경우, 외부 영역이 허용 영역이 된다.
-
ball이 겹침은 ball 중간 거리가 두 ball의 반지름 합보다 크면 된다.
-
ball 간 거리는 --- \begin{align*} 중심 간 거리(D) &= r_1+r_2+d=\sqrt{{(x_1-x_2)}^2 + {(y_1-y_2)}^2}\\ ball 간 거리(d) &= \sqrt{{(x_1-x_2)}^2 + {(y_1-y_2)}^2} - (r_1 + r_2) \end{align*} ---
ball 간 거리가 두 ball의 반지름 합보다 작을 경우, 두 ball은 충돌한 상태다.
임의의 위치에 생성한 결과 일부 ball이 겹침을 ball 수 있다.
이는 앞서 추가된 ball이 어디에 얼만한 크기로 존재하는지 확인하지 않고 추가해 발생한 문제이다.
world에 ball이 추가될 때 해당 영역을 다른 ball이 없는지 확인하고 추가하도록 수정한다. 만약, 다른 ball이 차지하고 있어 새로운 ball의 추가가 어렵다면 exception을 발생시켜서 다른 위치에 추가될 수 있도록 한다.
참고
-
제곱근 함수 Math.sqrt()
-
반복해서 ball을 생성할 때, 반드시 for문을 사용해야 하는 것은 아니다.
-
Source Code
-
-
충돌 확인을 위한 isCollision 함수 추가
-
-
-
world에서 ball 추가시 충돌을 확인하고, 충돌시 exception 발생
-
-
다음 그림은 ball과 box 간 충돌을 나타낸 것이다.
\begin{align*} 두 점의 중심 간 거리(d) &= \sqrt {{(x_1-x_2)}^2 + {(y_1-y_2)}^2}\\ 최소 충돌 거리(c) &= r1 + {w_2 \over 2} \end{align*} ---
ball과 box의 충돌 역시 복잡해 보이지는 않는다. 중심 간 거리가 최소 충돌 거리도 짧으면 충돌이다.
하지만, 다음 그림을 보자.
복잡한 식을 이용하면 구할 수도 있을 것이다.
하지만, 본 과정에서는 중요한 문제가 되지 않는다.
이러한 경우, 물체가 충돌한 조건을 정의하고 충돌 시 그에 대한 행동만 정의할 수 있으면 된다.
다음 그림은 두 ball의 충돌을 intersects 함수로 이용할 경우를 표현한 것이다.
실제 충돌하지는 않았지만, 충돌한 것으로 가정한다. 대신 box에도 적용할 수 있어 문제를 단순화시킬 수 있다.
-
Ball class에 있는 충돌 확인 함수를 수정한다.
-
생성되는 ball을 크기를 조절해 본다.
-
Source Code
-
-
ball이 들어갈 수 있는 최소 4각 영역을 돌려주는 getRegion 함수 추가
-
ball간 충돌을 중심점 거리 계산에서 영역 계산으로 변경
-
-
-
ball에 충돌이 발생한 경우, 충돌 부분을 붉은색으로 표시한다.
-
충돌 영역을 얻어 낼 수 있어야 한다.
-
충돌을 감지할 때와 그릴 때가 달라 따로 저장해야 한다.
-
저장된 충돌 영역은 매번 갱신되어야 한다.
-
Source Code
-
-
world에서 화면을 새로 그릴때, ball간 충돌 영역을 추출해 표시
-
Rectangle class에서 중첩 영역을 얻어내는 intersection 활용
-
-
움직이는 두 ball이 충돌하면 서로 튕겨 나간다. 여기서는 동시에 튕기는 것을 구현하기는 복잡하므로, 문제를 단순화하여 특정 순간에 하나의 ball만 움직여서 고정된 ball에 부딪히는 것으로 한다.
이럴 경우, 움직이던 ball은 어디를 부딪치느냐에 따라 특정한 방향으로 꺾여서 튕겨 나가게 된다.
다음 그림은 두 ball이 충돌하였을 때, 겹치는 부분을 표시한 것이다.
겹친 영역을 번호로 하여, 1, 3, 6, 8은 진행 방향의 반대로, 2, 7은 X축을 기준으로 반대로(즉, dy를 변경), 4, 5는 Y축을 기준으로 반대로 움직이도록 하면 정확하지는 않지만, 충돌 후 튕김을 구현할 수 있다.
큰 ball이 움직일 경우도 마찬가지가 된다.
BoundedBall class를 구현함으로써 ball을 이용한 닫힌 세상에서 움직임을 확인해 보았다. 그럼, ball이 아닌 world를 이용한 경우는 어떠한지 확인해 보자.
움직이는 ball이 주어진 공간을 벗어나는지에 대해 world에서는 지속적인 감시를 통해 알 수 있다.
또한, 현재까지 구현에서 world는 ball의 움직임을 관리하고 있으므로 더욱더 쉽게 구현할 수 있고 이를 BoundedWorld라고 하자.
BoundedWorld는 ball이 허용 공간을 벗어났는지 확인하고, 그러한 경우 적절하게 이동 방향을 변경하도록 변화량을 재설정해 주어야 한다.
-
getBounds
-
World의 영역에 대한 정보로서 World class에 추가한다.
-
awt component에서는 getBounds 함수를 지원하므로, 새롭게 정의할 필요는 없다.
-
-
outOfBounds
-
ball이 world를 벗어났는지 확인한다.
-
BoundedWorld 영역과 ball 영역의 중첩 영역을 구해 ball 영역과 다를 경우 벗어난 것으로 판단한다.
-
-
bounceBall
-
ball이 경계영역 벽에 부딪혔을 때 튕겨 나온 위치로 이동시킨다.
-
MovableBall만 해당한다.
-
BoundedBall의 bounce를 참고한다.
-
-
move
-
ball을 이동시키고, 충돌 검사를 해야 하므로 기능 변경이 필요하다
-
-
Source Code
-
-
MovableWorld를 BoundedWorld로 확장
-
BoundedWorld에서는 MovableBall 사용
-
-