[C#] 미로 만들기와 길찾기 알고리즘 Part 1 : 미로 만들기(1)

인트로

C# 콘솔 프로그래밍으로 미로를 만들고 BFS, A* 알고리즘으로 미로의 출구를 찾는 프로그램을 작성하려 한다.

 

Part1에선 2차원 미로를 만들어보려 한다.

 

(+ Visual Studio 기준으로 포스팅을 이어나갈 예정입니다.)

 

[C#] 미로 만들기와 길찾기 알고리즘 Part 1 : 미로 만들기(2)

[C#] 미로 만들기와 길찾기 알고리즘 Part 1 : SideWinder 미로(3)

[C#] 미로 만들기와 길찾기 알고리즘 Part 2 : Player 만들기

[C#] 미로 만들기와 길 찾기 알고리즘 Part 3 : BFS 길 찾기

 

프로젝트 만들기

Visual Studio -> 새 프로젝트 만들기 -> C# -> 콘솔 앱(.NET Core) -> 프로젝트 만들기

그럼 다음과 같이 Program의 Main 메서드가 덩그러니 있을 것이다.

using System;

namespace Maze
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Board라는 클래스를 하나 만들어서 미로를 만들고 관리하는 코드는 Board에서 처리하도록 하자.

Board 클래스 추가하기

 

FPS(Frames Per Second) 이해하기

기본적으로 게임은 메인(*무한) 루프가 하나 있고 메인 루프를 돌며 게임을 올바르게 동작하기 위한 여러 로직을 처리한다. 메인 루프는 크게 세 가지로 분리가 된다.

 

1) 유저의 입력 감지

 

2) 유저의 입력과 기타 로직을 처리

 

3) 렌더링 (화면에 뿌려줌)

본 포스팅은 미로를 만들고 출력하는 것이 목적이기에 렌더링과 연관된 코드를 작성하려 한다.

 

렌더링 코드를 작성하기 전에 프레임(Frame)을 알아야 한다. 게임을 즐겨 하는 사람이라면 "FPS"를 한 번쯤 들어봤을 것이다. 우리가 플레이하는 게임 또는 영화와 같은 영상물은 시간상 연속된 사진들의 모음인데, 이 각각의 사진을 프레임이라고 부른다. FPS(Frames Per Second)란 초당 몇 개의 프레임(사진)을 화면에 뿌리는지 나타내는 수치이다. 

 

일반적으로 사람은 60 프레임까진 시각적으로 어색함을 느끼지 않지만 60 프레임 미만으로 떨어지는 순간 영상이 뚝뚝 끊기는 이질감을 느끼게 된다.

 

결국 화면이 부드럽게 보인다 혹은 캐릭터가 부드럽게 움직인다는 의미는 짧은 시간 동안 렌더링을 여러 번 수행한다는 의미이다.

그렇다면 프로그래밍에서 프레임 관리는 어떻게 해야 할까? 간단하게 System.Environment.TickCount를 사용하면 된다. System.Environment.TickCount가 반환하는 값은 절대적인 시간의 개념이 아니라 시스템(컴퓨터)이 시작된 이후부터 경과된 시간을 밀리 세컨드로 반환한다. 우린 절대적인 시간의 개념이 필요한 게 아니라 마지막 측정 시간(lastTick)현재 시간(currentTick)차이만 알면 된다.

 

현재 시간과 이전 시간의 차이가 1/30 초보다 크다면 화면을 다시 렌더링 하며 이것을 30 FPS라 부른다.

//이전 측정 시간
int lastTick = 0; 
while (true)
{
    //현재 측정 시간
    int currentTick = System.Environment.TickCount;

    //경과 시간이 1/30초보다 작다면?
    if (currentTick - lastTick < WAIT_TICK) 
        continue;

    lastTick = currentTick;
 }

↓↓↓↓

static void Main(string[] args)
{
    const int WAIT_TICK = 1000 / 30;
    Console.CursorVisible = false;

    int lastTick = 0;
    while (true)
    {
        int currentTick = System.Environment.TickCount;

        //경과 시간이 1/30초보다 작다면? (30 FPS)
        if (currentTick - lastTick < WAIT_TICK)
            continue;

        lastTick = currentTick;

        // 1)사용자 입력 대기

        // 2)입력과 기타 로직 처리

        // 3)렌더링
        Console.SetCursorPosition(0, 0);
    }
}

 

2차원 배열로 사각형 출력하기

Board 클래스를 작성할 차례이다. Board를 초기화해줄 InitializeBoard 메서드를 작성해서 보드의 크기를 받아주고 Render메서드에서 Board의 크기만큼 이중 for문을 돌며 특수문자('\u25cf')를 출력한다.

색이 밋밋하니 Console.ForegroundColor = ConsoleColor.Green;를 추가해 녹색 미로를 출력하도록 코드를 작성해보자.

/* Board */
class Board
{
    const char CIRCLE = '\u25cf';
    public int _size;

    public void InitializeBoard(int size)
    {
        _size = size;
    }

    public void Render()
    {
        for (int y = 0; y < _size; y++)
        {
            Console.ForegroundColor = ConsoleColor.Green;

            for (int x = 0; x < _size; x++)
                Console.Write(CIRCLE);

            Console.WriteLine();
        }
    }
}

다시 Program 클래스로 돌아와서 Board 객체를 만들어주고 Render 메서드를 호출하는 코드를 메인 루프 내에 추가해준다.

/* Program.cs */
class Program
{
static void Main(string[] args)
{
    const int WAIT_TICK = 1000 / 30;

    Board board = new Board();
    board.InitializeBoard(25);

    Console.CursorVisible = false;

    int lastTick = 0;
    while (true)
    {
        #region 프레임 관리
        int currentTick = System.Environment.TickCount;

        //경과 시간이 1/30초보다 작다면?
        if (currentTick - lastTick < WAIT_TICK)
            continue;

        lastTick = currentTick;
        #endregion

        // 1)사용자 입력 대기

        // 2)입력과 기타 로직 처리

        // 3)렌더링
        Console.SetCursorPosition(0, 0);
        board.Render();
    }
}

심플한 미로(?)가 출력됐다.