본문 바로가기

뭐라도 만들어보자!!( 프로젝트 )/C, C++ 프로젝트

1-2 C/C++ Console 테트리스를 만들어보자!!(전체 구조 편)

반응형

사실 제일 중요한 파트라고 생각합니다. 

만약 제가 했던 방법대로 한 번 짜볼 생각이라면 이 번 파트에 조금 더 집중하는 것이 좋을 것입니다.

 

우선 테트리스가 어떻게 게임인지 이해할 필요가 있습니다.

 

테트리스의 게임 프로세스를 생각해보면 다음과 같습니다.

 

1. 블록이 생성된다.

2. 블록은 완전히 상태가 확정되기 전까지(추락하지 못하는 상태) 일정 속도로 떨어진다.

3. 블록이 떨어지기 전까지 사용자는 블록을 좌, 우, 아래 방향으로 이동시키거나 회전시킬 수 있다.

4. 단 이미 쌓여진 블록이나 경계에 이동, 회전 경로가 겹치면 조작이 불가능하다.

5. 블록이 완전히 떨어지게 되면 화면이 업데이트 된다.

6. 한 줄이 완전히 채워진 경우 그 줄은 지워지고, 지워진 줄 위에 쌓였던 블록들은 떨어진다. 그리고 점수가 올라간다.

7. 만약 블록들이 쌓여 맨 위줄에 닿게되면 그대로 게임이 오버된다.

 

테트리스를 구연하기 위해서는 이 과정을 구현해야 합니다. 그러나 당연하지만 구현만으로는 원하는 품질의 게임을 만들어내지는 못할 것입니다. 생각해 보십시오 매 과정마다 전체 게임화면을 그려낸다면 사용자의 입장에서는 깜박깜박 거리는 것처럼 느껴질 것입니다. 또한 내부 거쳐야 할 과정이 많아져 사용자가 느낄 정도로 진행이 느려진다면 그 것대로 문제가 될 것입니다. 그렇다면 우리의 목표는 다음과 같습니다.

 

"앞서 설명했던 테트리스 로직을 최소한의 출력과 작업으로 구현해야 한다."  

 

저는 위의 7 가지 단계를 3 가지의 클래스를 정의하여 해결하였습니다.

 

1. Block : 충돌 처리, 사용자 조작에 따른 위치 변경, 기본적인 낙하, 상태 확정 등 블록의 이동과 출력을 담당하는 클래스

 

2. TetrisBlock : 실제 4 개의 Block Class의 인스턴스를 생성한 후, 이들의 충돌처리, 조작에 따른 위치 변경, 회전 등 4 개의 블로에 대한 이동과 출력을 담당하는 클래스

 

3. StackedBlockState : 게임의 진행 상황을 저장 및 관리하는 클래스, 확정되 상태에 대한 업데이트, 충돌 경계 추출 및 저장 등에 관여합니다.

 

테트리스 블록이 떨어질 때는 블록만을 출력하다가(이렇게 하면 화면 상에서 8번만 바꾸면 됩니다.)

블록이 완전히 떨어지면 현재 상태를 게임 상황에 반영하고 게임 화면을 출력하는 방식을 썼습니다. 

 

여기까지는 이해를 하셨을 것이라 생각합니다. 다만 충돌 경계에 대해서 의문이 드셨을 것입니다. 충돌 경계는 블록의 충돌 처리를 보다 효율적으로 처리하기 위하여 추출한 것입니다. 충돌이란 결국 인접 방향에 쌓인 블록인 것이 있는지를 확인하는 것입니다. 우리는 경험적으로 이런 블록들이 경계 방향에 있다는 것을 알 수 있고, 이는 굳이 쌓인 블록 모두를 검증할 필요가 없다는 것을 의미합니다. 충돌 경계는 이러한 면에서 연산량을 줄여주는 역할을 합니다. 

 

코드로 보면 다음과 같이 구성됩니다.

 

// 주요 프로세스(반복되는 것)만을 따온 것입니다!!

while (1) {
	// TetrisBlock의 생성과 출력
		nowBlock = nextBlock;
		nextBlock = whichBlockType();
		chosenBlock = tetrisBlockTypeNColor[nowBlock];
		printOutBlockShow(tetrisBlockTypeNColor[nextBlock].blockType, 
			tetrisBlockTypeNColor[nextBlock].color);
		TetrisBlock* tetrisBlock = new TetrisBlock(TETRISBLOCKGENERATEPOINT_X, 
			TETRISBLOCKGENERATEPOINT_Y, chosenBlock.blockType);
		tetrisBlock->tetrisBlockPrintOut(chosenBlock.color);
	//사용자의 조작 및 기본 낙하
		while (!(tetrisBlock->getTetrisBlockNowState(0).whetherStacked)) {

			tetrisBlock->tetrisBlockDefaultFall(STATE->collisionBoundary);
			tetrisBlock->tetrisBlockPrintOut(chosenBlock.color);

			tetrisBlock->tetrisBlockRotate(STATE->collisionBoundary);
			tetrisBlock->tetrisBlockPrintOut(chosenBlock.color);

			tetrisBlock->tetrisBlockPlayerControllLeft(STATE->collisionBoundary);
			tetrisBlock->tetrisBlockPrintOut(chosenBlock.color);

			tetrisBlock->tetrisBlockPlayerControllRight(STATE->collisionBoundary);
			tetrisBlock->tetrisBlockPrintOut(chosenBlock.color);

			tetrisBlock->tetrisBlockPlayerControllDown(STATE->collisionBoundary);
			tetrisBlock->tetrisBlockPrintOut(chosenBlock.color);

			if (tetrisBlock->getTetrisBlockNowState(0).whetherStacked == true) {
				Sleep(100);
			}
			else {
				Sleep(500);
			}
		}
        //게임 상황 업데이트 및 라인 지우기, 게임오버 판단
		STATE->updateStackedTetrisBlock(tetrisBlock);
		LINE += STATE->updateStackedBlockLine();
		STATE->updateCollisionBoundary();
		delete tetrisBlock;
		STATE->printOutFinalChangedState();
		if (STATE->checkWhetherGameOver()) {
			delete STATE;
			break;
		}
	}

 

  다음 글 부터는 3 개의 클래스를 하나씩 보면서 주요 로직에 대해서 설명할려고 합니다!!

반응형