빙수달 게임 개발 노트

[WindowAPI] 알카노이드(Alkanoid) / 벽돌깨기 만들기 본문

Programming/WindowAPI

[WindowAPI] 알카노이드(Alkanoid) / 벽돌깨기 만들기

빙수달 2025. 1. 9. 00:32
#include "framework.h"
#include "Alkanoid.h"
#include <math.h>
#include <time.h>
#include <cmath>

#define MAX_LOADSTRING 100
#define BSIZE 10                // 원의 반지름

class CCircle
{
public:
	int mx;
	int my;
	float VectorX;
	float VectorY;

	void DrawCircle(HDC hdc);                 // 원 화면에 출력
	void Initialize(int x, int y);      // 원 그려지는 시작점
	void Translate();                   // 원 이동
	void WallCrash();          // 원이 벽 충돌할 때
	void ReflectfloorCollision();     // 원이 직사각형과 충돌할 때
	void BrickCollision();
};

static RECT reflectfloor;          // 발판

int MaxNum = 100;
int CurrentNum = 0;

static RECT rectView;       // 전체 화면
CCircle* CircleStack = new CCircle[MaxNum];

int brickWidth = 100;       // 벽돌 가로 크기
int brickHeight = 50;       // 벽돌 세로 높이
void InitializeBrick();
void DrawBrick(HDC hdc);

RECT brick[4][5];
bool brickActive[4][5];
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	POINT centerPoint = { 700,150 };
	RECT rt = { 0,0, 500,800 };
	SYSTEMTIME st;
	static int x, y;
	CCircle circle;

	switch (message)
	{
	case WM_CREATE:
	{
		srand((unsigned int)time(NULL));
		GetClientRect(hWnd, &rectView);
		AdjustWindowRect(&rt, WS_OVERLAPPEDWINDOW, true);
		MoveWindow(hWnd, 0, 0, rt.right - rt.left, rt.bottom - rt.top, true);

		SetTimer(hWnd, 1, 3, NULL);
		reflectfloor = { 190,720,300,740 };

		InitializeBrick();
	}
	break;
	case WM_SIZE:
	{
		GetClientRect(hWnd, &rectView);
		InvalidateRect(hWnd, NULL, TRUE);
	}
	break;
	case WM_TIMER:
	{
		for (int i = 0; i < CurrentNum; i++)
		{
			CircleStack[i].Translate();         // 원이 움직임
			CircleStack[i].WallCrash();     // 원이 벽에 충돌함
			CircleStack[i].ReflectfloorCollision();        // 원이 사각형에 충돌함
			CircleStack[i].BrickCollision();
		}
		InvalidateRgn(hWnd, NULL, TRUE);
	}
	break;
	case WM_PAINT:
	{

		hdc = BeginPaint(hWnd, &ps);

		for (int i = 0; i < CurrentNum; i++)        // 공 출력
		{
			CircleStack[i].DrawCircle(hdc);
		}

		Rectangle(hdc, reflectfloor.left, reflectfloor.top, reflectfloor.right, reflectfloor.bottom);      // 반사판 출력
		DrawBrick(hdc);

		EndPaint(hWnd, &ps);
	}
	break;

	case WM_CHAR:
	{
		break;
	}

	case WM_KEYDOWN:
	{
		if (wParam == VK_LEFT)
		{
			OffsetRect(&reflectfloor, -15, 0);
			if (reflectfloor.left < rectView.left)
			{
				reflectfloor.left = 0;
				reflectfloor.right = 110;
			}
		}
		if (wParam == VK_RIGHT)
		{
			OffsetRect(&reflectfloor, 15, 0);
			if (reflectfloor.right > rectView.right)
			{
				reflectfloor.left = 390;
				reflectfloor.right = 500;
			}
		}

		InvalidateRgn(hWnd, NULL, TRUE);
	}
	break;

	case WM_LBUTTONDOWN:
	{

		CircleStack[CurrentNum].Initialize(LOWORD(lParam), HIWORD(lParam));
		CurrentNum++;
	}
	break;

	case WM_DESTROY:
	{
		PostQuitMessage(0);
		break;
	}
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
void CCircle::DrawCircle(HDC hdc)
{
	Ellipse(hdc, mx - BSIZE, my - BSIZE, mx + BSIZE, my + BSIZE);
}

void CCircle::Initialize(int x, int y)
{
	mx = x;
	my = y;

	VectorX = -5;
	VectorY = 5;
}

void CCircle::Translate()
{
	mx -= VectorX * 1.0f;
	my += VectorY * 1.0f;
}

void CCircle::WallCrash()
{
	if (my + BSIZE > rectView.bottom || my - BSIZE < rectView.top)
	{
		VectorY *= -1;
	}
	else if (mx + BSIZE > rectView.right || mx - BSIZE < rectView.left)
	{
		VectorX *= -1;
	}
}

void CCircle::ReflectfloorCollision()
{
	// 반사판의 top/bottom에 대한 충돌 (Y축 반전)
	// 반사판의 left/right에 대한 충돌 (X축 반전 및 아래 방향으로 튕겨나감	

	if (my + BSIZE >= reflectfloor.top && my - BSIZE <= reflectfloor.bottom)		// 공이 반사판 top, bottom에 부딪혔을 때
	{
		if (reflectfloor.left <= mx && mx <= reflectfloor.right)
		{
			VectorY *= -1.0f;
			return;
		}
	}

	if (mx + BSIZE >= reflectfloor.left && mx - BSIZE <= reflectfloor.right)		// 공이 반사판 left, right에 부딪혔을 때
	{
		if (reflectfloor.top <= my&& my<= reflectfloor.bottom)
		{
			VectorX *= -1.0f;
			return;
		}
	}

	
	/*if(reflectfloor.left - (BSIZE / sqrt(2)) < mx && mx < reflectfloor.left)*/
	if (sqrt(pow(reflectfloor.left - mx, 2) + pow(reflectfloor.top - my, 2)) <= BSIZE) // 공이 반사판 왼쪽 상단 모서리에 부딪혔을 때
	{
		VectorY *= -1.0f;
		VectorX *= -1.0f;
		return;
	}
	

	if (sqrt(pow(mx - reflectfloor.right, 2) + pow(reflectfloor.top - my, 2)) < BSIZE)		// 공이 반사판 오른쪽 상단 모서리에 부딪혔을 때
	{
		VectorY *= -1.0f;
		VectorX *= -1.0f;
		return;
	}

	if (sqrt(pow(reflectfloor.left - mx, 2) + pow(my - reflectfloor.bottom, 2)) < BSIZE)		// 공이 반사판 왼쪽 하단 모서리에 부딪혔을 때
	{
		VectorY *= -1.0f;
		VectorX *= -1.0f;
		return;
	}

	if (sqrt(pow(mx - reflectfloor.right, 2) + pow(my - reflectfloor.bottom, 2)) < BSIZE)		// 공이 반사판 오른쪽 상단 모서리에 부딪혔을 때
	{
		VectorY *= -1.0f;
		VectorX *= -1.0f;
		return;
	}
}


void InitializeBrick()
{
	int startX = 0;
	int startY = 0;

	for (int row = 0; row < 4; row++)
	{
		for (int col = 0; col < 5; col++)
		{
			int left = startX + col * brickWidth;    // 각 열에 맞춰 X 좌표 계산
			int top = startY + row * brickHeight;    // 각 행에 맞춰 Y 좌표 계산
			int right = left + brickWidth;           // 사각형의 오른쪽 경계 계산
			int bottom = top + brickHeight;          // 사각형의 아래쪽 경계 계산

			brick[row][col] = { left, top, right, bottom };
			brickActive[row][col] = true;
		}
	}
}

void DrawBrick(HDC hdc)
{
	for (int row = 0; row < 4; row++)
	{
		for (int col = 0; col < 5; col++)
		{
			// 사각형 출력
			if (brickActive[row][col])
			{
				Rectangle(hdc, brick[row][col].left, brick[row][col].top,
					brick[row][col].right, brick[row][col].bottom);
			}
		}
	}
}

void CCircle::BrickCollision()
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			if (brickActive[i][j])
			{
				if (my + BSIZE >= brick[i][j].top && my - BSIZE <= brick[i][j].bottom)        // 반사판에 대한 충돌
				{
					if (brick[i][j].left < mx + BSIZE && mx + BSIZE < brick[i][j].right)
					{
						VectorY *= -1;
						brickActive[i][j] = false;
						break;
					}
				}


				if (mx + BSIZE >= brick[i][j].left && mx - BSIZE <= brick[i][j].right)
				{
					if (brick[i][j].top < my + BSIZE && my + BSIZE < brick[i][j].bottom)
					{
						VectorX *= -1;
						brickActive[i][j] = false;
						break;
					}
				}
			}
		}
	}
}

 

알카노이드(Alkanoid)

업데이트 할 점

1. 공 속도 늘리기

2. 점수

3. 게임 오버 판정

4. 아이템(공 개수 or 공 속도 감소 등)

5. 2번 이상 맞을 수 있는 벽돌

6. 더블 버퍼링 적용

'Programming > WindowAPI' 카테고리의 다른 글

[WindowAPI] 키보드 입력 사각형 처리  (0) 2025.01.09
[WindowAPI] 격자 그리기  (0) 2025.01.08
[WindowAPI] 원 그리기  (1) 2025.01.08