본문 바로가기

C#/기본 문법

C# TextRPG2

이전 TextRPG와 다르게 객체지향으로 설계

- 코드가 깔끔해짐

- 확장성이 좋음

 

TextRPG에서는 절차지향으로의 제약사향이 있었음

1. 게임이 로비 -> 타운 -> 필드순으로 규칙적으로 진행

순서를 무조건 가정하고 진행했음

로비 다음은 타운이고, 타운 다음은 필드라고 생각했음(절차지향)

 

객체지향에서는 순서를 가정하지 않고 개발함, 다음에 갈 곳을 정해주기만 하면 됨

Game 매니저 클래스를 만들고 mode 맴버 변수를 활용하여 switch 안에서 각각의 케이스만 추가해주면 된다

먄약, 기획자가 로비에서 캐릭터를 고르고 바로 필드로 가게 해달라고 한다면

객체지향에서는 다음에 갈 곳만 mode 값으로 지정해주면 됨

 

로직

Main() 함수에서 while문으로 Game 클래스의 Process() 함수를 호출

계속 반복되고 switch 안에서 맴버 mode를 통해 어떻게 동작할지 달라짐

맴버 mode가 값이 변경된다면 switch 안에서 다르게 동작할 것임

 

 

 

 

파일 구성

 


- Creature.cs

클래스 : Creature

Player와 Monster의 최상위 클래스 Creature

필드 : type(Player or Monster), hp, attack

함수 : SetInfo(hp, attack), GetHp(), GetAttack(), isDead(), OnDamage()

 

- Game.cs

클래스 :  Game

게임진행 및 전반적인 사항 진행

필드 : mode(None, Lobby, Town, Field), player, monster, rand

함수 : Process(), ProcessLobby(), ProcessTown(), ProcessField(), TryEscape(), ProcessFight(), CreateRandomMonster()

 

- Monster.cs

클래스 :  Monster(Creature 상속)

몬스터

필드 :  type(None, Slime, Orc, Skeleton)

함수 : GetMonsterType()

 


클래스 :  Slime(Monster 상속)

슬라임

 

클래스 :  Orc(Monster 상속)

오크

 

클래스 :  Skeleton(Monster 상속)

스켈레톤

 

 

- Player.cs

클래스 :  Player(Creature 상속)

몬스터

필드 :  type(None, Knight, Archer, Mage)

함수 : GetPlayerType()

 


클래스 :  Knight(Monster 상속)

기사

 

클래스 :  Archer(Monster 상속)

궁수

 

클래스 :  Mage(Monster 상속)

마법사

 

 

- Program.cs

Main() 함수로 진행

Game 클래스로 객체 생성하여 Process() 함수 호출하여 게임 진행

 

 

전체 코드


 

- Creature.cs

namespace Rookiss_CSharp
{
    public enum CreatureType // 크리처가 플레이어인지, 몬스터 타입인지
    {
        None,
        Player = 1,
        Monster = 2
    }


    // Creature : 살아있는 생물
    class Creature
    {
        protected CreatureType type;
        protected int hp = 0;
        protected int attack = 0;

        protected Creature(CreatureType type)
        {
            this.type = type;
        }

        public void SetInfo(int hp, int attack)
        {
            this.hp = hp;
            this.attack = attack;
        }

        public int GetHp() { return hp; }
        public int GetAttack() { return attack; }

        public bool isDead() { return hp <= 0; }

        public void OnDamaged(int damage)
        {
            hp -= damage;
            if (hp < 0)
                hp = 0;
        }
    }
}

 

 

- Game.cs

namespace Rookiss_CSharp
{
    public enum GameMode
    {
        None,
        Lobby,
        Town,
        Field
    }

    // 게임진행 및 전반적인 사항 진행
    class Game
    {
        private GameMode mode = GameMode.Lobby;
        private Player player = null;
        private Monster monster = null;
        private Random rand = new Random();

        public void Process()
        {
            switch (mode)
            {
                case GameMode.Lobby:
                    ProcessLobby();
                    break;
                case GameMode.Town:
                    ProcessTown();
                    break;
                case GameMode.Field:
                    ProcessField();
                    break;
            }
        }

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

            string input = Console.ReadLine();
            switch (input)
            {
                case "1":
                    player = new Knight();
                    mode = GameMode.Town;
                    break;
                case "2":
                    player = new Archer();
                    mode = GameMode.Town;
                    break;
                case "3":
                    player = new Mage();
                    mode = GameMode.Town;
                    break;
            }
        }

        private void ProcessTown()
        {
            Console.WriteLine("마을에 입장했습니다.");
            Console.WriteLine("[1] 필드로 가기");
            Console.WriteLine("[2] 로비로 돌아가기");

            string input = Console.ReadLine();
            switch (input)
            {
                case "1":
                    mode = GameMode.Field;
                    break;
                case "2":
                    mode = GameMode.Lobby;
                    break;
            }
        }

        private void ProcessField()
        {
            Console.WriteLine("필드에 입장했습니다.");
            Console.WriteLine("[1] 싸우기");
            Console.WriteLine("[2] 일정 확률로 마을 돌아가기");

            CreateRandomMonster();

            string input = Console.ReadLine();
            switch (input)
            {
                case "1":
                    ProcessFight();
                    break;
                case "2":
                    TryEscape();
                    break;
            }
        }

        private void TryEscape()
        {
            int randValue = rand.Next(0, 101); // 0 ~ 100
            if (randValue < 33) // 33% 미만이면 마을 돌아가기
            {
                mode = GameMode.Town;
            }
            else // 그 외에는 실패 : 싸우기
            {
                ProcessFight();
            }
        }

        private void ProcessFight()
        {
            while (true)
            {
                int damage = player.GetAttack();
                monster.OnDamaged(damage);
                if (monster.isDead())
                {
                    Console.WriteLine("승리했습니다.");
                    Console.WriteLine($"남은 체력: {player.GetHp()}");
                    break;
                }

                damage = monster.GetAttack();
                player.OnDamaged(damage);
                if (player.isDead())
                {
                    Console.WriteLine("패배했습니다.");
                    mode = GameMode.Lobby;
                    break;
                }
            }
        }

        private void CreateRandomMonster()
        {
            int randValue = rand.Next(0, 3); // 0, 1, 2
            switch (randValue)
            {
                case 0:
                    monster = new Slime();
                    Console.WriteLine("슬라임이 생성되었습니다.");
                    break;
                case 1:
                    monster = new Orc();
                    Console.WriteLine("오크가 생성되었습니다.");
                    break;
                case 2:
                    monster = new Skeleton();
                    Console.WriteLine("스켈레톤이 생성되었습니다.");
                    break;
            }
        }
    }
}

 

 

Monster.cs

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

    class Monster : Creature
    {
        protected MonsterType type = MonsterType.None;

        protected Monster(MonsterType type) : base(CreatureType.Monster)
        {
            this.type = type;
        }

        public MonsterType GetMonsterType() { return type; }
    }

    class Slime : Monster
    {
        public Slime() : base(MonsterType.Slime)
        {
            SetInfo(10, 10);
        }
    }

    class Orc : Monster
    {
        public Orc() : base(MonsterType.Orc)
        {
            SetInfo(20, 15);
        }
    }

    class Skeleton : Monster
    {
        public Skeleton() : base(MonsterType.Skeleton)
        {
            SetInfo(15, 25);
        }
    }
}

 

 

- Player.cs

namespace Rookiss_CSharp
{
    public enum PlayerType
    {
        None = 0,
        Knight = 1,
        Archer = 2,
        Mage = 3
    }

    class Player : Creature
    {
        // 네이밍 컨벤션(naming Convention)
        // 맴버 변수명 명명 규칙이 프로젝트마다 굉장히 다름(_변수명으로 하는 곳도 있고 맴버 변수라고 m_변수명 하는 곳도 있음)
        // _변수명할시 _로 보면 내부 변수임을 확인이 용이함

        // protected PlayerType _type;
        protected PlayerType type = PlayerType.None;

        protected Player(PlayerType type) : base(CreatureType.Player) // 외부에서 Player 자체를 생성할 수 없도록(외부에서 파생 클래스로 생성해야함) protected로 하고, PlayerType을 무조건 가지도록 생성자 파라미터 받음
        {
            // _type = type;
            this.type = type;
        }

        public PlayerType GetPlayerType() { return type; }
    }

    class Knight : Player
    {
        public Knight() : base(PlayerType.Knight)
        {
            SetInfo(100, 10);
        }
    }

    class Archer : Player
    {
        public Archer() : base(PlayerType.Archer)
        {
            SetInfo(75, 12);
        }
    }

    class Mage : Player
    {
        public Mage() : base(PlayerType.Mage)
        {
            SetInfo(50, 15);
        }
    }
}

 

 

 

- Program.cs

namespace Rookiss_CSharp
{
    class Program()
    {
        static void Main(string[] args)
        {
            // C++은 .h include 해줘야 하는데, C#은 동일한 네임스페이스를 사용한다면 타 .cs 파일에 있어도 불러올 수 있음
            // Player player = new Knight();

            Game game = new Game();

            while (true)
            {
                game.Process();
            }
        }
    }
}

 


 

 

 

출처


https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part1?srsltid=AfmBOopkXVxKs-MQPYZkTjEYW-yIDMO5xP7l5hQq0hQasUWhobvyKO6N

 

[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문| Rookiss - 인프런 강

현재 평점 4.9점 수강생 6,987명인 강의를 만나보세요. 기초 프로그래밍 지식이 없는 사람들을 위한 C# 프로그래밍 기초 강의입니다. 문법 암기 위주의 수업이 아니라, 최대한 필요한 부분만을 요

www.inflearn.com

45강 TextRPG2

'C# > 기본 문법' 카테고리의 다른 글

C# 배열 연습문제  (0) 2025.11.18
C# 배열  (0) 2025.11.13
C# 접근 제한자 , 어셈블리란?  (0) 2025.11.05
C# 문자열 둘러보기  (0) 2025.11.05
C# 다형성  (0) 2025.11.05