본문 바로가기

Unity/Unity 스파르타

3주차, 콘솔창으로 스네이크 게임 만들기

728x90

콘솔창으로 스네이크 만들어 보기

 

설명

플레이어가 조종하는 뱀을 키워나가는 게임입니다. 게임 화면은 일반적으로 격자 형태로 구성되어 있으며, 뱀은 한 칸씩 이동하면서 먹이를 먹어 몸을 키워나갑니다.

게임의 목표는 뱀을 최대한 길게 키우면서, 벽이나 자기 자신과 부딪히지 않도록 하는 것입니다. 뱀이 벽이나 자기 자신과 충돌하면 게임이 종료되며, 획득한 점수가 표시됩니다.

일반적으로 플레이어는 키보드의 화살표 키를 사용하여 뱀의 이동 방향을 조절합니다. 뱀은 먹이를 먹을 때마다 길어지며, 게임이 진행될수록 뱀의 이동 속도가 빨라집니다

 

순서

1. 기본적으로 2차원 배열을 만들어 보드판을 구성 할 것이기 때문에 위치값으로 사용할 구조체를 만들어 줍니다.

2. 보드판 클래스를 만들어 계속해서 맵을 랜더링할 함수를 만들어 줍니다.

3. 스네이크 클래스를 만들어줍니다.

4. 게임 실행 로직을 만들어 줍니다.

 

1. 기본적으로 2차원 배열을 만들어 보드판을 구성 할 것이기 때문에 위치값으로 사용할 구조체를 만들어 줍니다.

 struct Position
 {
     public int X;
     public int Y;

     public Position(int x, int y)
     {
         X = x;
         Y = y;
     }
  
 }

2. 보드판 클래스를 만들어 계속해서 맵을 랜더링할 함수를 만들어 줍니다.

  • 보드판을 구성할 타일의 모양을 정해줍니다.( ■를 유니코드로 전환을 하면 \u25A0 입니다)
 class Board
 {
     const char rect = '\u25A0';
     int boardSize;
     public Board(int size)
     {
         boardSize = size;
     }
     
     
     public void GeneratorBoard(List<Position> snake, Position food)
     {
     }
     
}
  • 보드판을 랜더링할 함수를 만들어 줍니다.
    • 보드판 크기만큼 순회를 하면서 먹이위치와 뱀의 위치를 확인하고 타일의 색을 바꿔줍니다.
public void GeneratorBoard(List<Position> snake, Position food)
{
   
    for (int i = 0; i < boardSize; i++)
    {
        for (int j = 0; j < boardSize; j++)
        {
            Console.ForegroundColor = ConsoleColor.Gray;
           
            if (food.X == i && food.Y == j)
            {
                Console.ForegroundColor = ConsoleColor.Red;
            }
            foreach (Position pot in snake)
            {
                if(pot.X == i && pot.Y == j)
                {
                    Console.ForegroundColor = ConsoleColor.Blue;           
                }
            }
            Console.Write(rect);
        }
        Console.WriteLine();
    } 
}

 

3. 스네이크 클래스를 만들어줍니다.

  • 기본적으로 스네이크를 구성하는 변수들을 만들어준 뒤 초기화 해줍니다. 
 class Snake
 {
      public List<Position> position;
      public int snakeSpeed;
      
      int headPotX;
      int headPotY;
      int dirX;
      int dirY;

     public Snake(int boardSize)
     {
         position = new List<Position>();
        
         dirX = 0;
         dirY = 1;
         headPotX = boardSize / 2;
         headPotY = boardSize / 2;
         position.Add(new Position(headPotX, headPotY));
         snakeSpeed = 0;
     }

     public int HeadPotX { get { return headPotX; } }
     public int HeadPotY { get { return headPotY; } }
    
    public void ChangeDir(ConsoleKey key)
     {
     }
     public void Move()
     {
     }
     public bool CheckSnackCollision()
     {
     }
     public void EatFood(Position _food)
     {
     }
 }
  • 스네이크의 움직임과 관련된 함수를 만들어 줍니다.
    • 플레이어의 방향키를 파라미터로 받아서 스네이크의 진행 방향을 바꿔 줍니다.
    • 스네이크를 현재 진행 방향으로 이동시켜줍니다.
      • 스네이크의 머리가 진행방향으로 한칸 이동하면 Insert 함수로 스네이크 머리 바로 다음 인덱스에 원래 머리 위치값으로 삽입해 준뒤, 맨뒤에있는 인덱스를 제거해 줍니다.
public void ChangeDir(ConsoleKey key)
{
    switch (key)
    {
        case ConsoleKey.LeftArrow:
            if (dirY != 1)
            {
                dirX = 0;
                dirY = -1;
            }
            break;
        case ConsoleKey.RightArrow:
            if (dirY != -1)
            {
                dirX = 0;
                dirY = 1;
            }
            break;
        case ConsoleKey.UpArrow:
            if (dirX != 1)
            {
                dirX = -1;
                dirY = 0;
            }
            break;
        case ConsoleKey.DownArrow:
            if (dirX != -1)
            {
                dirX = 1;
                dirY = 0;
            }
            break;
    }
}

public void Move()
{
    int newHeadPotX = headPotX + dirX;
    int newHeadPotY = headPotY + dirY;

    Position newPot = new Position(headPotX, headPotY);

    headPotX = newHeadPotX;
    headPotY = newHeadPotY;

    position[0] = new Position(newHeadPotX, newHeadPotY);
  
    if (position.Count > 1)
    {       
        position.Insert(1, newPot);          
        position.RemoveAt(position.Count-1);
    }
}

 

4. 게임 실행 로직을 만들어 줍니다.

  • 기본 변수들을 초기화 해줍니다.
  • 게임이 끝났을때 콘솔창을 깨끗하게 지워준뒤 게임오버 메세지와 스코어를 적어줍니다.
int boardSize = 25;
Snake snake = new Snake(boardSize);
Board board = new Board(boardSize);
Position food = new Position(2, 3);
Random random = new Random();

bool gameOver = false;
Console.Title = "Snake";
Console.CursorVisible = false;

       
while (!gameOver)
{

}

Console.Clear();
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Game Over! ");
Console.WriteLine($"Snake Score : {snake.position.Count}");
  • 만들어놓은 스네이크 클래스와 보드 클래스를 사용해 게임을 구현해 줍니다.
  • 0,0 위치에 커서를 위치시켜서 해당위치부터 반복해서 맵을 랜더링 시킬 것 입니다.
    • 일반적으로 콘솔 응용 프로그램은 사용자와 상호 작용하기 위해 키보드 입력을 받아야 합니다. 이때 Console.KeyAvailable 메서드를 사용하면 현재 입력 버퍼에 키 입력이 있는지 여부를 확인할 수 있습니다. 즉, 사용자가 키를 누를 때까지 프로그램이 대기하는 것을 방지하고, 키 입력이 발생하면 해당 키를 즉시 처리할 수 있습니다.Console.KeyAvailable 메서드는 불리언 값을 반환하며, 입력 버퍼에 키 입력이 있는 경우 true를 반환하고, 그렇지 않은 경우 false를 반환합니다. 따라서 이 메서드를 사용하여 키 입력이 발생했는지 여부를 확인한 후, 발생한 키를 읽어와서 원하는 동작을 수행할 수 있습니다.
  • 플레이어의 입력을 확인한 뒤 입력이 발생하면 스네이크의 방향을 바꿔줍니다.
  • 스네이크를 움직이고 먹이를 먹었는지 확인한 뒤 충돌 체크를 해줍니다.
  • 맵을 랜더링 해줍니다.
  • Thread.Sleep() 메서드를 사용하면 특정 시간 동안 스레드를 일시 정지시킬 수 있습니다.Thread.Sleep() 메서드는 밀리초 단위의 정수 값을 매개변수로 받습니다. 이 매개변수에 전달된 시간 동안 스레드가 일시적으로 정지하며, 다른 작업을 수행하지 않고 대기합니다. 예를 들어, Thread.Sleep(1000)은 스레드를 1초 동안 멈추게 합니다.
while (!gameOver)
{
    Console.SetCursorPosition(0, 0);

    if (Console.KeyAvailable)
    {
        ConsoleKeyInfo key = Console.ReadKey(true);
        snake.ChangeDir(key.Key);
    }
   
    snake.Move();
    CheckFood(food);
    CheckCollision();

    board.GeneratorBoard(snake.position, food);
    Thread.Sleep(Math.Clamp(100 - snake.snakeSpeed,0,100));
}

  void CheckCollision()
  {
      if (snake.HeadPotX < -1 || snake.HeadPotY < -1 || snake.HeadPotX >= boardSize - 1 || snake.HeadPotY >= boardSize - 1 || snake.CheckSnackCollision())
      {
          gameOver = true;
      }
  }

  void CheckFood(Position foodCoord)
  {
      if (snake.HeadPotX == foodCoord.X && snake.HeadPotY == foodCoord.Y)
      {
          snake.EatFood(foodCoord);
          food = new Position(random.Next(0, boardSize-1), random.Next(0, boardSize-1));
      }
  }

 

완성

 

 

728x90