KoreanFoodie's Study

윈도우 API 프로그래밍 4 : 충돌 검사 (IntersectRect...) 본문

Game Dev/윈도우 API

윈도우 API 프로그래밍 4 : 충돌 검사 (IntersectRect...)

GoldGiver 2021. 10. 15. 08:37

게임클래스의 하민우 교수님 강좌를 듣고 기억할 만한 내용을 리마인드하는 글입니다


윈도우 API 프로그래밍 4 : 충돌 검사 (IntersectRect...)

이번 글에서는 도형이 다른 도형과 만났을 때 이를 어떻게 구현하고 처리하는지를 살펴본다.

 

WM_TIMER에서 IntersectRect를 넣어보자

// 아래 두 줄은 전역변수로 선언함
enum MOVE_DIR { MOVE_LEFT, MOVE_RIGHT, MOVE_UP, MOVE_DOWN };
MOVE_DIR	eMoveDir;

// ...

case WM_TIMER:		// 타이머에 의해서 호출
		InvalidateRect(hWnd, NULL, true);

		// 포지션 위치에 따른 렉트 정보 업데이트
		rtBox1 = RECT_MAKE(ptPos1.x, ptPos1.y, 100);
		rtBox2 = RECT_MAKE(ptPos2.x, ptPos2.y, 100);

		RECT rt;
		if (IntersectRect(&rt, &rtBox1, &rtBox2))
		{
			switch (eMoveDir)
			{
			case MOVE_LEFT:
				ptPos2.x -= fMoveSpeed;
				break;
			case MOVE_RIGHT:
				ptPos2.x += fMoveSpeed;
				break;
			case MOVE_UP:
				ptPos2.y -= fMoveSpeed;
				break;
			case MOVE_DOWN:
				ptPos2.y += fMoveSpeed;
				break;
			}
		}

		if (isPicked)
		{
			ptPos2 = ptMouse;
		}

		break;.

아까 WM_CREATE에서 정의한 바에 따르면, 타이머가 1초에 100번 갱신되는 것을 우리는 이미 알고 있다.

InvalidateRect(hWnd, NULL, true)를 보면 윈도우 핸들 등의 변수가 들어가는데, 마지막 인수를 false로 바꾸면 이전에 그려졌던 내용이 사라지지 않고 계속 남아있게 된다.

IntersectRect라는 function을 이용해서 각각 rtBox1, rtBox2라는 녀석이 겹친 상태로 갔는지 아닌지를 파악할 수 있는데, enum type으로 선언한 eMoveDir을 이용해 ptPos2를 이동시켜 준다. 이는 마치 충돌한 Rect가 밀리는 것 같은 효과를 줄 수 있다.

또한 isPicked 함수는 마우스 피킹이 이루어졌는지를 체크하는 bool 값인데, 다음 블록에서 설정하게 된다.

 

간단한 마우스 관련 블록

	case WM_MOUSEMOVE:
		ptMouse.x = LOWORD(lParam);
		ptMouse.y = HIWORD(lParam);
		break;
	case WM_LBUTTONDOWN:
		if (PtInRect(&rtBox2, ptMouse))
		{
			ptPos2 = ptMouse;
			isPicked = true;
		}
		break;
	case WM_LBUTTONUP:
		isPicked = false;
		break;

WM_MOUSEMOVE에서는 ptMouse라는, 현 마우스의 위치를 받아오고 있고, WM_LBUTTONDOWN 안에서 현재 ptMouse(즉 현재 마우스의 좌표)가 rtBox2라는 Rect 내부에 있는지 PtInRect함수를 통해 확인하고 있다.

WM_LBUTTONDOWN은 이름처럼, 왼쪽 마우스 클릭이 있을 때 이를 감지하는 블록이다.

WM_LBUTTONUP에서, 마우스 클릭 버튼을 손에서 떼면 isPicked가 false가 되도록 구현하고 있다.

 

간단히 간단히

// 전역 매크로
// 렉트 생성 매크로 (x좌표, y좌표, 사이즈)
#define RECT_MAKE(x, y, s) { x - s / 2, y - s / 2, x + s / 2, y + s / 2 }
// 렉트 렌더 매크로
#define RECT_DRAW(rt) Rectangle(hdc, rt.left, rt.top, rt.right, rt.bottom)

// ...

case WM_PAINT:
        {
            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hWnd, &ps);

			RECT_DRAW(rtBox1);
			RECT_DRAW(rtBox2);

            EndPaint(hWnd, &ps);
        }

보면 WM_PAINT가 매우 간단해진 걸 알 수 있는데, 이는 RECT_DRAW라는 매크로를 통해 간략화를 시켰기 때문이다!


더 자세한 내용이 궁금하시다면 직접 들어보시는 걸 추천드립니다!

 
Comments