<테트리스 게임> 프로젝트
- 블록을 만들고 움직이기
작업 환경 : 윈도우 10, Visual Studio, C++ (Win32 Console)
참고 서적 : C프로그래밍 파워 업그레이드 (윤성우 저)
- 커서 위치 이동
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> #include <Windows.h> using namespace std; int main(void) { COORD pos1 = { 0, 2 }; // x,y 좌표 COORD pos2 = { 6, 6 }; COORD pos3 = { 15, 4 }; HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hConsoleOut, pos1); cout << "첫 번째 인사 : 안녕하세요."; getchar(); SetConsoleCursorPosition(hConsoleOut, pos2); cout << "두 번째 인사 : 안녕하세요."; getchar(); SetConsoleCursorPosition(hConsoleOut, pos3); cout << "세 번째 인사 : 안녕하세요."; getchar(); return 0; } | cs |
2행 : include는 ms가 제공해주는 구조체나 함수를 사용하기 위함
6-8행 : COORD는 구조체로서 다음과 같이 선언 및 정의되어 있음
1 2 3 4 5 | typedef struct _COORD { int x; int y; } COORD; | cs |
10행 : GetStdHandle이란 함수를 호출하면서 인자로 STD_OUTPUT_HANDLE를 전달하면 콘솔 출력 창을 조절 가능하다.
HANDLE은 MS가 정의한 자료형으로 이 자료형의 변수는 hConsoleOut이 된다. 여기에 GetStdHandle 함수의 반환값을 저장하는 것이다.
12행 : SetConsoleCursorPosition 함수는 콘솔의 커서 위치를 변경하는 기능의 함수
이 함수를 호출하면서 첫번째 인자로 hConsoleOut, 두 번째 인자로 커서 좌표 경로를 전달하고 있다.
커서 위치는 좌상단 x,y좌표가 0,0 이며 x값이 증가할 수록 오른쪽으로, y값이 증가할수록 아래쪽으로 이동한다.
- 현재 커서 위치 정보 얻어오기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <iostream> #include <windows.h> using namespace std; int main(void) { CONSOLE_SCREEN_BUFFER_INFO curInfo; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); cout << "[" << curInfo.dwCursorPosition.X << ", " << curInfo.dwCursorPosition.Y << "]" << endl; cout << "First Hello world" << endl; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); cout << "[" << curInfo.dwCursorPosition.X << ", " << curInfo.dwCursorPosition.Y << "]" << endl; cout << "Second Hello world" << endl; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); cout << "[" << curInfo.dwCursorPosition.X << ", " << curInfo.dwCursorPosition.Y << "]" << endl; return 0; } | cs |
8행 : CONSOLE_SCREEN_BUFFER_INFO는 MS에서 콘솔출력창의 정보를 담기 위해 정의한 구조체
10행 : GetConsoleScreenBufferInfo 함수는 콘솔 출력창의 정보를 얻기위해 호출하는 함수
첫번째 인자는 위의 예제에서 호출한 SetConsoleCursorPosition 함수의 첫번째 인자와 동일하다.
두번째 인자에는 8행에 선언된 구조체 변수의 주소 값이 전달된다.
11,15,19행 : curInfo에 저장된 여러 정보중 현재 커서의 x,y 좌표값을 보여주고 있다.
- 벽돌을 그리는 원리
블럭 모델은 다음과 같이 배열에 정의하며, 블럭이 있는 공간은 1, 없는 공간은 0
원 위치와 회전 모델까지 포함하여 총 4개를 정의한다.
□ □ □ □
■ □ □ □
■ ■ ■ □
□ □ □ □
배열을 조회하여 index의 값이 1이면 ■ 출력 (특문은 2칸을 잡아먹는다)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #include <iostream> #include <Windows.h> using namespace std; void ShowBlock(char blockInfo[][4]); // ShowBlock 함수는 인자로 전달되는 정보를 토대로 콘솔 출력 창에 블록을 그림 void SetCurrentCursorPos(int x, int y); // 이 함수는 x,y 좌표를 입력받아 커서 위치를 이동시키는 기능 COORD GetCurrentCursorPos(void); // 이 함수는 현재 커서 위치 정보를 반환, 반환은 구조체 COORD를 기반으로 이뤄짐 char blockModel[][4][4] = { /* ■ ■ ■ ■ */ { // 원 위치 { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 0 }, { 0, 0, 0, 0 } }, { // 회전 {0, 1, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0} }, { // 회전 { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 } }, { // 회전 { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 } } }; int main(void) { SetCurrentCursorPos(0, 0); // 커서 위치를 [0, 0] 으로 이동 ShowBlock(blockModel[0]); // 커서 위치를 기준으로 블록을 그림 SetCurrentCursorPos(0, 8); ShowBlock(blockModel[1]); SetCurrentCursorPos(12, 0); ShowBlock(blockModel[2]); SetCurrentCursorPos(12, 8); ShowBlock(blockModel[3]); getchar(); return 0; } void ShowBlock(char blockInfo[][4]) { int x, y; COORD curPos = GetCurrentCursorPos(); for (y = 0; y < 4; y++) // 중첩된 for문은 인자로 전달된 배열 전체 정보를 참조하기 위함이다. { for (x = 0; x < 4; x++) { SetCurrentCursorPos(curPos.X + (x * 2), curPos.Y + y); // 블록을 그리기 위해 커서위치 이동. ■는 특문이기 때문에 x좌표에 *2 if (blockInfo[y][x] == 1) cout << "■" ; } } SetCurrentCursorPos(curPos.X, curPos.Y); // 커서 위치 원래대로 } void SetCurrentCursorPos(int x, int y) { COORD pos = {x, y}; HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); // 콘솔 출력창의 제어 SetConsoleCursorPosition(hConsoleOut, pos); } COORD GetCurrentCursorPos(void) // 커서 위치 정보를 반환하는 함수 { COORD curPoint; CONSOLE_SCREEN_BUFFER_INFO curInfo; GetConsoleScreenBufferInfo( // 콘솔 출력창의 정보를 얻음 GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); curPoint.X = curInfo.dwCursorPosition.X; curPoint.Y = curInfo.dwCursorPosition.Y; return curPoint; } | cs |
61행 SetCurrentCursorPos 함수에서 63-65행 코드는 운영체제에 의존적이다. 또한 기능은 가능하면 함수로 정의하고 호출하는 것이 좋다.
Output :
- 커서 깜박임 제거
위의 출력 결과를 보면 커서가 깜박이는 것을 볼 수 있다.
그래서 커서의 깜빡임을 제거하는 함수가 필요하다. (깜박임만 제거하기에 커서 위치에서의 출력은 여전히 가능)
1 2 3 4 5 6 7 8 | void RemoveCursor(void) { CONSOLE_CURSOR_INFO curInfo; HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleCursorInfo(hConsoleOut, &curInfo); curInfo.bVisible = FALSE; SetConsoleCursorInfo(hConsoleOut, &curInfo); } | cs |
위 함수를 정의하고 main함수에서 호출 // RemoveCursor();
3행 : CONSOLE_CURSOR_INFO는 콘솔의 커서 정보를 얻기위해 사용되는 구조체
5행 : GetConsoleCursorInfo 함수는 콘솔출력창의 정보를 반환하는 함수.
3행에서 선언한 변수의 주소가 두 번째 인자로 전달되고, curInfo변수에 커서 정보가 채워진다.
6행 : CONSOLE_CURSOR_INFO구조체의 bVisible 멤버는 표준 출력 커서 표시 유무를 담당. 숨기려면 FALSE
7행 : 함수 호출을 통해 변경된 특성을 저장
[ 테트리스의 28가지 블록 모델 ]
이 모델들은 blockInfo.h에 배열로 정의해 include하기로 한다.
총 28가지 모델이 되므로(회전시 모양이 변하지않는 것도 포함)
결과적으로 char blockModel[7*4][4][4]이 된다.
블록의 이동을 위한 Sleep 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> #include <Windows.h> int main(void) { RemoveCursor(); puts("One"); Sleep(1000); puts("Two"); Sleep(2000); puts("Three"); Sleep(3000); puts("Four"); Sleep(3000); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | #include <iostream> #include <Windows.h> #include <time.h> #include "blockInfo.h" // 블럭 정보 (4x7가지 블럭 모델) using namespace std; void ShowBlock(char blockInfo[][4]); // ShowBlock 함수는 인자로 전달되는 정보를 토대로 콘솔 출력 창에 블록을 그림 void DeleteBlock(char blockInfo[][4]); // 블럭을 지우기 위한 함수 void BlockDown(void); void RandomBlock(void); void SetCurrentCursorPos(int x, int y); // 이 함수는 x,y 좌표를 입력받아 커서 위치를 이동시키는 기능 COORD GetCurrentCursorPos(void); // 이 함수는 현재 커서 위치 정보를 반환, 반환은 구조체 COORD를 기반으로 이뤄짐 void RemoveCursor(void); int GetCurBlockIdx(void); int CurBlockModel; int curPosX, curPosY; void Init(int x, int y) { if (x < 0 || y < 0) return; curPosX = x; curPosY = y; } int main(void) { RemoveCursor(); SetCurrentCursorPos(curPosX=10, curPosY=0); // 커서 위치를 [ , ] 으로 초기화 RandomBlock(); while (TRUE) { BlockDown(); Sleep(1000); } return 0; } void ShowBlock(char blockInfo[][4]) { int x, y; COORD curPos = GetCurrentCursorPos(); for (y = 0; y < 4; y++) // 중첩된 for문은 인자로 전달된 배열 전체 정보를 참조하기 위함이다. { for (x = 0; x < 4; x++) { SetCurrentCursorPos(curPos.X + (x * 2), curPos.Y + y); // 블록을 그리기 위해 커서위치 이동. ■는 특문이기 때문에 x좌표에 *2 if (blockInfo[y][x] == 1) cout << "■"; } } SetCurrentCursorPos(curPos.X, curPos.Y); // 커서 위치 원래대로 } void DeleteBlock(char blockInfo[][4]) { int x, y; COORD curPos = GetCurrentCursorPos(); for (y = 0; y < 4; y++) // 중첩된 for문은 인자로 전달된 배열 전체 정보를 참조하기 위함이다. { for (x = 0; x < 4; x++) { SetCurrentCursorPos(curPos.X + (x * 2), curPos.Y + y); // 블록을 그리기 위해 커서위치 이동. ■는 특문이기 때문에 x좌표에 *2 if (blockInfo[y][x] == 1) cout << " "; } } SetCurrentCursorPos(curPos.X, curPos.Y); // 커서 위치 원래대로 } void SetCurrentCursorPos(int x, int y) { COORD pos = { x, y }; HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); // 콘솔 출력창의 제어 SetConsoleCursorPosition(hConsoleOut, pos); } COORD GetCurrentCursorPos(void) // 커서 위치 정보를 반환하는 함수 { COORD curPoint; CONSOLE_SCREEN_BUFFER_INFO curInfo; GetConsoleScreenBufferInfo( // 콘솔 출력창의 정보를 얻음 GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); curPoint.X = curInfo.dwCursorPosition.X; curPoint.Y = curInfo.dwCursorPosition.Y; return curPoint; } void RemoveCursor(void) { CONSOLE_CURSOR_INFO curInfo; HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleCursorInfo(hConsoleOut, &curInfo); curInfo.bVisible = FALSE; SetConsoleCursorInfo(hConsoleOut, &curInfo); } void BlockDown(void) { DeleteBlock(blockModel[GetCurBlockIdx()]); curPosY += 1; SetCurrentCursorPos(curPosX, curPosY); ShowBlock(blockModel[GetCurBlockIdx()]); } void RandomBlock(void) { srand((unsigned int)time(NULL)); CurBlockModel = (rand() % 7) * 4; } int GetCurBlockIdx(void) { return CurBlockModel; } | cs |
Output :
■
■■■
정의된 블록모델수 중 (위의 예시에서는 7x4) 랜덤함수에 의해 선택된 블록이 1초 간격으로 아래로 이동
블록(특수문자)의 한 칸이 콘솔 출력창 두 칸을 잡아먹는 것에 유의
112행의 RandomBlock 함수는 배열의 인덱스 값 중 하나를 무작위로 선택하기 위함이다.
이 함수는 0,4,8,12,16,20,24 중 하나의 숫자를 랜덤함수를 통해 생성하고 변수 CurBlockModel에 저장한다.
이렇게 저장된 값은 배열 blockModel에 저장된 기존 블럭의 index 값으로 사용된다.
118행 GetCurBlockIdx 함수가 변수 CurBlockModel에 저장된 값을 반환한다.
'코스웨어 > 16년 스마트컨트롤러' 카테고리의 다른 글
2016-10-13_조재찬_스터디일지_CPP-상속의 이해 (0) | 2016.10.13 |
---|---|
2016-10-09_조재찬_ 프로젝트 일지_테트리스 게임 (4) (0) | 2016.10.09 |
2016-10-07_조재찬_ 프로젝트 일지_테트리스 게임 (3) (0) | 2016.10.07 |
2016-10-05_조재찬_ 프로젝트 일지_테트리스 게임 (2) (0) | 2016.10.05 |
2016-09-29_조재찬_스터디일지_CPP-'상속'의 기본 개념, 핸들러 클래스 (0) | 2016.09.30 |
2016-09-29_조재찬_스터디일지_CPP-const, friend, static, mutable 선언 (0) | 2016.09.29 |
2016-09-28_조재찬_스터디일지_CPP-복사 생성자, 얕은 복사와 깊은 복사, 임시 객체 (0) | 2016.09.28 |
2016-09-27_조재찬_스터디일지_CPP-객체 배열과 this포인터, Self-Ref. (0) | 2016.09.27 |