때는 2022.12.29일 목요일
바킹독 선생님의 실전 알고리즘 강의 영상을 보던 중
C++ 공부 중인 해온이의 대결 신청
나도 C++ 익힐 겸 하기로 했다.
오랜만에 하루종일 앉아서 코드만 짠 것 같다.
이런 기분을 중학생 때 게임 만들 때 느꼈었는데, 고등학교 와서는 못 느꼈다.
노래 반복재생 - 코드짜기 - 버그 발생 - 노래 멈추고 디버깅 - 다시 노래 재생
시간은 대략 29일 하루종일 + 30일 반나절 정도 걸렸다.
클래스는 다음과 같이 나눴다
Tetromino / Tetris / TetrisUI / Display
TetrisUI와 Display는 static method로 구성되어있고, TetrisUI는 미노 정보만 받아서 Display 내 함수를 호출하는 역할,
Display 는 테트리스와 분리되어서 화면 출력하는 역할을 맡도록 했다.
Tetromino 클래스는 각 미노의 좌표들을 가지고 있다. 각 미노의 중심 블록으로부터 상대적인 나머지 블록들의 대한 좌표를 담은 points 벡터를 계속 불러와 화면에 그려줄 수 있게 하였다.
꽤 오랜 고민을 했던 부분이 미노의 회전, 움직임, 드랍에대한 처리였는데,
1. 입력
2. 회전/움직임/드랍 처리 (실제로 움직이지 않고 움직였을 때의 정보 처리)
3. 검사
4. Ok or No
2번에서 Rotate 함수를 호출하는데 실제로 Rotate 되어선 안된다.
근데 또 4번에서 Ok 되었을 때 Rotate 함수를 호출하지 않고 setRotation으로 회전시켜버린다.
이부분은 개인적으로 수정해야할 부분인 것 같다.
void Tetris::StartGame() {
while (true) {
Init();
TetrisUI::DrawMap(width, height);
int fallTick = 0;
while (true) {
if (!isFalling) {
minoIndex = ChooseMino();
TetrisUI::NextUpdate(minos, minoQueue);
UpdateMino({ 0,0 }, 0);
isFalling = true;
}
Command cmd = EXIT;
Point dp = { 0,0 };
bool isMoved = false; bool isRotated = false; bool isDropped = false; int rot = minos[minoIndex]->getRotation();
if (fallTick == 20) {
dp.y += 1;
fallTick = 0;
if (!IsPlacable(dp,rot)) {
dp.y -= 1;
cmd = DROP;
}
Tetris::UpdateMino(dp, rot);
}
if(cmd == EXIT)
cmd = Input();
switch (cmd) {
case LEFT:
dp.x -= 1; isMoved = true;
break;
case RIGHT:
dp.x += 1; isMoved = true;
break;
case DOWN:
dp.y += 1; isMoved = true;
break;
case R_ROTATE:
case L_ROTATE:
if (minoIndex == 6) break;
if (minoIndex >= 0 && minoIndex <= 4) {
if (rot == 1)
dp.x += 5 - cmd;
else if (rot == 0)
dp.x += cmd - 5;
}
rot = minos[minoIndex]->Rotate(cmd | FAKE_ROTATE); isRotated = true;
break;
case DROP:
dp.y = CalculateDropPos().y - minos[minoIndex]->centerPos.y; isDropped = true;
break;
case HOLD:
if (canHold) {
UpdateMino(dp, rot, UPDATE_ONLY_ERASE);
minos[minoIndex]->centerPos = DEFAULT_CENTER;
if (holdIndex != -1) {
int tmp = minoIndex;
minoIndex = holdIndex;
holdIndex = tmp;
}
else {
holdIndex = minoIndex;
minoIndex = ChooseMino();
TetrisUI::NextUpdate(minos, minoQueue);
}
TetrisUI::HoldUpdate(minos[holdIndex]);
//isFalling = false;
isMoved = true;
canHold = false;
}
break;
default:
break;
}
if (isMoved || isRotated) {
if (!IsPlacable(dp, rot))
goto SKIP;
Tetris::UpdateMino(dp, rot);
}
if (isDropped) {
if (CheckGameOver(dp))
break;
Tetris::UpdateMino(dp, rot);
Tetris::LineClear(); // clear line if line was filled
TetrisUI::PrintScore(score);
minos[minoIndex]->Init();
isFalling = false;
canHold = true;
}
SKIP:
fallTick += 1;
Sleep(tickSpeed);
}
TetrisUI::RetryMenu(score);
if (_getch() == 'R')
break;
}
}
핵심은 위에서 말했듯 입력 받고 변화하는 정도를 저장한다.( move면 dp 변경, rotate면 rotation 저장)
이후 isMoved 거나 isRotated 거나 isDropped 이면 검사를 하는데, IsPlacable 함수에 dp 와 rot 정보를 넘겨준다.
placable 하면 UpdateMino 함수를 호출하여 진짜로 움직이게 한다.
UpdateMino 에서는 현재 미노를 지우고, 움직이거나, 회전하거나, 드랍한 후에 다시 그린다.
흠
여기까지.