Programming Language/Windows API

지렁이게임

D4tai1 2018. 9. 30.

1. 뱀 그리기

- 먼저 게임을 만들기 위해 규칙이 여러가지 필요하지만 게임에 대한 깊이가 낮아 핸디캡이나 죽는 것이 없다.

[1] 화면 영역 내에서 뱀이 자유롭게 움직일 수 있다.

[2] 화면 영역 내에 뱀의 먹이가 파란색으로 표시된다.

[3] 뱀의 먹이는 일정 시간이 흐른 후에 없어지며, 다른 곳에 새로 먹이가 생성된다.

[4] 뱀은 먹이를 먹으면 꼬리가 1씩 증가한다.

[5] 뱀의 머리는 빨간색이다.

[6] 뱀의 몸은 녹색이다.

----------------미 추가사항------------------

[7] 머리가 자신의 몸에 닿으면 죽는다.

[8] 화면 영역 밖으로 나가도 죽는다.

[9] 30초에 한 번은 먹이를 먹어야 한다. (안 그럴 경우 가만히 있으면 죽지 않기 때문이다.)

 

2. 소스

1) 사용자 정의함수

#include<time.h>

void Init(HWND hWnd, int size) {
	srand(time(NULL));
	SetTimer(hWnd, 1, 5000, NULL);
	//5초마다 1번 타이머가 WM_TIMER 발생
}

void Outline(HWND hWnd, HDC hdc, RECT rect, int size) {
	//맵 표시
	int x = 10;
	int y = 30;
	TCHAR str[80];
	
	_stprintf_s(str, _T("범위좌표 : (%d, %d) / (%d, %d)"), 20, 40, rect.right - rect.right % 20, rect.bottom - rect.bottom % 20);
	TextOut(hdc, 500, 0, str, _tcslen(str));

	for (x = 10; x < rect.right; x += 20) {
		Rectangle(hdc, x - size, y - size, x + size, y + size);
	}
	x = 10;

	for (y = 30; y < rect.bottom; y += 20) {
		Rectangle(hdc, x - size, y - size, x + size, y + size);
	}
	y -= 20;

	for (x = 10; x < rect.right; x += 20) {
		Rectangle(hdc, x - size, y - size, x + size, y + size);
	}
	x -= 20;

	for (y = 30; y < rect.bottom; y += 20) {
		Rectangle(hdc, x - size, y - size, x + size, y + size);
	}
}

void Snake(HDC hdc, int *x, int *y, int length) {
	for (int i = 0; i <= length; i++) {
		if (i == 0) {
			SelectObject(hdc, CreateSolidBrush(RGB(255, 0, 0)));
			//처음 그리는 머리는 빨간색
		}
		else {
			SelectObject(hdc, CreateSolidBrush(RGB(0, 255, 0)));
			//이후 그리는 몸은 녹색
		}
		Ellipse(hdc, x[i] - 10, y[i] - 10, x[i] + 10, y[i] + 10);
	}
	
	/*SelectObject(hdc, CreateSolidBrush(RGB(255, 0, 0)));
	Ellipse(hdc, x1 - 10, y1 - 10, x1 + 10, y1 + 10);
	SelectObject(hdc, CreateSolidBrush(RGB(0, 0, 255)));
	Ellipse(hdc, x2 - 10, y2 - 10, x2 + 10, y2 + 10);*/
}

void Food_Place(HWND hWnd, RECT rect, int *fx, int *fy) {
	int tmp;
	do {
		*fx = (rand() % rect.right);
		tmp = *fx % 10;
		*fx -= tmp;
	} while (*fx % 20 != 10);
	//먹이의 x좌표

	do {
		*fy = (rand() % rect.bottom);
		tmp = *fy % 10;
		*fy -= tmp;
	} while (*fy % 20 != 10);
	//먹이의 y좌표
	//영역 내 먹이를 표시해야 한다. 뱀의 좌표와 일치하기 위해서는 항상 20의 간격을 유지해야 한다.
	
	InvalidateRgn(hWnd, NULL, TRUE);
}

void Food_Draw(HDC hdc, int *fx, int *fy) {
	TCHAR tmp[20];
	_stprintf_s(tmp, _T("먹이좌표 : [%d, %d]"), *fx, *fy);
	TextOut(hdc, 20, 0, tmp, _tcslen(tmp));

	SelectObject(hdc, CreateSolidBrush(RGB(0, 0, 255)));
	//먹이를 먹었을 경우 파란색으로 다시 그린다.
	Rectangle(hdc, *fx - 10, *fy - 10, *fx + 10, *fy + 10);
}

 

2) 윈도우 메세지처리함수

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	static int width[100] = { 50, 70, 90, 110, 130 };
	static int height[100] = { 50, 50, 50, 50, 50 };
	//뱀의 시작위치

	static int length = 4;
	//뱀의 길이
	
	static int fx;
	static int fy;
	//먹이의 위치

	static RECT rect;	
	//맵의 영역

	static int size;
	//맵테두리 사각형길이의 반

    switch (message)
    {
	case WM_CREATE:
		size = 10;
	
		GetClientRect(hWnd, &rect);

		Init(hWnd, size);

		break;

	case WM_TIMER:
	{
		hdc = GetDC(hWnd);

		Food_Place(hWnd, rect, &fx, &fy);
		//먹이 좌표 재생성

		ReleaseDC(hWnd, hdc);
	}
	break;

	case WM_KEYDOWN:
	{
		if (wParam == VK_UP) {
			
			for (int i = 0; i < length; i++) {
				width[length - i] = width[length - i - 1];
				height[length - i] = height[length - i - 1];
			}
			height[0] -= 20;
			//뱀의 머리는 위로 한 칸 이동, 몸은 한 칸씩 땡기기
		}
		else if (wParam == VK_DOWN) {
			
			for (int i = 0; i < length; i++) {
				width[length - i] = width[length - i - 1];
				height[length - i] = height[length - i - 1];
			}
			height[0] += 20;
			//뱀의 머리는 아래로 한 칸 이동, 몸은 한 칸씩 땡기기
		}
		else if (wParam == VK_LEFT) {
			
			for (int i = 0; i < length; i++) {
				width[length - i] = width[length - i - 1];
				height[length - i] = height[length - i - 1];
			}
			width[0] -= 20;
			//뱀의 머리는 왼쪽으로 한 칸 이동, 몸은 한 칸씩 땡기기
		}
		else if (wParam == VK_RIGHT) {
			
			for (int i = 0; i < length; i++) {
				width[length - i] = width[length - i - 1];
				height[length - i] = height[length - i - 1];
			}
			width[0] += 20;
			//뱀의 머리는 오른쪽으로 한 칸 이동, 몸은 한 칸씩 땡기기
		}

		if (width[0] == fx && height[0] == fy) {
			//뱀의 머리위치와 먹이의 위치가 같으면?
			width[length + 1] = width[length];
			height[length + 1] = height[length];
			length++;
			//뱀의 꼬리가 하나 증가

			Food_Place(hWnd, rect, &fx, &fy);
			//먹이 위치 재배치

			KillTimer(hWnd, 1);
			SetTimer(hWnd, 1, 5000, NULL);
			//타이머 처음부터 다시발동

			
			InvalidateRgn(hWnd, NULL, TRUE);
			TCHAR msg[40];
			TCHAR show[40];
			_stprintf_s(msg, _T("먹이를 먹어 꼬리가 +1 증가하였습니다."));
			_stprintf_s(show, _T("현재 꼬리의 길이는 %d칸 입니다."), length+1);
			MessageBox(hWnd, msg, show, MB_OK);
			//먹이를 먹었다는 메세지 표시
		}

		InvalidateRgn(hWnd, NULL, TRUE);

	}
	break;

    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 메뉴 선택을 구문 분석합니다:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
			hdc = BeginPaint(hWnd, &ps);
			Outline(hWnd, hdc, rect, size);
			//맵 영역그리기
			Snake(hdc, width, height, length);
			//뱀 그리기
			Food_Draw(hdc, &fx, &fy);
			//타이머에서 새로 만든 먹이좌표를 그리기
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

 

 

3. 시연

- 먹이를 먹으면 메세지박스가 출력되고, 먹이의 좌표는 변경되고, 뱀의 꼬리는 증가한다.

 

'Programming Language > Windows API' 카테고리의 다른 글

리소스파일  (0) 2018.12.09
메뉴 및 공용대화상자  (0) 2018.10.09
두더지잡기 게임  (0) 2018.09.30
입력된 방향키 확인  (0) 2018.09.30
사각형 그리기  (0) 2018.09.30

댓글