XR 플밍 - 1. 프로그램 언어 기초 (7) - 콘솔 프로젝트 2 - 미로게임(자작) (3/19)

이형원·2025년 3월 19일
0

XR플밍

목록 보기
15/215

콘솔 프로젝트 1에서 미로 게임을 간단하게 제작해 보았다. 미로 게임이나 소코반을 만들 때에도 상당히 많은 과정이 필요했고, 리팩토링이란 과정에 대해서도 배웠다.
* 리펙토링 : 소프트웨어의 코드 구조를 변경하는 과정 - 코드 정리 및 가독성 높이는 작업

이제 이와 같이 배워보는 과정을 거쳐 직접 게임을 만들어 보는 시간을 가져 보고자 한다.

1. 어떤 게임을 만들 것인가?

우선은 게임 제작에 어떤 기능을 중점적으로 구현할 지 생각해 보았다.
지금껏 만든 게임이 미로 게임과 소코반이 있었지만, 개인적으로는 남들이 생각지도 못한 독창적인 아이디어를 생각해 보고 싶었다. (뭘 만들어도 어딘가에는 구현한 사람이 있겠지만)
다만 내가 중점적으로 구현해 보겠자고 생각한 아이디어가 하나 있었다.

미로 게임을 만들 것이다. 다만 난이도를 높이기 위해서 시야 제한을 두겠다.
(미로의 모든 구조가 출력되지 않고, 플레이어 주변의 벽만 확인할 수 있는 정도로 출력)

이런 걸 구현할 수 있을지 처음에는 감이 잡히지 않았지만, 차근차근 코드를 짜 보며 게임을 구현한 과정을 정리해보고자 한다.

2. 맵을 일부만 출력해 보자

또 다시 게임의 구성 단계를 가져왔다.

bool gameOver = false;

Start();	// 시작

while(gameOver == false)	// 게임 진행동안
{
    Render();	// 출력
    Input();		// 입력
    Update();	// 처리
}

End();		// 종료

우선은 필요한 기능부터 생각해 보자

  1. 플레이어를 구현하고 출력하며, 플레이어를 움직일 수 있도록 할 것
  2. 맵을 구성하고, 플레이어가 벽을 뚫지 못하게 할 것
  3. 구성한 미로에서 플레이어 주변의 인접한 1칸(8방향)의 맵만 출력하도록 할 것.

이렇게 조건을 생각해 두고 코드를 작성해 보자.
다만 1과 2의 과정은 저번 프로젝트와 중복되는 부분이므로 코드 작성 부분은 생략하고,
게임을 만들면서 생겼던 문제와 해결 방법 중심으로 서술하고자 한다.

우선 첫 번째로 느낀 문제점에 대해 서술해 보고자 한다.

1) 플레이어가 이동할 때, 그 출력했던 동선이 지워지지 않았다.

콘솔 프로젝트 1을 진행할 때에는 실시간으로 지웠다가 다시 쓰는 방식인 Console.Clear();
대신, Console.SetCursorPosition(0, 0)으로 설정하고 덧그리는 방식으로 지운 것처럼 구현했다.
하지만 이번에 구현하는, 실시간으로 맵의 출력이 달라지는 부분 때문인지, 이 방법이 먹히지 않았다.
아무래도 Console.SetCursorPosition으로 맵을 덧그리는 방식으로 만들다 보면, 이번과 같은 방식으로 게임을 제작할 때 제대로 지워지지 않는 현상을 경험할 수 있었다.

  • 플레이어의 동선의 일부는 물론, 맵도 지워지지 않는 현상이 보였다.
    맵이 전부 출력되는 상황에서는 덧그려도 문제점이 크게 드러나지 않았지만 맵 출력이 실시간으로 변하는 상황에서 리스크를 두지 않기로 했다. 그래서 Console.Clear();로 맵을 실시간으로 지워줬다 출력하는 방식을 채택했다.

2) 맵을 일부만 출력하게 코드를 작성해 보자

맵은 미로 찾기의 버전처럼, true와 false로 출력하기 위해 작성하였다.

map = new bool[10, 15]      // 전체 맵 (벽 표시)
{
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
    {false, true, false, true, true, true, true, true, true, true, true, true, true, true, false },
    {false, true, false, true, false, false, false, true, false, false, false, true, false, true, false },
    {false, true, false, true, true, true, false, true, false, true, false, false, false, true, false },
    {false, true, true, true, false, false, false, false, false, true, true, true, true, true, false },
    {false, false, false, true, false, true, true, true, true, true, false, false, false, false, false },
    {false, true, true, true, false, false, false, true, false, false, false, true, true, true, false },
    {false, true, false, false, false, true, true, true, true, true, false, true, false, true, false },
    {false, true, true, true, false, true, false, true, false, true, true, true, false, true, false },
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }
};

(엄청난 노가다였다...)
이와 같이 맵을 작성하는 과정은 똑같이 하고, 맵을 출력하는 과정에서 변화를 두어야 한다.
이전에 맵 전체를 출력했을 때의 경우를 생각해 보자.

for (int y = 0; y < map.GetLength(0); y++)
{
    for (int x = 0; x < map.GetLength(1); x++)
    {
        if (map[y, x] == false)
        {
            Console.Write("#");
        }
        else
        {
            Console.Write(" ");
        }
    }
    Console.WriteLine();
}

이와 같이 출력을 했었으니, 맵 출력에 변화를 주려면 아무래도 for 문의 내용을 변경해야 할 것이라 생각했다. 그렇다면 조건을 0에서부터 배열 끝까지의 길이가 아니라, 플레이어의 좌표를 기준으로 조건을 설정해 주면 되지 않을까?

static void PrintMap(Position playerPos, bool[,] map)   // 맵 출력
{
    // 플레이어의 위치 기준으로 상하좌우 대각선 4방향(8방향)의 거리만 보이도록 설계
    for (int y = playerPos.y - 1; y <= playerPos.y + 1; y++)
    {
        for (int x = playerPos.x - 1; x <= playerPos.x + 1; x++)
        {
            if (map[y, x] == false)
            {
                Console.SetCursorPosition(x, y);
                Console.Write("#");
            }
            else
            {
                Console.Write(" ");
            }
        }
    }
}

플레이어 좌표 기준 (x - 1) ~ (x + 1) 까지의 범위, 그리고 (y - 1) ~ (y + 1) 까지의 범위를 설정하여 맵이 출력되도록 하였다.
여러 번 테스트를 돌린 결과 잘 기동되는 것을 확인하였다.
앞으로 구현하는 부분에서 이 코드를 다른 물체에도 적용해서 썼던 지라, 어떻게 출력했는지 잘 기억해 두도록 하자

  • 오류의 발견(IndexOutOfRangeException)

참고로 이 과정을 구현하는 과정에서 발견한 것이지만, 캐릭터의 위치 설정이나 맵의 구현에서도 유의할 점이 있다.
내가 설정한 플레이어의 초기 위치는 (1, 1)이고, 플레이어 주변의 한 칸만 보여주기에는 너무 어렵나 싶어서 두 칸으로 늘려보려고도 했다.
(위 코드에서 범위를 (x - 2) ~ (x + 2) 까지의 범위, 그리고 (y - 2) ~ (y + 2)로 설정해 봤다)
바로 오류가 걸리는 것을 확인했다. 처음에는 게임 테스트를 하는데 이런 문제가 생기니 당황스러울 수밖에 없었다.
하지만 이 오류가 뭔지 잘 살펴보자.

IndexOutOfRangeException 잘못된 인덱스가 배열 또는 컬렉션의 멤버에 액세스하거나 버퍼의 특정 위치에서 읽거나 쓰는 데 사용되는 경우 예외가 throw됩니다.

위에 출력된 오류의 내용까지 같이 확인해 보면 즉, 배열의 범위를 넘어선 값을 출력하려다 생긴 오류라는 것을 알 수 있다.

앞에 내가 처음에 적은 조건을 생각해 보자.

  1. 플레이어의 위치는 (1, 1)로 설정하였다.
  2. 플레이어 상하좌우로 2칸에 해당하는 부분이 출력되어야 하므로,
    배열 상 값이 할당되지 않은 (-1, -1)과 같은 부분을 출력하려 해서 오류가 생겼다.

이렇게 직접 오류를 겪어 보니, 이 오류가 무엇인지 어떻게 해결할 것인지도 감을 잡게 되었다.
나는 결과적으로는 그냥 플레이어 시야를 1로 설정하여 작성하긴 했지만, 시야를 2로 늘리고 싶다면 조건을 이렇게 하면 된다.

  1. 플레이어는 맵 가장자리에서 2칸 안에 있는 곳에 위치할 것.
  2. 플레이어 바깥을 둘러싸는 벽의 두께를 2칸 이상으로 둘 것.

이와 같이, 오류에 대한 공부와 해결 방법에 대해 스스로 해결해보았다.

3. 골인지점도 출력했고, 다음으론 코인을 구현해 보자.

1) 골인지점을 구현해 보자

골인지점 구현은, 벽과는 달리 플레이어 시야 2칸 범위 내에서 출력하도록 아래와 같이 조건을 추가하였다.

static void PrintGoalPos(Position playerPos, Position goalPos)  // 골인 지점 출력
{
    // 플레이어 시야-2칸 거리에 들어왔을 때 보이도록 설정
    for (int y = playerPos.y - 2; y <= playerPos.y + 2; y++)
    {
        for (int x = playerPos.x - 2; x <= playerPos.x + 2; x++)
        {
            if (x == goalPos.x && y == goalPos.y)
            {
                Console.SetCursorPosition(goalPos.x, goalPos.y);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write('G');
                Console.ResetColor();
            }
        }
    }
}

출력하면 아래와 같이 나온다.

골인 지점이 벽 너머로 보이는 게 조금 웃기긴 하지만, 너무 안 보이게 한 칸으로 설정하면 어렵고 귀찮을 것 같아서 이렇게 만들었다.

2) 새로운 기능을 만들어 보자.

골인 지점도 출력하고, 이겼습니다 라는 멘트도 출력했다. 하지만 프로젝트를 더 다듬고 기능을 추가할 시간이 남아 있었다.
그러면 무슨 기능을 넣을까? 처음에는 스테이지를 만들려고 생각하기도 했지만, 다른 걸 해 보기로 했다.
(사실은 맵을 또 만들기가 너무 싫었다... 아무래도 시야각이 좁다 보니 첫 맵을 만들 때에도 구현하고 테스트하는데 시간을 무지 잡아먹었다...)

그러면 무슨 기능을 추가해 볼까. 시야각이 좁은 맵에서 추가하기 가장 좋은 요소는 아무래도 '히든 찾기' 요소라고 생각했다. 그러면 구현할 기능을 추가해 보자.

미로 게임을 만들 것이다. 다만 난이도를 높이기 위해서 시야 제한을 두겠다.
(미로의 모든 구조가 출력되지 않고, 플레이어 주변의 벽만 확인할 수 있는 정도로 출력)
(완료)
히든 아이템, 간단하게 코인을 구현해 보자. 그리고 코인을 획득하면 점수가 오르고, 모은 개수에 따라 엔딩 출력이 다르게 나오게 하자.

이렇게 다음 목표를 설정하고 해당 기능을 구현하기로 했다.

새로운 아이템을 만들려면 어떻게 해야 할까. 처음에는 코인 1의 위치, 코인 2의 위치, 이런 식으로 좌표를 작성해야 한다고 생각했다. 하지만, 가만 생각해 보면 코인은 골인 지점과 달리 여러 개가 존재할 수 있는 아이템이다
즉 코인은 좌표를 표시하는 것보다는 배열로 여러 개를 한 번에 표시하는 게 가독성이 훨씬 좋을 것이라 판단했다.

그렇게... 맵과 똑같이 bool값을 작성하기 시작했다.

coin = new bool[10, 15]     // 전체 코인 위치
{
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
    {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false },
    {false, false, false, false, false, true, false, true, false, true, false, false, false, false, false },
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
    {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false },
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
    {false, false, false, true, false, true, false, false, false, false, false, false, false, false, false },
    {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false },
};

(아아... 맵이랑 비교하면서 위치를 찍어야 해서 엄청난 노가다의 현장이었다...)
이렇게 코인이 있는 곳의 위치를 true로 작성하여 맵 내의 코인의 위치를 표시하였다. 그리고 코인 또한 앞에서 작성했던 플레이어 시야 조건을 우려먹으면서 작성했다.

static void PrintCoin(Position playerPos, bool[,] coin) // 코인 출력
{
    // 플레이어의 위치 기준으로 상하좌우 대각선 4방향(8방향)의 거리만 보이도록 설계
    for (int y = playerPos.y - 1; y <= playerPos.y + 1; y++)
    {
        for (int x = playerPos.x - 1; x <= playerPos.x + 1; x++)
        {
            if (coin[y, x] == true)
            {
                Console.SetCursorPosition(x, y);
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.Write("*");
                Console.ResetColor();
            }
        }
    }
}

이제 잘 구동되는지 확인해보자.

파란색의 별(코인)이 잘 출력된 것을 확인할 수 있었다.
여담으로 맵의 코드를 복사해 조금 수정해서 쓰다 보니, else문의 내용을 삭제하지 않고 출력하면 또 이상한 오류가 생기는 것을 발견했다.

  • else문을 안지우면 이상하게 출력된다.

    (참고로 플레이어 오른쪽에 벽이 있는데, 출력이 되지 않는 모습이다.)

아무래도 else 문의 조건이 잘못 출력되어 공백이 자꾸 플레이어 오른쪽에 표시되는 버그인 것 같다.
이는 위에 최종으로 작성한 코인 출력문처럼 else문을 지우면 해결된다.

3) 코인 상호작용 구현

여기까지가 코인을 출력하는 과정이었고, 이제는 코인과 상호작용을 구현할 차례이다.

  1. 일단 Main 함수에 int Score = 0;으로 초기화(최종 획득점수/코인 획득 갯수를 담을 변수)
  2. 코인 상호작용은 Update문에 구현한다.
  3. 코인을 획득한 순간 Score가 올라간다, 그리고 코인을 획득했을 때 해당 코인은 사라져야 한다.

이러한 기능이 필요하므로, 해당 조건을 생각하여 작성하였다.

static int GetCoin(Position playerPos, bool[,] coin, ref int score)   // 코인 획득 판정
{
    if (coin[playerPos.y, playerPos.x] == true) // 플레이어가 코인과 좌표가 겹쳤을 때(코인에 도달했을 때)
    {
        score++;    // 획득갯수(score 상승)
        coin[playerPos.y, playerPos.x] = false; // 해당 위치의 얻은 코인은 사라짐
    }
    return score;
}

지금 보면 간단한 코드이지만, 생각보다 코드를 쓸 때 '왜 안 되지'가 많이 나왔었다.

3.1) 잠깐, 오류 확인하고 넘어가자 (CS0161)

특히 잘 이해가 되지 않는 오류코드 'CS0161'이란 코드였다.
이 오류가 뭔지 이해가 안 돼서 상당히 머리를 쥐어싸맸는데, 이건 결국 이게 문제였다.

  • return score가 if 문 안에 있을 때에는 오류가 출력된다.
  • return score를 if 문 바깥으로 빼냈을 때에는 오류가 사라졌다.

이렇게 비교해보면서 해당 오류의 발생 원인을 알 수가 있다.

나는 해당함수를 int를 반환하는 함수로 선언하였다. 그러므로 반환값이 필수로 있어야 하기에, return으로 값을 반환해야 한다.
하지만 return 반환을 if 문 안에 넣게 되면, return이 반환되지 않는 상황이 생긴다.
따라서 "일부만 반환"하는 상황이기 때문에 오류가 발생한다.

이런 점을 몸소 겪으면서 함수를 사용할 때의 유의점을 확실히 알게 되었다.
반환값이 있는 함수를 사용할 때에는 꼭 return이 모든 상황에서 적용되는지 확인해보자.

4) 점수 계산과 코인 개수 출력

이 부분은 간단했다. End 부분의 내용을 좀 더 추가하여 모은 코인 개수가 몇 개인지, 몇 개를 못 찾았는지 출력하는 부분을 추가했다.

Console.Clear();
Console.WriteLine("축하합니다!!! 게임을 클리어하였습니다!");
Console.WriteLine("모은 코인의 개수는 {0}개입니다.", score);
if (score == 7) // 코인을 전부 획득했을 때
{
    Console.WriteLine();
    Console.WriteLine("축하합니다!!! 숨겨진 코인을 전부 찾으셨습니다!");
}
else // 코인을 전부 획득하지 못했을 때
{
    Console.WriteLine();
    Console.WriteLine("찾지 못한 코인 : {0}", 7 - score);
    Console.WriteLine();
    Console.WriteLine("다시 찾아보실까요?");
}

if 조건문을 활용하여 코인을 다 모았을 시 코인을 전부 찾았다고 출력하고, 다 못 모았으면 몇 개를 못 찾았는지 출력하게 했다.

4. 아직도 더 뭔가를 할 수 있을 것 같다. 코인 획득 출력하기

자. 이젠 뭘 할까? 고민하다가, 문득 코인을 먹었을 때 먹은 건지 만 건지 잘 모르겠다는 생각이 들었다. 그래서 코인을 먹었을 때 코인을 획득했다는 멘트를 출력하는 기능을 만들어 보기로 했다.

미로 게임을 만들 것이다. 다만 난이도를 높이기 위해서 시야 제한을 두겠다.
(미로의 모든 구조가 출력되지 않고, 플레이어 주변의 벽만 확인할 수 있는 정도로 출력)
(완료)
히든 아이템, 간단하게 코인을 구현해 보자. 그리고 코인을 획득하면 점수가 오르고, 모은 개수에 따라 엔딩 출력이 다르게 나오게 하자. (완료)
코인을 먹었을 때, 코인을 획득했다고 한 번 출력하게 만들자.

이렇게만 생각했을 때, 아까 코인을 획득한 상황에서 그냥 Console.WriteLine();만 출력하면 되는 일이 아닌가 생각했다.

static int GetCoin(Position playerPos, bool[,] coin, ref int score, )   // 코인 획득 판정
{
    if (coin[playerPos.y, playerPos.x] == true) // 플레이어가 코인과 좌표가 겹쳤을 때(코인에 도달했을 때)
    {
        score++;    // 획득갯수(score 상승)
        Console.WriteLine("코인을 획득했습니다.");
        coin[playerPos.y, playerPos.x] = false; // 해당 위치의 얻은 코인은 사라짐                
    }
    return score;
}

이렇게 입력하고 출력했다.

구문 상 오류도 안 보이는데, 왜 출력이 안 될까? 여러 번 테스트 한 결과 출력이 안 되는게 아니었다.
엄청나게 빠른 속도로 출력되고 나서 바로 지워지는 현상이 발견되었다. 그 이유는 다음과 같았다.

Getcoin 구문은 Update 부분에 위치한다. Update에서 조건이 충족되어 출력되었지만, 출력되는 순간 바로 Render 단계로 넘어가서 맵이 Clear 되어버리기 때문에 적히고 바로 지워지는 것이다.

그렇다면 해결 방법은 이 출력하는 부분을 Render로 옮겨주는 것이다.
이게 불가능한 것은 아니지만 참 골 때리는 부분이었다. 계속 변수가 늘어나는 것 같은데, 그것 말고는 구현 방법이 딱히 안보여서 bool 변수 하나를 추가했다.

  1. 메인에 bool getCoin = false; // 코인을 획득했을 때(true)
    선언
  2. GetCoin 에서 동전을 먹었을 때, 해당 bool값을 true로 변경되게 만듬
  3. 해당 bool값이 true가 되었을 때 Render에서 "코인을 획득했습니다." 멘트 출력하게 만들기

이렇게 내용을 구성해 보자.
우선 GetCoin 함수의 내용을 조금 수정했다.

static int GetCoin(Position playerPos, bool[,] coin, ref int score, ref bool getCoin)   // 코인 획득 판정
{
    if (coin[playerPos.y, playerPos.x] == true) // 플레이어가 코인과 좌표가 겹쳤을 때(코인에 도달했을 때)
    {
        score++;    // 획득갯수(score 상승)
        getCoin = true;     // 코인을 획득상태 = true
        coin[playerPos.y, playerPos.x] = false; // 해당 위치의 얻은 코인은 사라짐                
    }
    return score;
}

getCoin의 bool값을 반영하기 위해 ref로 참조했다. 이 다음 getCoin이 true일 때 멘트 출력을 하는 함수를 만들고, Render에 배치한다.

static void GetCoinWrite(ref bool getCoin)  // 코인 획득시 출력
{
    if (getCoin == true)
    {
        Console.SetCursorPosition(1, 13);
        Console.WriteLine("코인을 획득했습니다.");
        getCoin = false;    // 코인 획득한 순간 한 번만 출력하고 다시 false로 전환
    }
}

여기서 우선 맵 밖에 멘트가 찍히도록 커서 위치를 설정해주었다. 그리고 bool값을 false로 다시 바꿔주지 않으면 계속 코인을 획득했다는 멘트가 출력될 것이므로 bool값을 초기화해주는 과정을 추가했다.

이렇게 작성한 후에 실행시켜보자.

이렇게 코인을 먹은 순간 멘트가 출력되고,

다시 한 칸을 움직이면 멘트가 사라진다.

5. 재시작 및 종료 기동

이만큼 해도 아직 기능 한 가지 정도는 더 구현할 시간이 남았다. 그러면 마지막으로 뭘 해볼까.
이쯤 부터는 피드백 시간도 있다 보니 추가 구현 기능 예시 같은 것도 엿들을 수 있었다.
시간 개념을 넣는다든지 스테이지를 여러 개 넣는다든지.
맵을 만들 시간은 없으니 스테이지는 패스하고, 이런 게임에 제한시간을 넣으면 욕할 것 같아서 이 부분은 넘어갔다.

재시작 기능 만들기. 이걸로 한 번 해 보기로 했다.

미로 게임을 만들 것이다. 다만 난이도를 높이기 위해서 시야 제한을 두겠다.
(미로의 모든 구조가 출력되지 않고, 플레이어 주변의 벽만 확인할 수 있는 정도로 출력)
(완료)
히든 아이템, 간단하게 코인을 구현해 보자. 그리고 코인을 획득하면 점수가 오르고, 모은 개수에 따라 엔딩 출력이 다르게 나오게 하자. (완료)
코인을 먹었을 때, 코인을 획득했다고 한 번 출력하게 만들자. (완료)
재시작 기능, 종료 기능을 만들어 보자.

1) 기능의 틀을 잡기 전에 일단 텍스트 출력부터 해 보자.

재시작 기능을 만들자. 이렇게 말만 해서 바로 구현할 수 있는 것은 아니다.
일단 틀을 잡기 위해서 End 부분에 텍스트를 짧게 추가했다.

Console.WriteLine();
Console.WriteLine("다시 시작하려면 R, 종료하려면 T를 눌러주세요.");

일단 이렇게, R 버튼을 누르면 재시작, T 버튼을 누르면 종료하는 기능을 만들고자 한다.
이렇게 출력을 추가한 다음 기능만 구현하면 끝난다.

2) 재시작을 위해 반복문을 하나 더 추가한다.

지금까지의 게임 구동을 생각하면, 게임이 진행되는 동안 While 반복문에서 내내 반복 재생하고 있었다. 그러면 게임이 끝났을 때 그 게임을 재시작 하려면, 결국 그 게임 자체를 반복하게 할 While문을 하나 더 만들면 된다.
아주 귀찮아지는 작업이지만, While 속에 넣을 bool 값을 하나 더 만들고 While 구문을 하나 더 만들어서 과정을 넣겠다.

static void Main(string[] args)
{
    Opening();  // 시작 화면
    bool reset = true;  // 리셋을 위한 bool값

    while (reset == true)
    {
        bool gameOver = false;  // 게임오버 여부
        Position playerPos;     // 캐릭터 좌표
        playerPos.x = 0;
        playerPos.y = 0;
        Position goalPos;       // 골인 지점 좌표
        int score = 0;          // 먹은 코인 갯수(점수)
        bool getCoin = false;   // 코인을 획득했을 때(true)

        Start(ref playerPos, out goalPos, out bool[,] map, out bool[,] coin);

        while (gameOver == false)   // 게임 종료가 아닐 시
        {
            Render(playerPos, goalPos, map, coin, ref getCoin);
            ConsoleKey key = Input();
            Update(key, ref playerPos, goalPos, map, coin, ref gameOver, ref score, ref getCoin);
        }
        End(score, ref reset);
    }
}

하도 이것저것 기능을 만들어 넣다 보니 번잡하기 그지없지만, 이마저도 나름 정리한 코드란 게 놀랍다.

어쨌든 게임 전체를 또 다른 While 반복문 속에 넣었으니, 여기에서 reset == false가 되었을 대 게임이 종료될 것이다. 즉, T를 눌렀을 때 reset을 false가 되게 만들면 된다.

End 구문 뒤쪽에 내용을 추가했다.

if (Console.ReadKey(true).Key == ConsoleKey.T)  // T를 누르면 종료
{
    reset = false;
}
else if (Console.ReadKey(true).Key == ConsoleKey.R) // R을 누르면 게임 재시작 가능
{

}

else if로 R에 대해서도 저렇게 공백으로 처리하면 reset은 여전시 true일 것이므로 다시 게임이 재시작될 것이다.
그런데 여기서 문제가 생겼다. 이렇게 코드 조건을 짰지만, 내가 선언하지 않은 부분 - else에 대한 조건은 없는 상태이다 보니 R 버튼이 아니어도 T를 제외한 버튼만 입력받으면 초기화되는 현상을 발견했다.
의도하지 않은 이 현상을 어떻게 해결해야 할까. 가만 생각해 보다가 좋은 아이디어가 떠올랐다.

T를 누르면 종료, R을 누르면 재시작, 그리고 나머지 키를 입력받으면 무반응이어야 한다.
>>무반응이라는 소리는? End 구문을 빠져나가지 못하게 묶어두어야만 한다.

이에 따라 위의 코드를 조금 수정하여, while 반복문 안에 넣어서 구현했다.

while (reset == true) // 리셋 기능 구현
{
    if (Console.ReadKey(true).Key == ConsoleKey.T)  // T를 누르면 종료
    {
        reset = false;
    }
    else if (Console.ReadKey(true).Key == ConsoleKey.R) // R을 누르면 End구문을 빠져나가 게임 재시작 가능
    {
        return;
    }
    // else - 나머지 키를 입력할 경우 이 구문에서 못 빠져나가게 하여, 입력 시 무반응으로 처리
}

이와 같이 원래 제작 의도대로 구현하였다.

6. 프로젝트 제작 결과

7. 결론

이와 같이 게임을 직접 구현해 보는 과정을 거치면서, 많은 난관을 느끼고 그걸 뛰어넘는 경험을 했다.
특히나 이론적인 공부와 남들이 이미 만들었던 걸 똑같이 만들어보는 경험도 중요하지만,
남들이 구현하지 않았던 기능을 생각해 보고, 스스로 기능을 구현해 보는 과정이 뜻깊었다고 생각한다.
단순히 예제로만 배우는 것과는 다른 느낌이 들었고, 앞으로 현업에서도 이런 식으로 새로운 기능을 만들어야 할 때가 있을 것이다.

  1. 직접 프로젝트를 하면서 겪었던 문법 오류 및 논리 오류, 그런 것들을 되짚어 보면서 어떻게 해결하면 될지 잘 생각해보자.
  2. 문제에 봉착했을 때 그 문제를 어떻게 해결해 나가야 하는지, 사고 과정이 중요하다는 걸 느꼈다. 차분히 생각하면서 문제 해결의 경험을 쌓고 다양한 사례 등을 보면서 사고의 폭과 시야를 넓히자.
profile
게임 만들러 코딩 공부중

0개의 댓글