본문 바로가기

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

1-5. C/C++ Console 테트리스를 만들어보자!!(Class : StackedBlockState)

반응형

어쩌면 게임의 진행과 가장 큰 연관성이 높은 클래스라고도 할 수 있겠습니다.

클래스의 역할은 현재 쌓인 블록 상태의 업데이트, 바운더리 추출, 라인 삭제, 게임 오버 판단이 있습니다. 

클래스의 구조는 다음과 같습니다.

 

class StackedBlockState {
public:
	blockMemory stackedBlockState[GAMETEMPLATE_Y + GAMETEMPLATE_MARGIN_TYPE_1]
		[GAMETEMPLATE_X + 2 * GAMETEMPLATE_MARGIN_TYPE_1];
	list<dotPos> collisionBoundary;
	StackedBlockState() {
		collisionBoundary = list<dotPos>();

		for (int i = 0; i < GAMETEMPLATE_Y + GAMETEMPLATE_MARGIN_TYPE_1; i++) {
			for (int j = 0; j < GAMETEMPLATE_X + 2 * GAMETEMPLATE_MARGIN_TYPE_1; j++) {
				stackedBlockState[i][j] = {FIRSTBLOCKSTATE[i][j], GREEN};
			}
		}
	}
	int updateStackedBlockLine();
	void updateStackedBlock(dotPos* updatedData);
	void updateStackedTetrisBlock(TetrisBlock* tetrisBlock);
	void updateCollisionBoundary();
	void printOutFinalChangedState();
	bool checkWhetherGameOver();
};

 

메서드에 대한 설명은 다음과 같습니다.

  1. void updateStackedBlock(dotPos* updatedData) : 구성 블록의 위치가 확정된 후, 현재 게임 상황에 반영
  2. void updateStackedTetrisBlock(TetrisBlock* tetrisBlock) : 테트리스 블록의 위치가 설정된 후, 현재 게임 상황에 반영
  3. void updateCollisionBoundary() : 충돌 경계 업데이트
  4. void printOutFinalChangedState() : 게임 현재 상황을 화면상 출력
  5. int updateStackedBlockLine() : 지워진 줄의 개수 반환 및 변화된 현재 상황 반영
  6. bool checkWhetherGameOver() : 게임 오버 여부 판단

 

테트리스 블록이 완전히 떨어져서 확정이 되면 현재 게임상황에 이를 반영해야 합니다. 이때 사용하는 메서드가 updateStackedTetrisBlock(TetrisBlock* tetrisBlock) 입니다. 코드는 다음과 같습니다.

 

void StackedBlockState::updateStackedTetrisBlock(TetrisBlock* tetrisBlock) {
	for (int i = 0; i < 4; i++) {
		dotPos buffer = tetrisBlock->getTetrisBlockNowState(i);
		if (buffer.whetherStacked == true) 
			stackedBlockState[buffer.posY - TEMPLATESTARTPOINT_Y][buffer.posX - TEMPLATESTARTPOINT_X] 
			= {BLOCK, tetrisBlockTypeNColor[tetrisBlock->getTetrisBlockType()].color};
	}
}

 

저장해야 될 것은 블록의 위치(일종의 게임 스크린처럼 저장됩니다.), 색입니다.

 

다음은 바운더리 추출에 대한 것입니다. 경계에 대해서 정의를 해봅시다. 저는 경계에 대해서 적어도 하나의 면이 외부로 노출되어 있는 경우에 대해서 경계 블록이라고 정의하였습니다. 그리고 이를 코드 상으로 구현하였습니다. 코드는 다음과 같습니다.

 

void StackedBlockState::updateCollisionBoundary() {
	while (collisionBoundary.size() != 0)
		collisionBoundary.pop_back();

	for (int j = 0; j < GAMETEMPLATE_Y + GAMETEMPLATE_MARGIN_TYPE_1; j++) {
		for (int i = 0; i < GAMETEMPLATE_X + 2*GAMETEMPLATE_MARGIN_TYPE_1; i++) {
			dotPos buffer = {i+TEMPLATESTARTPOINT_X, j + TEMPLATESTARTPOINT_Y, true};
			collisionBoundary.push_back(buffer);
			if (stackedBlockState[j][i].blockORBlank != BLOCK) {
				collisionBoundary.pop_back();
			}
			else {
				bool whetherXLowerThan0 = (i-1)<0 ? true : false;
				bool whetherYLowerThan0 = (j-1)<0 ? true : false;
				bool whetherXHigherThanMax = (i+1)>11 ? true : false;
				bool whetherYHigherThanMax = (j+1)>20 ? true : false;

				if (whetherXLowerThan0) {
					if (whetherYLowerThan0) {
						if (stackedBlockState[j+1][i].blockORBlank==BLOCK) {
							if (stackedBlockState[j][i+1].blockORBlank == BLOCK) {
								collisionBoundary.pop_back();
							}
						}
					}
					else if (whetherYHigherThanMax) {
						if (stackedBlockState[j-1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i+1].blockORBlank == BLOCK) {
								collisionBoundary.pop_back();
							}
						}
					}
					else {
						if (stackedBlockState[j+1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i+1].blockORBlank == BLOCK) {
								if (stackedBlockState[j-1][i].blockORBlank == BLOCK) {
									collisionBoundary.pop_back();
								}
							}
						}
					}
				}

				else if(whetherXHigherThanMax) {
					if (whetherYLowerThan0) {
						if (stackedBlockState[j+1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i-1].blockORBlank == BLOCK) {
								collisionBoundary.pop_back();
							}
						}
					}
					else if (whetherYHigherThanMax) {
						if (stackedBlockState[j-1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i-1].blockORBlank == BLOCK) {
								collisionBoundary.pop_back();
							}
						}
					}
					else {
						if (stackedBlockState[j+1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i-1].blockORBlank == BLOCK) {
								if (stackedBlockState[j-1][i].blockORBlank == BLOCK) {
									collisionBoundary.pop_back();
								}
							}
						}
					}
				}

				else {
					if (whetherYLowerThan0) {
						if (stackedBlockState[j+1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i+1].blockORBlank == BLOCK) {
								if (stackedBlockState[j][i-1].blockORBlank == BLOCK) {
									collisionBoundary.pop_back();
								}
							}
						}
					}
					else if (whetherYHigherThanMax) {
						if (stackedBlockState[j-1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j][i+1].blockORBlank == BLOCK) {
								if (stackedBlockState[j][i-1].blockORBlank == BLOCK) {
									collisionBoundary.pop_back();
								}
							}
						}
					}
					else {
						if (stackedBlockState[j+1][i].blockORBlank == BLOCK) {
							if (stackedBlockState[j-1][i].blockORBlank == BLOCK) {
								if (stackedBlockState[j][i+1].blockORBlank == BLOCK) {
									if (stackedBlockState[j][i-1].blockORBlank == BLOCK) {
										collisionBoundary.pop_back();
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

 

코드 내용을 간단하게 설명하자면 list를 만드는데 각 칸에 대해서 우선 넣고 주변이 완전히 막혀 있다면 pop_back()을 해서 빼내느 형식으로 구성하였습니다. 단 경계부분(맨 위쪽, 아래쪽, 왼쪽, 오른쪽)의 경우 비교에 있어서 예외가 생겨서 코드가 길어지게 되었습니다. 한 번 읽어보시기를 추천드립니다.

 

다음으로는 라인을 지우는 함수에 대해서 설명해드리겠습니다. 간단하게 한 줄이 가로로 채워졌으면 이를 지웁니다. 그리고 위에서 부터 빈 라인을 기준으로 그 위 부터 한 칸씩 내리는 작업을 반복하여 주었습니다. 마지막으로 지워진 줄의 개수를 반환하는 것으로 매서드가 마무리됩니다. 직접 코드를 한 번 읽어보시는 것을 추천드립니다.

 

int StackedBlockState::updateStackedBlockLine() {
	int line = 0;
	bool whetherStacked[20] = { false, };

	for (int j = 0; j < GAMETEMPLATE_Y; j++) {
		for (int i = 1; i < GAMETEMPLATE_X+GAMETEMPLATE_MARGIN_TYPE_1; i++) {
			if (stackedBlockState[j][i].blockORBlank == BLANK) {
				whetherStacked[j] = false;
				break;
			}
			whetherStacked[j] = true;
			line++;
		}
	}

	for (int i = 0; i < 20; i++) {
		if (whetherStacked[i] == true) {
			for (int j = 1; j < 11; j++) {
				stackedBlockState[i][j] = {BLANK, GREEN};
			}
		}
	}

	for (int i = 0; i < 20; i++) {
		if (whetherStacked[i] == true) {
			for (int j = i - 1; j >= 0; j--) {
				for (int k = 1; k < GAMETEMPLATE_X + GAMETEMPLATE_MARGIN_TYPE_1; k++) {
					stackedBlockState[j + 1][k] = stackedBlockState[j][k];
				}
			}
			for (int k = 1; k < GAMETEMPLATE_X + GAMETEMPLATE_MARGIN_TYPE_1; k++)
				stackedBlockState[0][k] = {BLANK, GREEN};
		}
	}
	return line;
}

 

이렇게 주요 클래스, 게임 시스템의 구성 등 프로젝트 전반에 대한 개략적인 설명을 마쳤습니다. 

글을 쓰면서 코드를 다시 읽어보는 데, 솔직히 많은 부분에 있어서 고칠 부분들이 보이네요..(코드가 좀 더 단순하고, 심플해질 수 있을텐데...ㅠㅠ)

글을 쓰는 것도 미루다보니 첫 글하고 마지막 글 간격이 장난 아니네요 ㅋㅋ

앞으로는 뭘 배우거나 아니면 만들때 일기처럼 쓰는 것이 맞다는 생각이 드네요 ㅋㅋㅋ

어쨌든 첫 번째 프로젝트가 끝났습니다. 혹시라도 따라서 만들어 보실 분은 소스코드를 한 번은 훓어보는 것이 좋을 것 같네요.. 스파게티여서 그렇게 해야 이해가 되실 거에요 ㅠㅠ

이 것으로 글을 마칩니다. 감사합니다!!!!!!!!

 

반응형