김김김의 게임개발
  • C# #7 스네이크 게임
    2023년 08월 18일 13시 55분 00초에 업로드 된 글입니다.
    작성자: noun06

    개요

    3주차 과제 중 스네이크 게임이다. 초기 게임 구조 설계와 각 클래스별 생성자를 구현하는 부분은 완료하였지만 세부적인 메서드를 작성하는 것이 감이 잡히지 않아서 풀이 코드를 참조하면서 작업을 진행하였다. 그 후 모든 코드를 해석하고 이해하는 과정을 주석으로 남기는 것으로 마무리하였다.

     

    설명

    • Main 메서드: 프로그램의 시작점이다. 뱀과 음식의 생성, 게임 실행 및 초기화, 충돌체크, 게임 오버 등을 제어.
    • DrawWalls 메서드: 게임의 벽을 그리는 역할.
    • WriteGameover 메서드: 게임 오버 메시지를 출력.
    • WriteText 메서드: 텍스트를 지정된 위치에 출력.
    • 키 입력처리: Console.KeyAvaiable()을 통해 사용자의 키 입력 여부를 확인하고 Console.ReadKey()를 통해 실제 입력된 키를 읽음. 그 후 각 키 입력마다의 출력을 지정함.
    • 게임 속도 조정: 매개변수로 일시 중지될 시간을 전달하며 작업의 일시적인 지연을 위해 사용되는 Thread.Sleep()메서드 사용. 'gameSpeed' 변수에 지정된 값만큼 게임을 일시적으로 멈추게 하여 뱀의 이동이 지정된 시간마다 이루어짐.

    • Point 클래스: 좌표와 모양을 나타내는 point를 그리거나 지우는 것을 관리하는 역할.
    • Direction 열거형(enum): 뱀의 방향을 정의하여 방향을 나타내기 위한 값 지정.
    • Snake 클래스: 뱀의 상태와 동작을 관리, Move() 메서드를 통해 뱀의 이동 제어.
    • FoodCreator 클래스: 음식의 생성과 위치를 관리.
    • Point, Snake, FoodCreator 클래스에서 객체 초기화를 위해 생성자 사용.

     

    코드

    using System.Drawing;
    using System.Xml.Linq;
    
    class Program
    {
        static void Main(string[] args)
        {
            int gameSpeed = 100; // 게임 속도
            int foodCount = 0; // 먹은 음식 수
    
            DrawWalls(); // 게임 시작 시 벽 그리기
    
            Point p = new Point(4, 5, '*'); // 뱀의 초기 위치와 모양 세팅
            Snake snake = new Snake(p, 4, Direction.RIGHT); // 초기 뱀 생성과 세팅(초기 위치, 길이, 방향)
            snake.Draw(); // 뱀을 화면에 그리기
    
            FoodCreator foodCreator = new FoodCreator(80, 20, '$'); // 음식 생성을 위한 객체 생성(범위 넓이, 범위 높이, 모양)
            Point food = foodCreator.CreateFood(); // 초기 음식 생성
            food.Draw(); // 음식을 화면에 그리기
    
            while (true) // 게임 루프: 게임이 끝날 때까지 반복 실행
            {
                if (Console.KeyAvailable) // 키 입력이 있는 경우에만 실행
                {
                    var key = Console.ReadKey(true).Key; // 눌린 키를 읽어옴
    
                    switch (key) // 키에 따라 뱀의 방향을 변경
                    {
                        case ConsoleKey.UpArrow:
                            snake.direction = Direction.UP;
                            break;
                        case ConsoleKey.DownArrow:
                            snake.direction = Direction.DOWN;
                            break;
                        case ConsoleKey.LeftArrow:
                            snake.direction = Direction.LEFT;
                            break;
                        case ConsoleKey.RightArrow:
                            snake.direction = Direction.RIGHT;
                            break;
                    }
                }
    
                if (snake.Eat(food)) // 뱀이 음식을 먹었는지 확인
                {
                    foodCount++; // 먹은 음식 수 증가
                    food.Draw(); // 음식을 다시 그림
    
                    food = foodCreator.CreateFood(); // 새로운 음식 생성
                    food.Draw(); // 새로운 음식을 그림
    
                    if (gameSpeed > 10) // 게임 속도 조절: 점점 빨라짐
                    {
                        gameSpeed -= 10;
                    }
                }
                else // 뱀이 음식을 먹지 않은 경우
                {
                    snake.Move(); // 뱀 이동
                }
    
                Thread.Sleep(gameSpeed); // 일정 시간만큼 대기
    
                if (snake.IsHitTail() || snake.IsHitWall()) // 충돌 체크
                {
                    break; // 뱀 충돌 시 게임 루프 종료
                }
    
                Console.SetCursorPosition(0, 21); // 커서 위치 설정
                Console.WriteLine($"먹은 음식 수: {foodCount}"); // 먹은 음식 수 출력
            }
    
            WriteGameOver(); // 게임이 끝나면 게임 오버 메시지 출력
            Console.ReadLine();
        }
    
        static void WriteGameOver() // 게임 오버 메시지 출력 메서드
        {
            int xOffset = 25;
            int yOffset = 22;
            Console.SetCursorPosition(xOffset, yOffset++);
            WriteText("============================", xOffset, yOffset++);
            WriteText("         GAME OVER", xOffset, yOffset++);
            WriteText("============================", xOffset, yOffset++);
        }
    
        static void WriteText(string text, int xOffset, int yOffset) // 텍스트 출력 메서드
        {
            Console.SetCursorPosition(xOffset, yOffset);
            Console.WriteLine(text);
        }
    
        static void DrawWalls() // 벽을 그리는 메서드
        {
            for (int i = 0; i < 80; i++) // 상하 벽
            {
                Console.SetCursorPosition(i, 0);
                Console.Write("#");
                Console.SetCursorPosition(i, 20);
                Console.Write("#");
            }
    
            for (int i = 0; i < 20; i++) // 좌우 벽
            {
                Console.SetCursorPosition(0, i);
                Console.Write("#");
                Console.SetCursorPosition(80, i);
                Console.Write("#");
            }
        }
    }
    
    // 게임 화면의 좌표, 모양 정보 나타내는 클래스
    public class Point
    {
        public int x { get; set; } // x 좌표
        public int y { get; set; } // y 좌표
        public char sym { get; set; } // 그려질 모양
    
        // 생성자: 초기 좌표, 모양 설정
        public Point(int _x, int _y, char _sym)
        {
            x = _x;
            y = _y;
            sym = _sym;
        }
    
        // 해당 좌표에 모양 그리는 메서드
        public void Draw()
        {
            Console.SetCursorPosition(x, y);
            Console.Write(sym);
        }
    
        // 해당 좌표에 그려진 모양 지우는 메서드
        public void Clear()
        {
            sym = ' ';
            Draw();
        }
    
        // 두 점의 충돌을 확인하는 메서드
        public bool IsHit(Point p)
        {
            return p.x == x && p.y == y;
        }
    }
    
    // 뱀의 이동 방향을 나타내는 열거형
    public enum Direction
    {
        LEFT,
        RIGHT,
        UP,
        DOWN
    }
    
    // 뱀을 관리하고 동작시키는 클래스
    public class Snake
    {
        public List<Point> body; // 뱀의 몸통을 저장하는 리스트
        public Direction direction; // 현재 뱀의 이동 방향
    
        // 생성자: 초기 위치와 길이, 초기 방향으로 뱀 생성
        public Snake(Point tail, int length, Direction _direction)
        {
            direction = _direction;
            body = new List<Point>();
            for (int i = 0; i < length; i++)
            {
                Point p = new Point(tail.x, tail.y, '*');
                body.Add(p);
                tail.x += 1;
            }
        }
    
        // 뱀의 몸통을 화면에 그리는 메서드
        public void Draw()
        {
            foreach (Point p in body)
            {
                p.Draw();
            }
        }
    
        // 뱀이 음식을 먹었는지 확인하고 처리하는 메서드
        public bool Eat(Point food)
        {
            Point head = GetNextPoint();
            if (head.IsHit(food))
            {
                food.sym = head.sym;
                body.Add(food);
                return true;
            }
            else
            {
                return false;
            }
        }
    
        // 뱀을 이동시키는 메서드
        public void Move()
        {
            Point tail = body.First();
            body.Remove(tail);
            Point head = GetNextPoint();
            body.Add(head);
    
            tail.Clear();
            head.Draw();
        }
    
        // 다음에 이동할 위치를 계산하는 메서드
        public Point GetNextPoint()
        {
            Point head = body.Last();
            Point nextPoint = new Point(head.x, head.y, head.sym);
            switch (direction)
            {
                case Direction.LEFT:
                    nextPoint.x -= 2;
                    break;
                case Direction.RIGHT:
                    nextPoint.x += 2;
                    break;
                case Direction.UP:
                    nextPoint.y -= 1;
                    break;
                case Direction.DOWN:
                    nextPoint.y += 1;
                    break;
            }
            return nextPoint;
        }
    
        // 뱀의 머리가 몸통과 충돌했는지 확인하는 메서드
        public bool IsHitTail()
        {
            var head = body.Last();
            for (int i = 0; i < body.Count - 2; i++)
            {
                if (head.IsHit(body[i]))
                    return true;
            }
            return false;
        }
    
        // 뱀이 벽에 충돌했는지 확인하는 메서드
        public bool IsHitWall()
        {
            var head = body.Last();
            if (head.x <= 0 || head.x >= 80 || head.y <= 0 || head.y >= 20)
                return true;
            return false;
        }
    }
    
    // 음식을 생성하고 관리하는 클래스
    public class FoodCreator
    {
        int mapWidth;
        int mapHeight;
        char sym;
    
        Random random = new Random();
    
        // 생성자: 음식의 생성 범위와 모양을 설정
        public FoodCreator(int mapWidth, int mapHeight, char sym)
        {
            this.mapWidth = mapWidth;
            this.mapHeight = mapHeight;
            this.sym = sym;
        }
    
        // 무작위 위치에 음식을 생성하는 메서드
        public Point CreateFood()
        {
            int x = random.Next(2, mapWidth - 2);
            x = x % 2 == 1 ? x : x + 1; // x 좌표를 2단위로 맞춤
            int y = random.Next(2, mapHeight - 2);
            return new Point(x, y, sym);
        }
    }

     

    게임 화면

     

     

    'C#' 카테고리의 다른 글

    C# #9 알고리즘 기초 2  (0) 2023.08.19
    C# #8 TextRPG 1  (0) 2023.08.18
    C# #6 알고리즘 기초  (0) 2023.08.17
    C# #5 클래스, 객체, 상속, 다형성  (0) 2023.08.16
    C# #4 메서드와 구조체  (0) 2023.08.15
    댓글