Chapter 4. Text RPG

Date:     Updated:

카테고리:

태그:

인프런에 있는 Rookiss님의 강의 Part1: C# 기초 프로그래밍 입문 를 듣고 정리한 필기입니다. 😀

👱‍♀️ 직업 고르기

using System;

namespace CSharp
{
    class Program
    {
        enum ClassType
        {
            None = 0,
            Knight = 1,
            Archer = 2,
            Mage = 3
        }

        static ClassType ChooseClass()
        {
            Console.WriteLine("직업을 선택하세요");
            Console.WriteLine("[1] 기사");
            Console.WriteLine("[2] 궁수");
            Console.WriteLine("[3] 법사");

            ClassType choice = ClassType.None;
            string input = Console.ReadLine(); 
            switch (input)
            {
                case "1":
                    choice = ClassType.Knight;
                    break;
                case "2":
                    choice = ClassType.Archer;
                    break;
                case "3":
                    choice = ClassType.Mage;
                    break;
            }

            return choice;
        }

        static void Main(string[] args)
        {
            while (true)
            {
                ClassType choice;   // 초기화 하지 않으면 자동으로 첫 번째 상수인 ClassType.None 으로 초기화 된다.
                choice = ChooseClass(); // static함수라 클래스 이름으로 호출하는 Program.ChooseClass(); 와도 같다. 
                if (choice != ClassType.None)  // 함수 내부에서 switch 문에 걸리지 않았다면, 즉 123 입력을 하지 않았다면 다시 반복. 제대로 123 중 하나 입력 했다면 직업 선택 그만함.
                    break; 
            }
        }
    }
}
  • ChooseClass()가 일반 멤버 함수 였다면
    • 같은 Program 클래스 내에 있더라도 메인 함수에서 ChooseClass(); 이렇게 호출하는건 불가능하다.
      • 왜냐면 메인 함수는 static 함수이기 때문에 어디서든 Program 객체 생성 없이 바로 Program 이름으로 호출이 가능한데 ChooseClass()는 일반 멤버 함수이므로 객체 생성이 있어야하지만 가능하기 때문이다.
      • 따라서 Program 타입의 인스턴스를 만들고 인스턴스를 통해 호출해야 한다.
    • 메인 함수 같은 static 함수가 아닌 같은 일반 멤버 함수 내에서 호출된거라면 같은 클래스니까 ChooseClass(); 호출 가능
  • ChooseClass()가 static 함수여서
    • 같은 static 함수인 ChooseClass()도 객체 생성이 필요 없기 때문에 그냥 메인 함수 내에서 ChooseClass(); 이렇게 호출이 가능한 것이다.
    • 정석대로 클래스 이름 호출로 Program.ChooseClass(); 이렇게도 가능하다. 근데 같은 클래스 내부니까 클래스 이름 생략 가능.


👩🏼 플레이어 생성

    class Program
    {
        struct Player
        {
            public int hp;
            public int attack;
            public ClassType type;
        }

        static void CreatePlayer(ClassType choice, out Player player)
        {
            player.type = choice;

            // 기사(100, 10) 궁수(75, 12) 법사(50, 15)
            switch (choice)
            {
                case ClassType.Knight:
                    player.hp = 100;
                    player.attack = 10;
                    break;
                case ClassType.Archer:
                    player.hp = 75;
                    player.attack = 12;
                    break;
                case ClassType.Mage:
                    player.hp = 50;
                    player.attack = 15;
                    break;
                default:
                    player.hp = 0;
                    player.attack = 0;
                    break;
            }
        }

        static void Main(string[] args)
        {
            while (true)
            {
                // 직업 고르기
                ClassType choice;
                choice = ChooseClass(); // Program.ChooseClass();
                if (choice != ClassType.None)
                {
                    // 플레이어 캐릭터 생성
                    Player player;
                    CreatePlayer(choice, out player);
                    Console.WriteLine($"HP{player.hp}, Attack{player.attack}");
                }
            }
        }
    }
  • 플레이어 특성 구조체로 관리
    • hp, attack 만 사용했지만 mp, region 등등 많은 특성이 생길 수 있다.
    • 그래서 일일이 다 변수로 선언하기 보단 구조체로 묶어서 관리하는게 좋다.
  • 구조체 변수들도 접근 지정자가 있다. 지정 안해주면 디폴트로 private이 되서 player.hp 이렇게 호출할 수가 없다.
  • 구조체 매개 변수를 out으로 하면 구조체의 모든 멤버 변수들을 전부 다 빠짐 없이 write 해야 한다.
    • out은 엄격하기 때문에 case 에 걸리지 않아 write 되지 않을 것도 생각해서인지 모든 case에 대해 write 해주지 않으면 컴파일 오류 난다. default에도 꼭 write 해주어야 한다.
Player player;
CreatePlayer(choice, out player);

아무런 초기화 되어 있지 않던 player가 CreatePlayer 함수 호출이 끝난 후 멤버 변수들이 전부 다 설정된다! 초기화 안되 있었던 상태니, 즉 메모리 할당이 되어 있지 않은 상태라 ref는 쓸 수 없다. out은 메모리 할당 안되어 있는 변수를 참조하는 것이라도 함수 내에서 write 할 것이라는게 보장 되어 있기 때문에 문제 되지 않는다.


👩🏼 몬스터 생성

        enum MonsterType
        {
            None = 0,
            Slime = 1,
            Orc = 2,
            Skeleton = 3
        }

        struct Monster
        {
            public int hp;
            public int attack;
        }

        static void CreateRandomMonster(out Monster monster)
        {
            Random rand = new Random();
            int randMonster = rand.Next(1, 4); // 1 ~ 3 중 랜덤 정수 리턴

            switch(randMonster)
            {
                case (int)MonsterType.Slime:
                    Console.WriteLine("슬라임이 스폰 되었습니다!");
                    monster.hp = 20;
                    monster.attack = 2;
                    break;
                case (int)MonsterType.Orc:
                    Console.WriteLine("오크가 스폰 되었습니다!");
                    monster.hp = 40;
                    monster.attack = 4;
                    break;
                case (int)MonsterType.Skeleton:
                    Console.WriteLine("스켈레톤이 스폰 되었습니다!");
                    monster.hp = 30;
                    monster.attack = 3;
                    break;
                default:
                    monster.hp = 0;
                    monster.attack = 0;
                    break;
            }
        }

        static void EnterField()
        {
            Console.WriteLine("필드에 접속했습니다.");

            // 랜덤으로 1~3 몬스터 중 하나를 리스폰
            Monster monster;
            CreateRandomMonster(out monster);
        }

        static void EnterGame()
        {
            while(true)
            {
                Console.WriteLine("마을에 접속했습니다.");
                Console.WriteLine("[1] 월드로 간다.");
                Console.WriteLine("[2] 로비로 돌아가기.");

                String input = Console.ReadLine();

                switch (input)
                {
                    case "1":
                        EnterField();
                        break;
                    case "2":
                        return;
                }
            }
        }

        static void Main(string[] args)
        {
            while (true)
            {
                // 직업 고르기
                ClassType choice;
                choice = ChooseClass(); // Program.ChooseClass();
                if (choice != ClassType.None)
                {
                    // 플레이어 캐릭터 생성
                    Player player;
                    CreatePlayer(choice, out player);
                    Console.WriteLine($"HP{player.hp}, Attack{player.attack}");

                    // 게임 시작! 몬스터 생성 및 전투
                    EnterGame();
                }
            }
        }
    }

        static void CreateRandomMonster(out Monster monster)
        {
            Random rand = new Random();
            int randMonster = rand.Next(1, 4); // 1 ~ 3 중 랜덤 정수 리턴

            switch(randMonster)
            {
                case (int)MonsterType.Slime:
  • randMonsterint형이기 때문에 case문에서 (int)MonsterType.Slime 이렇게 형변환 해주어야 한다.
  • monster 구조체의 모든 멤버 변수들을 전부 다 write 해야 한다. write 되지 않을 케이스가 있어서도 안된다.
        static void EnterGame()
        {
            while(true)
            {
                Console.WriteLine("마을에 접속했습니다.");
                Console.WriteLine("[1] 월드로 간다.");
                Console.WriteLine("[2] 로비로 돌아가기.");

                String input = Console.ReadLine();

                switch (input)
                {
                    case "1":
                        EnterField();
                        break;
                    case "2":
                        return;
                }
            }
        }

2를 선택하면 로비로 돌아가게 하고 싶다. 즉, 다시 직업을 고르도록 Main 함수로 돌아가고 싶다. 따라서 case "2"의 경우엔 break가 아닌 그냥 return을 해주어 아예 EnterGame 함수를 종료시킨다.


👩🏼 전투

        static void Fight(ref Player player, ref Monster monster)
        {
            while(true)
            {
                // 플레이어가 몬스터 공격
                monster.hp -= player.attack;
                if (monster.hp <= 0)
                {
                    Console.WriteLine("승리했습니다!");
                    Console.WriteLine($"남은 체력 : {player.hp}");
                    break;
                }

                // 몬스터 반격
                player.hp -= monster.attack;
                if (player.hp <= 0)
                {
                    Console.WriteLine("패배했습니다!");
                    break;
                }
            }
        }

        static void EnterField(ref Player player)
        {
            Console.WriteLine("필드에 접속했습니다.");

            // 랜덤으로 1~3 몬스터 중 하나를 리스폰
            Monster monster;
            CreateRandomMonster(out monster);

            Console.WriteLine("[1] 전투 모드 돌입");
            Console.WriteLine("[2] 일정 확률로 마을로 도망");

            string input = Console.ReadLine();
            if (input == "1")
            {
                Fight(ref player, ref monster);
            }
            else if (input == "2")
            {
                // 33 %
                Random rand = new Random();
                int randValue = rand.Next(0, 101);
                if (randValue <= 33)
                {
                    Console.WriteLine("도망치는데 성공했습니다!");
                }
                else
                {
                    Fight(ref player, ref monster);
                }
            }

        }

        static void EnterGame(ref Player player)
        {
            while(true)
            {
                Console.WriteLine("마을에 접속했습니다.");
                Console.WriteLine("[1] 월드로 간다.");
                Console.WriteLine("[2] 로비로 돌아가기.");

                String input = Console.ReadLine();

                switch (input)
                {
                    case "1":
                        EnterField(ref player);
                        break;
                    case "2":
                        return;
                }
            }
        }

        static void Main(string[] args)
        {
            while (true)
            {
                // 직업 고르기
                ClassType choice;
                choice = ChooseClass(); // Program.ChooseClass();
                if (choice == ClassType.None)
                    continue;

                // 플레이어 캐릭터 생성
                Player player;
                CreatePlayer(choice, out player);
                Console.WriteLine($"HP{player.hp}, Attack{player.attack}");
                
                // 게임 시작! 몬스터 생성 및 전투
                EnterGame(ref player);
            }
        }
    }

Fight 함수에서 player 구조체가 필요하기 때문에 인수로 전달 받아야 한다. Call by Value로 해도 나쁘지 않지만, EnterGame 👉 EnterField 👉 Figt 이 순으로 호출되며 player를 전달해야 하기 때문에 그냥 계속 해서 사본 만드는 과정을 거치기 보다는 사본 생성 과정 없이 ref로 원본 player를 참조할 수 있도록 하였다.

Player player;
CreatePlayer(choice, out player);

위의 과정으로 player는 현재 메모리가 할당 되어 있는 상태기 때문에 ref 참조로 넘길 수 있다.


👩‍🦰 전체 코드

using System;

namespace CSharp
{
    class Program
    {
        enum ClassType
        {
            None = 0,
            Knight = 1,
            Archer = 2,
            Mage = 3
        }

        struct Player
        {
            public int hp;
            public int attack;
            public ClassType type;
        }

        enum MonsterType
        {
            None = 0,
            Slime = 1,
            Orc = 2,
            Skeleton = 3
        }

        struct Monster
        {
            public int hp;
            public int attack;
        }

        static ClassType ChooseClass()
        {
            Console.WriteLine("직업을 선택하세요");
            Console.WriteLine("[1] 기사");
            Console.WriteLine("[2] 궁수");
            Console.WriteLine("[3] 법사");

            ClassType choice = ClassType.None;
            string input = Console.ReadLine();
            switch (input)
            {
                case "1":
                    choice = ClassType.Knight;
                    break;
                case "2":
                    choice = ClassType.Archer;
                    break;
                case "3":
                    choice = ClassType.Mage;
                    break;
            }

            return choice;
        }

        static void CreatePlayer(ClassType choice, out Player player)
        {
            player.type = choice;

            // 기사(100, 10) 궁수(75, 12) 법사(50, 15)
            switch (choice)
            {
                case ClassType.Knight:
                    player.hp = 100;
                    player.attack = 10;
                    break;
                case ClassType.Archer:
                    player.hp = 75;
                    player.attack = 12;
                    break;
                case ClassType.Mage:
                    player.hp = 50;
                    player.attack = 15;
                    break;
                default:
                    player.hp = 0;
                    player.attack = 0;
                    break;
            }
        }

        static void CreateRandomMonster(out Monster monster)
        {
            Random rand = new Random();
            int randMonster = rand.Next(1, 4); // 1 ~ 3 중 랜덤 정수 리턴

            switch(randMonster)
            {
                case (int)MonsterType.Slime:
                    Console.WriteLine("슬라임이 스폰 되었습니다!");
                    monster.hp = 20;
                    monster.attack = 2;
                    break;
                case (int)MonsterType.Orc:
                    Console.WriteLine("오크가 스폰 되었습니다!");
                    monster.hp = 40;
                    monster.attack = 4;
                    break;
                case (int)MonsterType.Skeleton:
                    Console.WriteLine("스켈레톤이 스폰 되었습니다!");
                    monster.hp = 30;
                    monster.attack = 3;
                    break;
                default:
                    monster.hp = 0;
                    monster.attack = 0;
                    break;
            }
        }

        static void Fight(ref Player player, ref Monster monster)
        {
            while(true)
            {
                // 플레이어가 몬스터 공격
                monster.hp -= player.attack;
                if (monster.hp <= 0)
                {
                    Console.WriteLine("승리했습니다!");
                    Console.WriteLine($"남은 체력 : {player.hp}");
                    break;
                }

                // 몬스터 반격
                player.hp -= monster.attack;
                if (player.hp <= 0)
                {
                    Console.WriteLine("패배했습니다!");
                    break;
                }
            }
        }

        static void EnterField(ref Player player)
        {
            Console.WriteLine("필드에 접속했습니다.");

            // 랜덤으로 1~3 몬스터 중 하나를 리스폰
            Monster monster;
            CreateRandomMonster(out monster);

            Console.WriteLine("[1] 전투 모드 돌입");
            Console.WriteLine("[2] 일정 확률로 마을로 도망");

            string input = Console.ReadLine();
            if (input == "1")
            {
                Fight(ref player, ref monster);
            }
            else if (input == "2")
            {
                // 33 %
                Random rand = new Random();
                int randValue = rand.Next(0, 101);
                if (randValue <= 33)
                {
                    Console.WriteLine("도망치는데 성공했습니다!");
                }
                else
                {
                    Fight(ref player, ref monster);
                }
            }

        }

        static void EnterGame(ref Player player)
        {
            while(true)
            {
                Console.WriteLine("마을에 접속했습니다.");
                Console.WriteLine("[1] 월드로 간다.");
                Console.WriteLine("[2] 로비로 돌아가기.");

                String input = Console.ReadLine();

                switch (input)
                {
                    case "1":
                        EnterField(ref player);
                        break;
                    case "2":
                        return;
                }
            }
        }

        static void Main(string[] args)
        {
            while (true)
            {
                // 직업 고르기
                ClassType choice;
                choice = ChooseClass(); // Program.ChooseClass();
                if (choice == ClassType.None)
                    continue;

                // 플레이어 캐릭터 생성
                Player player;
                CreatePlayer(choice, out player);
                Console.WriteLine($"HP{player.hp}, Attack{player.attack}");

                // 게임 시작! 몬스터 생성 및 전투
                EnterGame(ref player);
            }
        }
    }
}



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

C Sharp 카테고리 내 다른 글 보러가기

댓글 남기기