테트리스 개발 1일차

나묘쿠·2021년 12월 6일
0

테트리스

목록 보기
1/8

5일에 여러 생각들이 지나가면서 테트리스 개발을 시작했었다.
친구가 예전에 나에게 해주었던 말과 저번에 유튜브에 출연하면서 뵙게 된 온리원 게임즈 대표님이 해주신 말이 머릿속을 지나가면서였다.

"하나라도 완성된 프로젝트가 있다면 취업할때 되게 큰 가산점이 붙는다"

난 나를 돌아보았다. 내 딴에는 완성이라 만든 작품 (나중에 기회가 되면 소개하겠다.)들이 있었지만 역시 이정도로는 부족한 것 같았다. 이대로 있어선 안 되겠다는 생각이 들어 무작정 이 프로젝트를 시작하게 되었다. 사실 시도는 예전에도 했었지만 그때는 다른 일들에 치여 흐지부지 되었었다. 지금처럼 시간이 비교적 널널할 때 해둬야 한다고 생각해서 일단 컴파일러를 켰다.

제일 먼저 게임의 시작화면과 게임 (룰은 뿌요뿌요 테트리스의 룰을 사용), 그리고 일시정지 화면의 구현을 하기로 했다.
게임 플레이는 배경음악이 존재하는 시작화면에서 아무 버튼이나 누르면 바로 게임 플레이가 시작되고, 왼쪽 방향키를 누르면 반시계 방향으로 90도, 오른쪽 방향키를 누르면 시계방향으로 90도를 회전하도록 하고, esc 키를 누르면 일시정지 되며 여유가 된다면 아래쪽 방향키를 누르면 블록이 더 빨리 내려오도록 하게 할 것이다.

가장 먼저 구현한 것은 테트리미노이다. 테트리미노는 총 I, O, J, L, S, Z, T 형태로 총 7개의 종류가 있다.

위 이미지와 같은 형태로 4 * 4 좌표평면 안에서
왼쪽 제일 하단좌표를 (0, 0)이라 했을때 (3, 3)을 회전축으로 두고 회전한다.

나는 2차원배열에 블록이 존재하는 좌표는 1, 그렇지 않은 좌표는 0의 값을 저장하였다.
이때, 전역변수로

int I90[4][4] =  //90도 회전했다는 뜻
	{0, 0, 0, 0},
	{1, 1, 1, 1},
	{0, 0, 0, 0},
	{0, 0, 0, 0}

의 형태로 저장했었다.

하지만 이러한 형태의 문제점은 후에 회전, 블록의 전환 등을 구현할 때 변수이름을 불러오는 데에 어려움을 겪을 수 있어 더욱 간편하게 회전을 위한 4가지 각도 (0', 90', 180', 270')를 위한 4칸짜리 차원 하나와 7가지 블록의 종류를 구현하기 위해 7칸짜리 차원 하나를 더 해 일단은 4차원 배열이 되었다. 후에 개발하며 편한 방향으로 바뀔 수 있다.

현재는

int block[7][4][4][4] = 
{	
	//I0
	{	
		{ //0도
			{0, 0, 0, 0},
			{1, 1, 1, 1},
			{0, 0, 0, 0},
			{0, 0, 0, 0}
		},
		
		{ //90도
			{0, 0, 1, 0},
			{0, 0, 1, 0},
			{0, 0, 1, 0},
			{0, 0, 1, 0}
		},
	
		{ //180도
			{0, 0, 0, 0},
			{1, 1, 1, 1},
			{0, 0, 0, 0},
			{0, 0, 0, 0}
		},
	
		{ //270도
			{0, 0, 1, 0},
			{0, 0, 1, 0},
			{0, 0, 1, 0},
			{0, 0, 1, 0}
		}
	},
    	.
    	.
    	.
 .

와 같은 형태로 모든 블록을 구현해두었다.

다음으로 구현한 것은 게임 전체화면의 테두리와 실제 게임이 이루어지는 영역의 테두리이다. 먼저, 테트리스가 진행되는 영역의 크기는 10 x 20 (뿌요뿌요테트리스 기준)칸 이다. 블록을 그릴 텍스트는 ▣, 테두리를 그릴 텍스트는 ◈ 를 사용하기로 하였다. 두 텍스트 모두 스페이스 두 칸 만큼의 공간을 차지하며 이 때문에 x좌표를 계산할 때 두 칸씩 계산해야 한다. 내 노트북 화면 크기에 대충 맞고 게임을 제작하는데에 방해가 되지 않는 게임화면의 크기를 잡아보니 160 x 40이 되었다.

system("mode con cols=160 lines=40");

를 사용하여 화면 크기를 지정해주었다. 먼저 헤더에 windows.h를 선언해야한다. 이는 api 함수 이기 때문인데 내가 아직 api까지 공부를 하지 못 하여 자세하게 설명하지는 못 한다.

이제 게임 화면 테두리를 그릴 차례다.
gotoxy(a, b) 함수는 (a, b)좌표로 커서가 이동하게 한다. 이 함수를 사용할 때에는

gotoxy(int x, int y)
{   
      COORD pos = {x, y};
      SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

이 함수를 헤더 밑에 선언해주어야 하는데, 이유는 아까와 동일한 이유로 모른다. 어쨌든 해야 한다라고만 알고 있으면 현 상황에선 문제는 없다.

void scrline(void)
{
	int i = 0;
	
	for(i = 0; i < 80; i++)		//첫 줄의 세로좌표 끝까지 그리기
		printf("◈");
		
	for(i = 0; i < 39; i++)		//두 번째 줄 부터 마지막에서 두 번째 줄 까지는 가장 왼쪽과 가장 오른쪽에만 그리기
	{
		gotoxy(0, i + 1);
		printf("◈");
		gotoxy(158, i);
		printf("◈");
	}
	
	gotoxy(0, 39);		//마지막 줄로 이동
	
	for(i = 0; i < 80; i++)		//마지막 줄의 세로좌표 끝까지 그리기
		printf("◈");
		
	return;
}

위와 비슷한 형식으로 테트리스가 진행되는 영역의 테두리는 대충 (10, 10)부터가 적절해보였다. 이때, 좌표상으로는 20 x 20이 되어야 한다. (블록의 크기가 공백 두 개 차지!)
그리고 테두리는 영역의 좌표에서 제외해야 하므로 10, 10 에서 가로 세로로 한 블록 씩 떨어진 (8, 9)부터 그리기로 하였다.

void gameline(void)
{
	int i = 0;
	
	gotoxy(8, 9);
	
	for(i = 0; i <12 ; i++)		//테두리 포함 12블록
		printf("◈");
		
	for(i = 0; i < 20; i++)	
	{
		gotoxy(8, i + 10);
		printf("◈");
		gotoxy(30, i + 10);
		printf("◈");
	}
	
	gotoxy(8, 30);
	
	for(i = 0; i <12 ; i++)
		printf("◈");
		
	return;
}

이렇게 테두리를 그리는 두 함수를 만들었다. 이를 메인 함수 안에 넣고 실행시키면

이러한 화면이 생성된다. 게임이 이루어지는 영역이 상대적으로 많이 작아 보이지만 어쩔 수 없다. 후에 블록에 색깔을 입히는 방법으로 가독성을 높여야겠다.

profile
사고의 흐름을 따라가 보자.

0개의 댓글