[ C# ] 미로 게임 구현

2024. 3. 9. 22:45C#/구현

 

다뤄야하는 객체가 2개 이상임으로 작업의 순서를 정할 필요가 있었다.

 

작업 순서

1. 미로 맵 제작.

2. 플레이어 캐릭터 제작.

3. 출구 지정.

4. 플레이어를 랜덤하게 스폰하고 해당 위치 저장.

5. 플레이어 움직이도록 설정.

6. 플레이어가 출구에 도달하면 루프 정지.


1. 미로 맵 제작.

1-0. 미로를 제작할 class 제작.

1-1. 미로의 크기를 입력받아 맵의 크기를 조절하도록 설정.

1-1-2. 만약 맵의 크기를 짝수로 입력하면 종료되도록 예외 처리.

1-2. 타일의 타입을 구분짓기 (wall, road, player, exit)

[이진 트리 방식]

1-3. 짝수번의 타일 타입은 Wall, 홀수 번의 타일 타입은 road가 되도록 설정.

1-4. 반복문을 사용해서 road의 타일들이 자신의 아래와 오른쪽의 Wall 타일들 중 하나를 랜덤하게 골라

        road타일로 바꾸도록 설정.

1-5. 오른쪽 테두리와 인접한 곳은 아래쪽 타일을 Road로 바꾸도록 설정.

1-6. 아래쪽 테두리와 인접한 곳은 오른쪽 타일을 road로 바꾸도록 설정.

1-7. 오른쪽 아래 구석은 아무것도 바꾸지 않도록 설정.

 

1-8. 타일의 타입에 따라 콘솔의 텍스트 색을 변경하는 함수를 제작

1-9. 해당 함수를 이용하여 Console.ForegroundColor 를 변경하고 화면을 출력하는 함수를 제작.

 

더보기
 enum Tile_Type	//1-2.
 {
     Road,
     Wall,
     Player,
     Monster,
     Exit
 }
 
 internal class Maze // 1-0
{
    const char Check = 'ㅁ';
    protected Tile_Type[,] tile;
    protected int size;
    protected Random random;
    public int RowIndex { get; set; }
    public int ColumnIndex { get; set; }
    public Maze()
    {
        random = new Random();
        player = new Player();
    }
    public void InitMap(int inputSize)
	{
    	if (inputSize %2 == 0)	//1-1-2.
        {
                Console.WriteLine("맵의 크기는 홀수여야만 합니다.");
                return;
        }
    	this.size = inputSize;	//1-1.
		tile = new Tile_Type[size, size];
        
        for (int y = 0; y < size; y++)	//1-3.
 		{
     		for (int x = 0; x < size; x++)
     		{
         		if (x % 2 == 0 || y % 2 == 0)
             	tile[y, x] = Tile_Type.Wall;
         	else
             	tile[y, x] = Tile_Type.Road;
     		}
 		}
        
        for (int y = 0; y < size; y++)
        {
        	for (int x = 0; x < size; x++)
            {
            	if (x % 2 == 0 || y % 2 == 0)	
                	continue;
               	if (x == size - 2 && y == size - 2)	//1-7.
                     continue;
                if (y == size - 2)	//1-6.
                {
                     tile[x + 1, y] = Tile_Type.Road;
                     continue;
                }
                if (x == size - 2)	//1-5.
                {
                     tile[x, y + 1] = Tile_Type.Road;
                     continue;
                }
                if (random.Next(0, 2) == 0)		//1-4.
                {
                     tile[y, x + 1] = Tile_Type.Road;  
                }
                else
                {
                	tile[y + 1, x] = Tile_Type.Road;  
                }
            }
         }
     }
}  
public void Render()	//1-9.
{
    ConsoleColor preColor = Console.ForegroundColor;
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            Console.BackgroundColor = ConsoleColor.Black;
            Console.ForegroundColor = GetTileColor(tile[i,j]);
            Console.Write(Check);
        }
        Console.WriteLine();
    }
    Console.ForegroundColor= preColor;
}

ConsoleColor GetTileColor(Tile_Type type)	//1-8.
{
    switch (type)
    {
        case Tile_Type.Wall:
            return ConsoleColor.White;
        case Tile_Type.Road:
            return ConsoleColor.Black;
        case Tile_Type.Player:
            return ConsoleColor.Blue;
        case Tile_Type.Exit:
            return ConsoleColor.Green;
        case Tile_Type.Monster:
            return ConsoleColor.Red;
        default:
            return ConsoleColor.Magenta;
    }
}

2. 플레이어 캐릭터 제작.

2-0. 플레이어 class 제작.

2-1. 좌표를 받는 Setter와 좌표를 출력하는 Getter 제작.

더보기
internal class Player //2-0.
{
    private int PosX {  get; set; }
    private int PosY {  get; set; }

  
    public void SetPlayerPosition(int posX, int posY) // 2-1.
    {
        this.PosX = posX;
        this.PosY = posY;
    }
    
    public int GetPlayerPositionX() { return this.PosX;}
    public int GetPlayerPositionY() { return this.PosY;}

}

3. 출구 지정.

3-0. 랜덤한 좌표에 출구가 spawn되도록 만들어주는 함수 제작.

3-1. x좌표와 y좌표 랜덤 설정.

3-2. 해당 좌표 타일이 Wall 인지 확인하고 없으면 해당 타일을 Exit으로 지정.

3-3. 해당 좌표를 Exit_X, Exit_Y에 저장하는 함수를 제작.

더보기
protected int ExitX;
protected int ExitY;

public void SetExitPosition(int x, int y) //3-3.
{
    this.ExitX = x; this.ExitY = y; 
}

//출구 랜덤 생성
public void ExitRandomSpawn() //3-0.
{
    while (true)
    {
        int x = random.Next(0, this.size); //3-1.
        int y = random.Next(0, this.size);

        if (tile[x, y] == Tile_Type.Wall) continue; //3-2.
        tile[x, y] = Tile_Type.Exit;
        SetExitPosition(x, y);
        break;
    }
}

4. 플레이어를 랜덤하게 스폰하고 해당 위치 저장.

4-0. 미로 클래스에 플레이어 선언.

4-1. 플레이어에게 좌표를 전달하는 함수 제작.

4-2. 랜덤한 좌표를 지정.

4-3. 해당 좌표를 플레이어 좌표로 지정하고 플레이어 클래스에게 전달.

더보기
 Player Player;	//4-0.
 //player = new Player();는 미로 생성자에 삽입
 
 public void PlayerStartPosition()	//4-1.
 {
     while (true)
     {
         int x = random.Next(0, this.size);	//4-2.
         int y = random.Next(0, this.size);
         
         if (tile[x, y] == Tile_Type.Wall) continue;
         if (tile[x, y] == Tile_Type.Exit) continue;

         tile[x, y] = Tile_Type.Player;
         Player.SetPlayerPosition(x, y);	//4-3.
         break;
     }
 }

5. 플레이어 움직이도록 설정.

5-0. 플레이어를 움직이도록 만드는 함수 제작.

5-1. 플레이어의 위치를 받아오기.

5-2. 입력받은 키가 무엇인지 판별하는 함수 제작.

5-3. 입력받은 키에 따라 좌표 변경.

5-4. 변경된 좌표 타일이 맵의 내부이고 road 타입인지 판별

5-5. 만약 조건을 만족하면 변경된 좌표는 player타입이 되고 기존의 좌표는 road타입으로 변경.

5-6. 플레이어 클래스에게 해당 좌표를 전송.

5-7. 플레이어가 움직였으면 true, 움직이지 않았으면 false를 반환.

더보기
enum Direction
{
    None,
    UP,
    DOWN,
    LEFT,
    RIGHT
}

protected Direction direction;
static Direction GetDirectionKey(ConsoleKey key)	//5-2.
{
    switch(key)
    {
        case ConsoleKey.RightArrow:
            return Direction.RIGHT;
        case ConsoleKey.LeftArrow:
            return Direction.LEFT;
        case ConsoleKey.UpArrow:
            return Direction.UP;
        case ConsoleKey.DownArrow:
            return Direction.DOWN;
        default:
            return Direction.None;
    }
}

public bool MovePlayer(Direction direction)	//5-0.
{
    int x = Player.GetPlayerPositionX();	//5-1.
    int y = Player.GetPlayerPositionY();

    switch(direction)	//5-3.
    {
        case Direction.LEFT:
            y--;
            break;
        case Direction.RIGHT: 
            y++;
            break;
        case Direction.UP: 
            x--;
            break;
        case Direction.DOWN:
            x++;
            break;
    }

    bool insideMap = x>=0 && x<size && y>=0 && y<size;	//5-4.
    bool notWall = tile[x, y] != Tile_Type.Wall;
    if (insideMap&&notWall) 
    {
        tile[Player.GetPlayerPositionX(), Player.GetPlayerPositionY()] = Tile_Type.Road;
        tile[x, y] = Tile_Type.Player;	//5-5.
        Player.SetPlayerPosition(x, y);	//5-6.
        return true;					//5-7.
    }
    return false;
}

6. 플레이어가 출구에 도달하면 루프 정지.

6-0. 플레이어가 출구에 도달했는지 판별하는 함수 제작.

6-1. 플레이어의 위치가 출구의 위치와 같은지 판별.

6-2. 만약 조건을 만족했다면 true 를 반환, 아니면 false를 반환

public bool MazeExit()	//6-0.
{
    bool samePositionX = Player.GetPlayerPositionX() ==ExitX;	//6-1.
    bool samePositionY = Player.GetPlayerPositionY() ==ExitY;
    if (samePositionX && samePositionY)
    {
        return true;	//6-2.
    }
    else return false;
}

Main 함수

더보기
using practice01;
using System;


class Program
{
    static void Main(string[] args)
    {
        Maze maze = new Maze();	//미로 선언
        maze.InitMap(25);		//미로 맵 제작
        maze.PlayerStartPosition();	//플레이어의 위치 설정
        while (true)
        {
            Console.Clear();	//콘솔 창 클리어
            maze.Render();		//미로 출력

            ConsoleKeyInfo keyInfo = Console.ReadKey();
            maze.InputKey(keyInfo);	//입력에 따라 플레이어 이동.

            bool GameEnd = maze.MazeExit();	//플레이어가 출구에 도달했는지 판별
            if (GameEnd) break;	//도달했으면 루프 중지
        }
    }
}

'C# > 구현' 카테고리의 다른 글

[ C# ] 플레이어 인벤토리 구현  (0) 2024.03.16
[ C# ] Stack 과 Queue 구현  (0) 2024.03.13