글
[Block 클래스의 Class Diagram]
테트리스에서 사용되는 하나의 블럭을 (다음 블럭 포함) 나타내는 클래스인 Block 에 대해서 살펴보려고 합니다. Block.h 와 Block.c 를 분석해보겠습니다.
[Block.h]
#ifndef _BLOCK_H #define _BLOCK_H typedef struct _point{ int x; int y; }Point; #define POSITIONS_SIZE 4 typedef struct _block{ Point positions[POSITIONS_SIZE][POSITIONS_SIZE]; int current; int next; int direction; }Block; Block Block_Make(int isFirst, Block block); Block Block_Move(Block block, int direction); Point* Block_GetPositions(Block block); void Block_PrintNext(Block block, int x, int y); #endif
1 ~ 2, 23 라인
헤더파일의 중복 포함을 방지하는 guard 입니다.
4 ~ 7 라인
Point 구조체의 정의부입니다. 멤버로는 x 좌표, y 좌표를 가지고 있습니다.
9 라인
Block 구조체의 멤버로 사용될 positions 배열의 크기를 매크로로 설정합니다. (= 4)
11 ~ 16 라인
Block 구조체의 정의부입니다. Block 구조체는 테트리스에서 사용되는 하나의 블럭을 나타냅니다. (현재 블럭, 다음 나올 블럭 포함) 멤버로는 positions 배열이 있는데, 이는 4 X 4 크기의 2 차원 배열로, Point 형 (x, y 좌표) 으로 구성되어 있습니다. 이는, 하나의 블럭이 4 개의 정사각형 상자로 구성되어 있기 때문에, 4 개의 상자의 x, y 좌표를 담기 위해 사용됩니다. current 는 여러 타입의 블럭들 중에서 현재 이동하는 블럭의 타입을 나타냅니다. next 는 여러 타입의 블럭들 중에서 다음에 나올 블럭의 타입을 나타냅니다. direction 은 블럭의 방향을 나타내고, 위쪽, 오른쪽, 아래쪽, 왼쪽 을 나타냅니다.
18 ~ 21 라인
소스 파일에서 사용될 Block 관련 함수들의 선언부입니다.
[Block.c]
#include <time.h> #include <windows.h> #include <stdio.h> #include "Block.h" #include "Util.h" #include "Constant.h" #define BLOCK_EXAMPLES_SIZE 6 const static Point blockExamples[BLOCK_EXAMPLES_SIZE][POSITIONS_SIZE][POSITIONS_SIZE] = { //ㅁㅁㅁㅁ { { { 0, 5 }, { 0, 6 }, { 0, 7 }, { 0, 8 } }, { { -2, 6 }, { -1, 6 }, { 0, 6 }, { 1, 6 } }, { { 0, 5 }, { 0, 6 }, { 0, 7 }, { 0, 8 } }, { { -2, 6 }, { -1, 6 }, { 0, 6 }, { 1, 6 } } }, // ㅁ //ㅁㅁㅁ { { { 0, 8 }, { 1, 6 }, { 1, 7 }, { 1, 8 } }, { { -1, 7 }, { 0, 7 }, { 1, 7 }, { 1, 8 } }, { { 0, 6 }, { 0, 7 }, { 0, 8 }, { 1, 6 } }, { { -1, 6 }, { -1, 7 }, { 0, 7 }, { 1, 7 } } }, // ㅁㅁ //ㅁㅁ { { { 0, 7 }, { 0, 8 }, { 1, 6 }, { 1, 7 } }, { { -1, 6 }, { 0, 6 }, { 0, 7 }, { 1, 7 } }, { { 0, 7 }, { 0, 8 }, { 1, 6 }, { 1, 7 } }, { { -1, 6 }, { 0, 6 }, { 0, 7 }, { 1, 7 } } }, //ㅁㅁ // ㅁㅁ { { { 0, 6 }, { 0, 7 }, { 1, 7 }, { 1, 8 } }, { { -1, 8 }, { 0, 8 }, { 0, 7 }, { 1, 7 } }, { { 0, 6 }, { 0, 7 }, { 1, 7 }, { 1, 8 } }, { { -1, 8 }, { 0, 8 }, { 0, 7 }, { 1, 7 } } }, //ㅁ //ㅁㅁㅁ { { { 0, 6 }, { 1, 6 }, { 1, 7 }, { 1, 8 } }, { { -1, 8 }, { -1, 7 }, { 0, 7 }, { 1, 7 } }, { { 0, 6 }, { 0, 7 }, { 0, 8 }, { 1, 8 } }, { { -1, 7 }, { 0, 7 }, { 1, 7 }, { 1, 6 } } }, //ㅁㅁ //ㅁㅁ { { { 0, 6 }, { 0, 7 }, { 1, 6 }, { 1, 7 } }, { { 0, 6 }, { 0, 7 }, { 1, 6 }, { 1, 7 } }, { { 0, 6 }, { 0, 7 }, { 1, 6 }, { 1, 7 } }, { { 0, 6 }, { 0, 7 }, { 1, 6 }, { 1, 7 } } } }; static Block _Block_MoveToDown(Block block); static Block _Block_MoveToLeft(Block block); static Block _Block_MoveToRight(Block block); static Block _Block_RotateRight(Block block); Block Block_Make(int isFirst, Block block){ int i; int j; srand((unsigned int)time(NULL)); if (isFirst){ block.current = rand() % BLOCK_EXAMPLES_SIZE; } else{ block.current = block.next; } for (i = 0; i < POSITIONS_SIZE; i++){ for (j = 0; j < POSITIONS_SIZE; j++){ block.positions[i][j] = blockExamples[block.current][i][j]; } } block.next = rand() % BLOCK_EXAMPLES_SIZE; block.direction = UP; return block; } Block Block_Move(Block block, int direction){ switch (direction){ case LEFT: return _Block_MoveToLeft(block); case RIGHT: return _Block_MoveToRight(block); case DOWN: return _Block_MoveToDown(block); case UP: return _Block_RotateRight(block); } return _Block_MoveToDown(block); } Point* Block_GetPositions(Block block){ return block.positions[block.direction]; } void Block_PrintNext(Block block, int x, int y){ CursorUtil_GotoXY(x, y); printf("Next block : "); x += 3; y += 2; CursorUtil_GotoXY(x, y++); switch (block.next){ case 0: printf("■■■■"); CursorUtil_GotoXY(x, y++); printf(" "); break; case 1: printf(" ■"); CursorUtil_GotoXY(x, y++); printf(" ■■■"); break; case 2: printf(" ■■"); CursorUtil_GotoXY(x, y++); printf(" ■■ "); break; case 3: printf(" ■■ "); CursorUtil_GotoXY(x, y++); printf(" ■■"); break; case 4: printf("■ "); CursorUtil_GotoXY(x, y++); printf("■■■ "); break; case 5: printf(" ■■ "); CursorUtil_GotoXY(x, y++); printf(" ■■ "); break; } } static Block _Block_MoveToDown(Block block){ int i; int j; for (i = 0; i < POSITIONS_SIZE; i++){ for (j = 0; j < POSITIONS_SIZE; j++){ block.positions[i][j].x++; } } return block; } static Block _Block_MoveToLeft(Block block){ int i; int j; for (i = 0; i < POSITIONS_SIZE; i++){ for (j = 0; j < POSITIONS_SIZE; j++){ block.positions[i][j].y--; } } return block; } static Block _Block_MoveToRight(Block block){ int i; int j; for (i = 0; i < POSITIONS_SIZE; i++){ for (j = 0; j < POSITIONS_SIZE; j++){ block.positions[i][j].y++; } } return block; } static Block _Block_RotateRight(Block block){ block.direction = (block.direction + 1) % POSITIONS_SIZE; return block; }
1 ~ 6 라인
필요한 헤더파일들을 include 합니다.
8 라인
생성될 수 있는 여러 블럭 타입들의 사이즈를 매크로로 정의합니다.
10 ~ 58 라인
생성될 수 있는 블럭 타입들을 미리 상수 배열로 정의해 놓습니다. const 를 붙인 이유는 상수화하기 위함이고, static 은 현재 파일 내에서만 내부적으로 사용하기 위함입니다. 총 6 개의 타입의 블럭들을 위쪽, 오른쪽, 아래쪽, 왼쪽 방향에 대한 상대좌표로 저장해 놓습니다.
예를 들어 ㅁㅁㅁㅁ 와 같은 모양의 블럭은 위쪽 방향일 때 (0,5), (0, 6), (0, 7), (0, 8) 의 위치를 가집니다. 만약 한번 회전하여 오른쪽 방향일 때는 (-2, 6), (-1, 6), (0, 6), (1, 6) 의 위치를 가집니다. 한번 더 회전하여 아래쪽 방향일 때는 위쪽 방향과 마찬가지로 (0,5), (0, 6), (0, 7), (0, 8) 를 가지게 되고, 마지막으로 한번 더 회전하여 왼쪽 방향일 때는 오른쪽 방향과 같은 (-2, 6), (-1, 6), (0, 6), (1, 6) 의 위치를 가집니다. 이처럼 x 좌표는 0 을 기준으로 하고, y 좌표는 테트리스의 중간 지점을 기준으로 하여, 각각 6 개의 모양 별로, 위쪽, 오른쪽, 아래쪽, 왼쪽으로 회전했을 때 좌표 위치를 배열에 미리 저장해 놓습니다.
60 ~ 63 라인
Block.c 소스 파일 내부에서만 사용될 내부 함수의 선언부입니다.
65 라인
Block_Make 함수의 정의부입니다. 매개변수로 isFirst (블록이 테트리스 시작하고 난 후에 가장 처음에 만들어지는지 아닌지 여부) 와 block (실제 만들어서 내용물을 저장하여 반환할 블럭) 을 받고, 만들어진 block 을 반환합니다.
68 ~ 74 라인
srand 와 time 함수를 통해 난수 생성 전 시드를 섞어줍니다. 그리고 처음 블럭을 생성하는 경우면 (isFirst 가 True 이면) block.current 에 난수를 생성해서 저장하고 (0 ~ 5 사이의 임의의 정수), 처음이 아니면, block.current 에는 이전에 생성해 두었던, block.next 를 가져와서 저장합니다.
즉, 쉽게 말하면, 처음 블럭을 생성하는 경우에는, current 를 난수로 생성해야 하고, 두번 째 부터는, current 를 다시 난수로 생성하지 말고, 이전에 생성해 두었던, next 에서 가져오면 된다는 의미입니다.
(의사 난수 (랜덤 숫자) 생성하기 참고)
http://kkikkodev.tistory.com/52
75 ~ 59 라인
block.positions 배열에 위에서 생성 혹은 next 에서 가져온 block.current 를 기반으로 blockExamples (6 개 모양의 블록들을 4 방향 모두 상대좌표 위치를 저장해 놓은 배열) 에서 블럭을 가져와서 4 개의 상자의 위치를 저장합니다.
80 라인
block.next 에 난수를 생성하여 저장합니다. (다음 블럭을 생성합니다.)
81 라인
block.direction 을 UP (= 0) 으로 초기화합니다. (위쪽 방향을 시작 방향으로 설정)
82 라인
생성한 block 을 반환합니다.
85 라인
Block_Move 함수의 정의부입니다. 매개변수로, block 과 direction (사용자가 입력한 방향키 값) 을 받아서, 4 가지 방향키에 해당하는 처리를 한 block 을 반환합니다.
86 ~ 96 라인
direction 을 살펴봐서, LEFT (= 3) 이면 Block_MoveToLeft 함수를 호출하여 얻은 반환 값을 retrun 하고, RIGHT (= 1) 이면 Block_MoveToRight 함수를 호출하여 얻은 반환 값을 return 하고, 마찬가지로, DOWN (= 2) 인 경우는 Block_MoveToDown 함수를, UP (= 0) 인 경우는, Block_RotateRight 함수릂 호출하여 얻은 반환 값을 return 합니다. 그리고 마지막 줄에서는, 혹시나 이 4 가지 방향 말고 다른 값이 들어왔을 경우를 대비해서 Block_MoveToDown 함수를 호출하도록 처리하였습니다. 여기서 주목할 점은, direction 이 UP 일 때는, Move 가 아닌 Rotate 를 해야 한다는 것입니다. 오른쪽으로 90 도씩 회전하게 됩니다.
99 라인
Block_GetPositions 함수의 정의부입니다. 매개변수로 block 을 받아서 positions 에서 현재 방향에 해당하는 요소를 반환합니다.
100 라인
매개변수로 받은 block 의 positions 의 block.direction 번째의 요소를 반환합니다. 반환형이 Point* 인 이유는 positions 가 2 차원 배열이기 때문에 X 번째의 요소는 1 차원 배열이 되기 때문입니다.
103 라인
Block_PrintNext 함수의 정의부입니다. 매개변수로 block 과 x, y 를 받습니다. 해당 좌표 (x, y) 에 다음 블럭을 출력하는 역할을 합니다.
104 ~ 140 라인
CursorUtil_GotoXY 함수를 통해 x, y 좌표로 출력 커서를 이동시킵니다.
(콘솔 커서 좌표 이동하기 참고)
http://kkikkodev.tistory.com/26
block.next 를 확인하여 0 부터 5 까지에 해당하는 타입의 블럭 모양들을 출력합니다. (총 6 가지 모양)
143 라인
_Block_MoveToDown 함수의 정의부입니다. 이 함수는 내부에서만 쓰이는 함수로 static 으로 정의되어 있습니다. 매개변수로 block 을 받아서 밑으로 한칸 이동시키고 반환하는 역할을 합니다.
144 ~ 151 라인
block.positions (블럭의 4 개 상자의 좌표들) 들의 x 좌표를 증가시킵니다. (아래로 한 칸 이동)
154 라인
_Block_MoveToLeft 함수의 정의부입니다. 이 함수는 매개변수로 block 을 받아서 왼쪽으로 한칸 이동시키고 반환하는 역할을 합니다.
155 ~ 162 라인
block.positions (블럭의 4 개 상자의 좌표들) 들의 y 좌표를 감소시킵니다. (왼쪽으로 한 칸 이동)
165 라인
_Block_MoveToRight 함수의 정의부입니다. 이 함수는 매개변수로 block 을 받아서 오른쪽으로 한칸 이동시키고 반환하는 역할을 합니다.
166 ~ 173 라인
block.positions (블럭의 4 개 상자의 좌표들) 들의 y 좌표를 증가시킵니다. (오른쪽으로 한 칸 이동)
176 라인
_Block_RotateRight 함수의 정의부입니다. 이 함수는 매개변수로 block 을 받아서 오른쪽 90 도 방향 (시계 방향) 으로 블럭을 돌리고, 반환하는 역할을 합니다.
177 ~ 178 라인
block.direction 을 1 증가시킵니다. (다음 방향으로 전환합니다.) % 처리는 0 -> 1 -> 2-> 3-> 0 으로 순환하게 만들게 하기 위해서 사용한 기법입니다.
'1.2) 프로젝트 > 테트리스' 카테고리의 다른 글
테트리스 ver 0.2 (1) - 실행 결과 & 프로젝트 개요 (0) | 2015.06.12 |
---|---|
테트리스 ver 0.1 (10) - 개발 완료 / 후기 (0) | 2015.06.12 |
테트리스 ver 0.1 (9) - 소스코드 구현 (Main) (0) | 2015.06.12 |
테트리스 ver 0.1 (8) - 소스코드 구현 (TetrisView) (0) | 2015.06.12 |
테트리스 ver 0.1 (7) - 소스코드 구현 (TetrisManager) (0) | 2015.06.12 |
테트리스 ver 0.1 (5) - 소스코드 구현 (Util) (0) | 2015.06.12 |
테트리스 ver 0.1 (4) - 소스코드 구현 (Constant) (0) | 2015.06.12 |
테트리스 ver 0.1 (3) - 프로그램 설계 (Use Case, Class, Sequence) (0) | 2015.06.12 |
테트리스 ver 0.1 (2) - 요구사항 분석 (0) | 2015.06.12 |
테트리스 ver 0.1 (1) - 실행 결과 & 프로젝트 개요 (0) | 2015.04.28 |
RECENT COMMENT