GameDevelopment/[C++] Project
[C++] 벽돌깨기 게임 ( WinAPI )
주녘
2024. 9. 15. 00:37
1. 벽돌깨기 게임 ( WinAPI )
- 알카노이드를 레퍼런스로 하여 제작한 게임
https://github.com/godgjwnsgur7/WinAPI_BrickBreaking/tree/main/BrickBreaking
WinAPI_BrickBreaking/BrickBreaking at main · godgjwnsgur7/WinAPI_BrickBreaking
Contribute to godgjwnsgur7/WinAPI_BrickBreaking development by creating an account on GitHub.
github.com
2. 게임 핵심 구현 요소
1) Vector2
- 위치 벡터, 방향 벡터 등 범용적으로 사용하기 편리하도록 Vector2 구조체를 구현하여 사용함
struct Vector2
{
float x, y;
Vector2();
Vector2(float x, float y);
Vector2 operator-(const Vector2& other) const;
Vector2 operator+(const Vector2& other) const;
Vector2 operator*(float scalar) const;
float Dot(const Vector2& other) const;
Vector2 Normalize() const;
};
2) 일정 주기로 호출 ( Update )
- deltaTime 개념을 넣어 일정 주기로 Update 메서드를 호출
- Update에서 각 객체들의 상태를 업데이트하고, 갱신된 내용을 그려줌
int APIENTRY wWinMain( ... )
{
...
while (1)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { ... }
else
{
DWORD currentTime = GetTickCount64();
deltaTime += (currentTime - previousTime);
previousTime = currentTime;
if (deltaTime > 10)
{
Update();
deltaTime = 0;
InvalidateRect(winhWnd, NULL, FALSE);
}
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND: { ... }
break;
case WM_CREATE:
Start();
break;
case WM_MOUSEMOVE:
OnMouseMoveEvent(lParam);
break;
case WM_PAINT:
Draw(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
3) 더블 버퍼링, 메모리 풀
- 더블 버퍼링 : 메모리 DC를 생성해 미리 그림을 그린 후 실제 출력할 화면 DC에 고속 복사
- 메모리 풀 : 많이 사용될 수 있는 객체에 대하여 일정 메모리 크기만큼 미리 점유시켜놓고 사용
void Draw(HWND& hWnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBitmap = CreateCompatibleBitmap(hdc, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
SelectObject(memDC, memBitmap);
FillRect(memDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
// 아이템
for (Item& item : items)
if (item.isUsing)
item.Draw(memDC);
// 블럭
for (std::vector<Brick>& brick : bricks)
{
for (Brick& b : brick)
{
if (b.GetCount() > 0)
b.Draw(memDC);
else
b = NULL;
}
}
// 공
for (Ball& ball : balls)
if (ball.isUsing)
ball.Draw(memDC);
// 바
bar.Draw(memDC);
BitBlt(hdc, 0, 0, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, memDC, 0, 0, SRCCOPY);
DeleteObject(memBitmap);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
}
4) 원과 직사각형의 충돌 판단 ( Ball <-> Bar, Brick )
- 직사각형의 모서리 영역 중에 원의 원점과 가장 가까운 위치 좌표를 가져와 거리를 측정
- 거리가 원의 반지름보다 적다면 충돌이 일어났다고 판단
bool Ball::CheckCollisionBar(Bar& bar)
{
int closestX = std::max((float)(bar.GetRect().left), std::min(pos.x, (float)(bar.GetRect().right)));
int closestY = std::max((float)(bar.GetRect().top), std::min(pos.y, (float)(bar.GetRect().bottom)));
int distanceX = pos.x - closestX;
int distanceY = pos.y - closestY;
if ((distanceX * distanceX + distanceY * distanceY) <= (radius * radius))
{
float diffX = pos.x - bar.GetPos().x;
float barHalfLength = bar.GetLength() / 2;
float velocityX = diffX / barHalfLength;
velocity = { velocityX * 2.0f , -1 };
if (pos.y >= bar.GetPos().y - 10 - radius)
pos.y = bar.GetPos().y - 10 - radius;
return true;
}
return false;
}