Programming Language/Windows API

두더지잡기 게임

D4tai1 2018. 9. 30.

1. 두더지잡기 게임을 모티브로 한 원 잡기.

[1] 게임을 진행할 영역을 얻어와야 한다.

[2] 마우스 좌표가 원의 영역내에 있다면 점수가 증가한다.

[3] 1000점이 초과되면 스테이지가 증가한다.

[4] 10스테이지가 되면 메세지가 출력된다.

[5] 원은 1초에 한번씩 좌표가 변경된다.

[6] 스테이지가 올라갈 수록 원의 크기는 줄어든다.

 

2. 소스

1) 사용자 정의함수

#include<time.h>
#include<math.h>

void Init(HWND hWnd, LPRECT rect, int *x, int *y, int *r, int *score, int *stage) {
	srand(time(NULL));

	GetClientRect(hWnd, rect);
	//창 생성 시 화면의 좌표영역을 얻어온다.

	*x = rand() % rect->right;
	*y = rand() % rect->bottom;

	*r = 185;
	//원의 반지름

	*score = 0;
	*stage = 1;

	SetTimer(hWnd, 1, 1000, NULL);
	//1번타이머 = 1초에 한 번씩 WM_TIMER메세지 발생
}

void Draw(HDC hdc, LPRECT rect, int *x, int *y, int r, int score, int stage) {
	TCHAR str[80];

	_stprintf_s(str, _T("스테이지 : %d     점수 : %d     좌표 : (%d, %d)     반지름 : %d"), stage, score, *x, *y, r);
	TextOut(hdc, 400, 5, str, _tcslen(str));
	//현재 정보 출력

	Rectangle(hdc, 5, 25, rect->right - 5, rect->bottom - 5);
	// 전체 맵 그리기

	SelectObject(hdc, CreateSolidBrush(RGB(255, 0, 0)));
	// 창의 브러시속성에 빨간색을 부여

	Ellipse(hdc, *x - r, *y - r, *x + r, *y + r);
	//빨간색 원 그리기
}

void Timer(HWND hWnd, LPRECT rect, int *x, int *y) {
	*x = rand() % rect->right;
	*y = rand() % rect->bottom;
	//영역 내 원의 좌표 얻어오기

	InvalidateRgn(hWnd, NULL, TRUE);
}

double Length(int x1, int y1, int x2, int y2) {
	return sqrt((x2 - x1)*(x2 - x1) + (y2 - y1) * (y2 - y1));
	//클릭한 마우스의 범위를 구하기 위해 사용하며 원으로부터 거리 반환
}

void Mouse(HWND hWnd, LPARAM lParam, LPRECT rect, int *x, int *y, int *r, int *score, int *stage) {
	int mx = LOWORD(lParam);
	int my = HIWORD(lParam);
	//마우스의 좌표 얻어오기

	//if (*x - *r < mx && mx < *x + *r && *y - *r < my && my < *y + *r) {
	if (Length(*x, *y, mx, my) < *r) {
		//마우스의 좌표가 원의 내부에 있다면?

		*score += 100;
		//스코어 증가

		*x = rand() % rect->right;
		*y = rand() % rect->bottom;
		//새로그리기 위한 원의 좌표 확보

		if (1000 < *score) {
			//점수가 1000점을 초과했다면?
			(*stage)++;
			//다음스테이지로
			*score = 0;
			//스코어는 초기화
			*r -= 20;
			//난이도 조절을 위해 원의 반지름은 감소
		}

		if (10 < *stage) {
			MessageBox(hWnd, _T("WOW!!!"), _T("you are great!!"), MB_OK);
			// 10스테이지가 넘어가면 메세지 출력
		}

		KillTimer(hWnd, 1);
		//1번타이머 죽이기

		SetTimer(hWnd, 1, 1000, NULL);
		//1번 타이머 생성
		//죽이고 다시 생성하는 이유는 클릭했을 때 기존 원의 위치에서 대기하지 않도록하기위함.

		InvalidateRgn(hWnd, NULL, TRUE);
	}

}

 

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

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

	static RECT space;
	//게임영역

	static int x;
	static int y;
	//원의 좌표

	static int r;
	//반지름

	static int stage;
	//스테이지
	static int score;
	//점수

	switch (message)
	{
	case WM_CREATE:
		Init(hWnd, &space, &x, &y, &r, &score, &stage);
		break;

	case WM_TIMER:
		Timer(hWnd, &space, &x, &y);
		break;

	case WM_LBUTTONDOWN:
		Mouse(hWnd, lParam, &space, &x, &y, &r, &score, &stage);
		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);
		Draw(hdc, &space, &x, &y, r, score, stage);
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

 

 

3. 시연[1]

 

- 마우스 클릭 시 점수가 올라가고 1000점 초과 시 스테이지가 올라가고 반지름은 줄어드는 것을 확인할 수 있다.

- 그러나 이렇게 할 경우 9스테이지에서는 원이 거의 보이지 않는다.

 

4. 시연[2]

1) 치트엔진을 사용하여 10스테이지를 달성하고 메세지의 내용을 확인하려 한다.

- 치트엔진을 실행 후 현재 실행중인 두더지게임을 올린다.

 

- 현재 점수가 200이므로 메모리 상에 200이라는 값이 있는 주소를 검색한다.

 

- first scan 시 5개의 메모리주소에 200이라는 값이 들어있다.

- 그러나 이 중 어떤 것이 점수인지 모르므로 점수 점수를 올린다.

 

- 원을 클릭해서 점수를 300으로 올리고 300을 next scan 한다.

 

- 점수가 들어있는 주소를 확인하고 이 주소의 값을 1111로 변경하였다.

 

- 스테이지가 6으로 오른 것을 확인할 수 있다.

 

- 이렇게 할 경우 여러번 해야해서 번거로우므로 반지름의 사이즈를 변경해보려고 한다.

- 현재 반지름인 85를 first scan한다.

 

- 스테이지를 올린 후 반지름의 크기에 변화를 주어 65로 next scan한다.

 

- 반지름이 저장되어 있는 주소를 찾았으므로 1000정도로 변경한다.

 

- 이 경우 원의 반지름이 커져서 영역 대부분을 차지하므로 수월하게 스테이지를 올릴 수 있다.

 

- 11스테이지까지 올린 후 출력되는 메세지를 확인할 수 있다.

 

[1] 변경 불가능하도록 하는 방법

 - 출력되는 값을 기준값 + 알파 식으로 부여한다.

 ex) 점수의 메모리에 있는 값을 50이라고 잡으면 출력시에는 항상 score - 50을 출력한다.

 

※ 치트엔진은 휴리스틱을 이용하여 찾는방법을 이용한다.

한 마디로 값만 알면 찾을 수 있기에 실제 값이랑 보여지는 값이랑 다르게 하면 된다.

그러나 리버싱으로 변경하는 것은 단순히 값을 다르게 하는 것만으로는 불가능하며 복잡도와 난독화를 이용해야 한다.

 

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

메뉴 및 공용대화상자  (0) 2018.10.09
지렁이게임  (0) 2018.09.30
입력된 방향키 확인  (0) 2018.09.30
사각형 그리기  (0) 2018.09.30
도형 자동이동  (0) 2018.09.30

댓글