본문 바로가기
ProgramingLagnuage/C

[c언어] - 테트리스 게임 설계

by TSpoons 2024. 8. 30.

군대를 다녀오고 c언어를 가물가물해서
c언어를 복습할 겸 테트리스 게임을 만들고, 최적화 시켜보려고 프로젝트를 진행한다. 
 
 
 
 
 
 
 
main.c: 프로그램의 진입점이 되는 메인 함수가 있는 파일.
tetris.c: 게임의 핵심 로직을 구현하는 파일.
tetris.h: 필요한 함수와 변수들을 선언하는 헤더 파일.
(추가 사항) graphics.h 및 graphics.c : 그래픽 관련 기능이 필요한 경우 사용
 

 
 
1. 테트리스 GUI

 
2. 블록 정의 (회전축을 어디로 잡아야 할까..?)

https://sbrngm.tistory.com/143


2-1. L 
{{0, 0, 0, 0}, {1, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}}

    
1   
111 
    
    
  1 
  1 
 11 
    
 111
   1
    
 11 
 1  
 1  
    

2-2. J

    
   1
 111
    
 11 
  1 
  1 
    
    
111 
1   
    
    
 1  
 1  
 11 

 
2-3 T

    
 1  
11●1 
    
    
 1  
11●  
 1  
    
    
11●1 
 1  
    
 1  
 1●1 
 1  

 
2-4 I

 1  
 1  
 1  
 1  
    
    
1111
    

 
2-5 O

    
 11 
 11 
    

 
2-6 Z

    
11●  
 11 
    
  1 
 1 ●1 
 1  
    

 
2-7 S

    
 1●1 
11  
    
 1  
 1●1 
  1 
    

 


3. 블록 생성 + GUI
 

issue 1

블록 생성 후 맵과 grid 혼합
=> 한 칸 씩 내려가지를 않음
block이 쌓인다..

int moveBlockDown(Block *block) {
    block->y++;
    // 블록이 보드의 경계를 넘거나 다른 블록과 충돌하는지 확인
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (blockModel[block->type * 4 + block->rotation][i][j] && (block->y + i >= HEIGHT || grid[block->y + i][block->x + j] != ' ')) {
                block->y--; // 이전 위치로 복구
                placeBlock(block); // 복구된 위치에 블록 배치
                return 0; // 이동 실패 (블록이 바닥에 닿음)
            }
        }
    }
    placeBlock(block);
    displayMap();
    return 1; // 이동 성공
}

 
이전 위치에 있는 블럭을 삭제하고 다음 위치로 이동한 후 그 다음 grid를 업데이트해야지 아니면 잔상이 계속 남게 된다.
 

issue 2 

블럭을 그냥 뭉개버린다.
=> 충돌 시 처리하는 코드가 필요하다.

 
issue 3 

블럭이 충돌하면 해당 모양도 아닌데 grid에 생성된다.
 
- 자세히 보면 블럭이 한칸씩 밀려서 grid와 합성되고 있다.
 
즉, 한 칸 밀려서 합성되고 있던 것이였다.
 

 
 
--> 디버깅할 파일을 하나 만들어야 겠다. 

#include <stdio.h>
#include <windows.h>

FILE* debugFile;

void openDebugFile() {
    debugFile = fopen("debug_log.txt", "w");  // 디버그 파일 열기
    if (!debugFile) {
        printf("Failed to open debug file.\n");
        exit(1);
    }
}

void closeDebugFile() {
    if (debugFile) {
        fclose(debugFile);  // 디버그 파일 닫기
    }
}

int main() {
    Block currentBlock;

    openDebugFile();  // 디버그 파일 열기
    
    initializeGame();
    generateNewBlock(&currentBlock);

    while (!isGameOver()) {
        clearScreen();
        displayMap();

        if (_kbhit()) {
            handleInput(&currentBlock);
        }

        if (!moveBlockDown(&currentBlock)) {
            generateNewBlock(&currentBlock);
        }
        printBlockShape(&currentBlock);

        // 디버그 파일에 출력
        fprintf(debugFile, "Block position: (%d, %d)\n", currentBlock.x, currentBlock.y, currentBlock.shape);
        fflush(debugFile);  // 출력 내용을 파일에 즉시 기록

        Sleep(200);
    }
}

 

 
.으로 표시되어있는 곳이 20*40의 좌표다.
현재 문제는 블록(8,37)의 위치를 인지함에도
블록(8,37) 블록(8,38)을 or연산되어 같이 grid에 표시된다는 점이다.
 
 

// 충돌 체크
int checkCollision(Block *block) {
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (block->shape[i][j]) {
                int y = block->y + i;
                int x = block->x + j;
                if (x < 0 || x >= WIDTH || y >= HEIGHT || (y >= 0 && grid[y][x])) {
                    // fixBlock(block); 
                    return 1; // 충돌 발생
                    
                }
            }
        }
    }
    return 0; // 충돌 없음
}

 
원인은,, fixBlock 함수에서 블럭을 하나 더 생성해서 그렇게 되었다..
fixBlock은 기존 설계할 떄 합성 후 grid 생성하는 함수 였었는데 이중 선언이 되버린 꼴이였다.
 
 
 
 

블록 회전

 issue 1

블록 회전 시 L자블럭이 Z로 변경된다.
 
처음에는 메모리 관리가 안되서 이상한 값이 들어갔나 싶었는데
그저 코드 실수,, 머쓱..
 
BEFORE

// 블록 생성
void generateNewBlock(Block *block) {
    block->x = WIDTH / 2 - 2;  // 중간 위치에서 블록 생성
    block->y = 1;              // 최상단에서 블록 생성
    block->type = rand() % 7;  // 블록 종류를 랜덤으로 선택
    block->rotation = 0;       // 기본 회전 상태

    memcpy(block->shape, blockModel[block->type*4*block->rotation], sizeof(block->shape));

    placeBlock(block);         // 블록을 보드에 배치
    
}

AFTER

// 블록 생성
void generateNewBlock(Block *block) {
    block->x = WIDTH / 2 - 2;  // 중간 위치에서 블록 생성
    block->y = 1;              // 최상단에서 블록 생성
    block->type = rand() % 7;  // 블록 종류를 랜덤으로 선택
    block->rotation = 0;       // 기본 회전 상태

    memcpy(block->shape, blockModel[block->type*4+block->rotation], sizeof(block->shape));

    placeBlock(block);         // 블록을 보드에 배치
    
}

 
 
중간 기능 영상

 https://youtu.be/UIBjQURfSMY


 
앞으로 해야 할 일
 
1. 기능리스트 작성
- 다음 블럭 표시 
- 점수 표시 
- 게임시작 창 추가
- push 버튼 같은 요소들을 추가하여 1인, 2인용 선택
 
2. 디자인적인 요소 개선
 
 
3. 네트워크 관리