hyrule 2022. 6. 2. 23:06

http://hyrule.tistory.com/111 

 

*** 공부 방법 ***

1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. 처

hyrule.tistory.com

 


- 사각형과 원의 충돌 판정이 의외로 복잡하다.

- 우선 사각형의 변에 닿는 경우는, 원의 가운데 점이 사각형의 한 변의 끝에서의 거리보다 가까우면 충돌이다.

--> 쉽게 말해서, 사각형의 크기를 반지름만큼 확장해주고 그 안에 원의 중심점이 들어오게 되면 충돌이라는 것이다.


- 문제는 대각선이다.

- 대각선은, 점이 확장한 사각형 안에 들어와도 충돌이 아니기 때문에, 이 경우를 제외해 줄 필요가 있다.

- 이 범위만 체크해 주어야 한다는 의미이다.

합집합 -> OR을 통해 확인 가능하다.


- 대각선은 반대로 처리한다.

- 사각형의 꼭짓점과 원의 중심점과의 거리를 구해서, 이 거리가 반지름 이내라면 충돌인 것이다.


 

- 결론은, 크게 두 가지 경우를 체크해야 된다.

1-1. 빨강 혹은 파랑의 범위 안에 원의 중심점이 있는지를 판단

 

1-2. 위의 범위 안에 있다면, 사각형을 반지름만큼 확장하여 이 넓이의 사각형 안에 있는지를 판단한다.

x친 부분은 위에서 이미 확인 되었으므로, x친 위치에 있던 원의 중심점이 이 과정까지 들어올 리는 없다.

(교집합)


2. 그렇지 않다면, 사각형의 네 꼭지점과의 거리를 확인해서 범위 안에 있는지 확인한다.

 

-> 둘 중 하나라도 통과하면 충돌이다.

이것을 코드로 구현해주면 된다.


class CCollisonManager

< bool CollisionBoxToCircle(
class CColliderBox* Src,
class CColliderCircle* Dest
);
bool CollisionCircleToBox(
class CColliderCircle* Src,
class CColliderBox* Dest
);

protected:
bool CollisionBoxCircle(
const BoxInfo& Box,
const CircleInfo& Circle,
Vector2& HitPoint
);

>

- Box-To-Circle 은 상황에 따라 Src가 Box가 될 수도, Circle이 될 수도 있다.

- 그러므로 Src가 Box이든 Circle든, Dest가 Box이든 Circle이든 간에 처리해줄 수 있게,

내부에서만 쓸 수 있는 CollisionBoxCircle() 메소드에서 실제 충돌 계산을 시킨 뒤 값을 반환받는 이중 형태로 구현한다.

 

- CollisionBoxCirdle() 메소드는 Vector2 HitPoint를 추가로 받아서 HitPoint까지 계산하여 반환한다.

 

[ 로직 ]

* 히트포인트의 경우 사각형-사각형 방식과 원-원 방식 모두 비슷한 지점이 나오므로 그냥 사각형 방식을 해주어도 큰 차이는 없다.

1. 우선 가장 안쪽을 포함하는 '박스의 변쪽 접근' 부터 처리한다.

1-1.사각형의 십자 범위 안에 원의 중심점이 존재하는 지 확인

더보기
	//박스의 변 쪽으로 접근 시
	if((Circle.Center.x >= Box.LT.x && Circle.Center.x <= Box.RB.x) ||
		(Circle.Center.y >= Box.LT.y && Circle.Center.y <= Box.RB.y))
	{

1-2.사각형을 반지름만큼 확장한 사각형 안에 원의 중심점이 들어오는지 확인

더보기
	//박스의 변 쪽으로 접근 시
	if((Circle.Center.x >= Box.LT.x && Circle.Center.x <= Box.RB.x) ||
		(Circle.Center.y >= Box.LT.y && Circle.Center.y <= Box.RB.y))
	{

		BoxInfo ExtendedBox;
		ExtendedBox.LT = Box.LT - Circle.Radius;
		ExtendedBox.RB = Box.RB + Circle.Radius;


		if (ExtendedBox.LT.x > Circle.Center.x)
			return false;

		else if (ExtendedBox.LT.y > Circle.Center.y)
			return false;

		else if (ExtendedBox.RB.x < Circle.Center.x)
			return false;

		else if (ExtendedBox.RB.y < Circle.Center.y)
			return false;

1-3.사각형의 대각선 접근 확인

더보기
	//박스의 변 쪽으로 접근 중이지 않다면 
	else
	{
		//대각선을 확인한다.
		//사각형의 네 꼭짓점과 원의 중심점의 거리가
		//반지름보다 짧다면 충돌이다.
		Vector2 Vertex[4] = {
			Box.LT,
			Box.RB,
			Vector2(Box.LT.x, Box.RB.y),
			Vector2(Box.RB.x, Box.LT.y)
		};

		//하나라도 걸리면 True 이므로 바로 계산하고 빠져나오면 성능적으로 도움이 될 것.
		for (int i = 0; i < 4; ++i)
		{
			if(Circle.Radius > Circle.Center.Distance(Vertex[i]))
			{

 

 


class CColliderBox;
class CColliderCircle;

< Collision() >

- ColliderCircleToBox() / ColliderBoxToCircle()을 상황에 맞게 호출해 준다.


- 이제 몬스터를 Box 충돌체로 다시 바꾸고, 총알을 Circle 충돌체로 바꾸어서 제대로 작동하는지 확인해 본다.

 

 

GameFrameworkStepbyStep_52_BoxToCircle.zip
0.31MB

 

 

 


* 성능 개선 팁

if (ExtendedBox.LT <= Circle.Center &&
   	ExtendedBox.RB >= Circle.Center)
{

}

- 사각형의 안에 존재하는지 확인하는 과정은, 사각형의 네 변에 대해 무조건 모두 검사하고 넘어간다.

 

- 하지만 이렇게 해 준다면, 

		if (ExtendedBox.LT.x > Circle.Center.x)
			return false;

		else if (ExtendedBox.LT.y > Circle.Center.y)
			return false;

		else if (ExtendedBox.RB.x < Circle.Center.x)
			return false;

		else if (ExtendedBox.RB.y < Circle.Center.y)
			return false;

- 끊어서 계산하므로 좀 더 빠르게 충돌이 아님을 판단할 수 있게 되고, 조금이나마 성능을 챙길 수 있게 된다.

- 밑의 대각선 접근의 경우도 네 꼭짓점 중 하나라도 true면 충돌 확정이기 때문에,

한번에 논리 연산을 하는 것이 아니라 나누어서 연산을 해 주는 것이 좋다.