내일배움캠프/TIL

내일배움캠프 5일차 TIL - 텍스트 RPG, 캐릭터 생성까지

서보훈 2024. 9. 19. 20:24

 

 

C# 을 사용해서 텍스트 RPG게임을 만드는 개인프로젝트 주간입니다.

오늘은 그 시작으로 캐릭터를 생성하고, 게임시작 텍스트를 띄우는데 까지 구현하였습니다.

 

자세한 내용입니다.

 


캐릭터 정보를 저장하는 클래스 Character

가장 먼저, 별개의 클래스로 Character 클래스를 만들어서 이 클래스에 캐릭터에 관련된 모든 내용을 저장할 예정입니다.

 

namespace TextRPG_SBH
{
    public enum Jobs
    {
        Warrior,
        Wizard,
        Theif,
        Archer
    }

    internal class Character
    {
        //플레이어 이름
        public string UserName { get; private set; }

        //플레이어 직업
        public Jobs PlayerJobs { get; private set; }

        //플레이어의 현재 레벨
        public int Level { get; private set; }

        //초기 공격력
        private float baseAttack;
        //현재 공격력
        public float AttackPoint { get; private set; }

        //초기 방어력
        private float baseDefence;
        //현재 방어력
        public float DefencePoint { get; private set; }

        //초기 체력, 최대체력
        private float baseHealth;
        private float maxHealth;
        //현재 체력
        public float HealthPoint { get; private set; }
        //골드
        public int PlayerGold { get; private set; }
        //경험치
        public int Exp { get; private set; }

        //생성자, 게임 시작시 이름과 직업을 고르고 그에 맞게 캐릭터 생성
        public Character()
        {
            UserName = "User";

            Level = 1;
            PlayerGold = 100;
            Exp = 0;
        }

        public void ExpUp(int expAmount)
        {
            Exp += expAmount;
            //내 레벨보다 경험치가 높아지면 레벨업
            if(Exp >= Level)
            {
                //레벨업에 경험치 소모
                Exp -= Level;
                LevelUp();
            }
        }

        public void LevelUp()
        {
            Level ++;

            baseAttack += 0.5f;
            baseDefence += 1f;
        }

        public void SetBaseStatus(string name, Jobs job)
        {
            UserName = name;

            switch (job)
            {
                case Jobs.Warrior:
                    baseAttack = 10;
                    baseDefence = 7;
                    baseHealth = 120;
                    break;
                case Jobs.Wizard:
                    baseAttack = 17;
                    baseDefence = 4;
                    baseHealth = 80;
                    break;
                case Jobs.Theif:
                    baseAttack = 13;
                    baseDefence = 6;
                    baseHealth = 100;
                    break;
                case Jobs.Archer:
                    baseAttack = 15;
                    baseDefence = 5;
                    baseHealth = 90;
                    break;
            }
        }
    }
}

 

일단 가장 위, 열거형 클래스를 통해 캐릭터가 가질 수 있는 직업을 만들어주었습니다.

public enum Jobs
{
    Warrior,
    Wizard,
    Theif,
    Archer
}

현재 4가지 직업을 만들어준 상태입니다.

상대적으로 근거리인 전사와 도적은 방어적인 성능을 조금 더 가지게

원거리인 마법사와 궁수는 공격적인 성능을 가지게 구성할 예정입니다.

 

이후 스텟에 사용할 필드를 선언해주었습니다.

//플레이어 이름
public string UserName { get; private set; }

//플레이어 직업
public Jobs PlayerJobs { get; private set; }

//플레이어의 현재 레벨
public int Level { get; private set; }

//초기 공격력
private float baseAttack;
//현재 공격력
public float AttackPoint { get; private set; }

//초기 방어력
private float baseDefence;
//현재 방어력
public float DefencePoint { get; private set; }

//초기 체력, 최대체력
private float baseHealth;
private float maxHealth;
//현재 체력
public float HealthPoint { get; private set; }
//골드
public int PlayerGold { get; private set; }
//경험치
public int Exp { get; private set; }

필드의 경우, 외부에서 수정을 피하기위해 외부에서 사용할 예정인 필드를 모두 속성으로 만들고, 기본이 될 스텟을 private로 선언하여 외부에서 수정이 불가능하도록 만들었습니다.

 

생성자입니다.

//생성자, 게임 시작시 이름과 직업을 고르고 그에 맞게 캐릭터 생성
public Character()
{
    UserName = "User";

    Level = 1;
    PlayerGold = 100;
    Exp = 0;
}

게임에서 캐릭터를 만듬과 동시에, 레벨, 골드, 경험치 량이 저장되도록 만들어주었습니다.

공격력, 방어력, 체력등은 직업을 선택함과 동시에 결정됨으로 생성자를 통해 정하지 않고, 모든 캐릭터가 동일하게 시작하는 내용만 생성자로 정해지도록 만들어주었습니다.

 

아직은 사용되지 않는 레벨업과 관련된 함수입니다.

public void ExpUp(int expAmount)
{
    Exp += expAmount;
    //내 레벨보다 경험치가 높아지면 레벨업
    if(Exp >= Level)
    {
        //레벨업에 경험치 소모
        Exp -= Level;
        LevelUp();
    }
}

public void LevelUp()
{
    Level ++;

    baseAttack += 0.5f;
    baseDefence += 1f;
}

경험치를 지정하는 속성의 Set값이 private이기 때문에 public 함수 호출함으로써 경험치를 획득합니다.

또한 레벨업은 임시로 레벨만큼의 경험치를 얻으면 레벨업을 하도록 만들었으며, 이후 달라질수도 있습니다.

 

캐릭터의 스텟을 정하는 함수입니다.

public void SetBaseStatus(string name, Jobs job)
{
    UserName = name;

    switch (job)
    {
        case Jobs.Warrior:
            baseAttack = 10;
            baseDefence = 7;
            baseHealth = 120;
            break;
        case Jobs.Wizard:
            baseAttack = 17;
            baseDefence = 4;
            baseHealth = 80;
            break;
        case Jobs.Theif:
            baseAttack = 13;
            baseDefence = 6;
            baseHealth = 100;
            break;
        case Jobs.Archer:
            baseAttack = 15;
            baseDefence = 5;
            baseHealth = 90;
            break;
    }
}

캐릭터의 직업을 정할때 이 내용을 호출하여 캐릭터의 기본 스텟이 정해질 예정입니다.

또한 이 함수를 통해 캐릭터에게 이름이 부여됩니다.


메인 클래스 - 게임진행

게임을 진행하는 클래스입니다.

이후 내용이 크게 변경될 수도 있습니다.

namespace TextRPG_SBH
{
    internal class Program
    {
        public static Character playerChar = new Character();
        public static ItemDatabase itemDb = new ItemDatabase();
        
        static void Main()
        {
            string inputName;
            int inputSelectedJop;
            string korJobName = "";
            itemDb.ResetItemList();

            bool isCharCreated = false;

            while (!isCharCreated)
            {
                Console.WriteLine("당신의 이름은?");
                inputName = Console.ReadLine();

                if (inputName == null)
                {
                    inputName = "User";
                }

                Console.Clear();
                Console.WriteLine("직업을 고르세요");
                Console.WriteLine("1. 전사    2. 마법사    3. 도적    4. 궁수    5. 직업설명");
                inputSelectedJop = InputInt(Console.ReadLine());

                switch (inputSelectedJop - 1)
                {
                    case 0:
                        playerChar.SetBaseStatus(inputName, Jobs.Warrior);
                        korJobName = "전사";
                        isCharCreated = true;
                        break;
                    case 1:
                        playerChar.SetBaseStatus(inputName, Jobs.Wizard);
                        korJobName = "마법사";
                        isCharCreated = true;
                        break;
                    case 2:
                        playerChar.SetBaseStatus(inputName, Jobs.Theif);
                        korJobName = "도적";
                        isCharCreated = true;
                        break;
                    case 3:
                        playerChar.SetBaseStatus(inputName, Jobs.Archer);
                        korJobName = "궁수";
                        isCharCreated = true;
                        break;
                    case 4:
                        Console.Clear();
                        Console.WriteLine("전사\n초기 체력 : 120 \n초기 공격력 : 10\n초기 방어력 : 7\n매우 높은 생존력");
                        Console.WriteLine();
                        Console.WriteLine("마법사\n초기 체력 : 80 \n초기 공격력 : 17\n초기 방어력 : 4\n매우 낮은 생존력과 매우 높은 공격력");
                        Console.WriteLine();
                        Console.WriteLine("도적\n초기 체력 : 100 \n초기 공격력 : 13\n초기 방어력 : 6\n평균적인 능력치");
                        Console.WriteLine();
                        Console.WriteLine("궁수\n초기 체력 : 90 \n초기 공격력 : 15\n초기 방어력 : 5\n낮은 생존력과 높은 공격력");
                        Console.WriteLine();
                        break;
                    default:
                        Console.Clear();
                        Console.WriteLine("잘못된 입력입니다.");
                        break;
                }

                if(isCharCreated == true)
                {
                    int isRight = 0;
                    while (isRight != 1)
                    {
                        Console.Clear();
                        Console.WriteLine($"당신의 이름 : {inputName}");
                        Console.WriteLine($"당신의 직업 : {korJobName}");

                        Console.WriteLine("이 내용이 맞습니까?");
                        Console.WriteLine("1. 예    2. 아니오");
                        isRight = InputInt(Console.ReadLine());
                        Console.Clear();

                        if (isRight == 1)
                        {
                            break;
                        }
                        else if (isRight == 2)
                        {
                            isCharCreated = false;
                            break;
                        }
                        else
                        {
                            Console.WriteLine("잘못된 입력입니다.");
                            Thread.Sleep(500);
                        }
                    }
                }
            }
            TextCreater("게임 시작");
            
        }

        //ReadLine 을 통해 int를 받을때 사용하는 함수
        //int 형이면 해당값 반환, 아니면 -1 반환
        private static int InputInt(string input)
        {
            int returnInt;

            if(int.TryParse(input, out returnInt))
            {
                return returnInt;
            }

            else
            {
                return -1;
            }
        }

        private static void TextCreater(string text)
        {
            for(int i = 0; i < text.Length; i++)
            {
                Console.Write(text[i]);
                Thread.Sleep(50);
            }

            Console.WriteLine();
        }
    }
}

 

먼저, 이 게임 내에서 계속 사용할 클래스들을 선언해주었습니다.

public static Character playerChar = new Character();
public static ItemDatabase itemDb = new ItemDatabase();

이 클래스가 static 클래스이기 때문에, 해당 클래스 변수를 static으로 선언해주어야 main 내에서 사용할 수 있습니다.

아이템 클래스는 이 클래스 이후 설명하겠습니다.

 

캐릭터를 생성할 때 만 사용되는 변수들을 지역변수로 선언해주었습니다

string inputName;
int inputSelectedJop;
string korJobName = "";

bool isCharCreated = false;

단, string korJobName은 이후 정보 표기시에 꾸준히 사용할 가능성이 있기 때문에, 해당내용을 Character 클래스로 옮길까 고민중입니다.

 

아이템 정보를 받아오는 함수입니다.

itemDb.ResetItemList();

아이템 데이터베이스 배열에, 아이템을 생성하여 배치하는 함수입니다.

이후 설명하겠습니다.

 

캐릭터를 생성하는 기능입니다.

while (!isCharCreated)
{
    Console.WriteLine("당신의 이름은?");
    inputName = Console.ReadLine();

    if (inputName == null)
    {
        inputName = "User";
    }

    Console.Clear();
    Console.WriteLine("직업을 고르세요");
    Console.WriteLine("1. 전사    2. 마법사    3. 도적    4. 궁수    5. 직업설명");
    inputSelectedJop = InputInt(Console.ReadLine());

    switch (inputSelectedJop - 1)
    {
        case 0:
            playerChar.SetBaseStatus(inputName, Jobs.Warrior);
            korJobName = "전사";
            isCharCreated = true;
            break;
        case 1:
            playerChar.SetBaseStatus(inputName, Jobs.Wizard);
            korJobName = "마법사";
            isCharCreated = true;
            break;
        case 2:
            playerChar.SetBaseStatus(inputName, Jobs.Theif);
            korJobName = "도적";
            isCharCreated = true;
            break;
        case 3:
            playerChar.SetBaseStatus(inputName, Jobs.Archer);
            korJobName = "궁수";
            isCharCreated = true;
            break;
        case 4:
            Console.Clear();
            Console.WriteLine("전사\n초기 체력 : 120 \n초기 공격력 : 10\n초기 방어력 : 7\n매우 높은 생존력");
            Console.WriteLine();
            Console.WriteLine("마법사\n초기 체력 : 80 \n초기 공격력 : 17\n초기 방어력 : 4\n매우 낮은 생존력과 매우 높은 공격력");
            Console.WriteLine();
            Console.WriteLine("도적\n초기 체력 : 100 \n초기 공격력 : 13\n초기 방어력 : 6\n평균적인 능력치");
            Console.WriteLine();
            Console.WriteLine("궁수\n초기 체력 : 90 \n초기 공격력 : 15\n초기 방어력 : 5\n낮은 생존력과 높은 공격력");
            Console.WriteLine();
            break;
        default:
            Console.Clear();
            Console.WriteLine("잘못된 입력입니다.");
            break;
    }

    if(isCharCreated == true)
    {
        int isRight = 0;
        while (isRight != 1)
        {
            Console.Clear();
            Console.WriteLine($"당신의 이름 : {inputName}");
            Console.WriteLine($"당신의 직업 : {korJobName}");

            Console.WriteLine("이 내용이 맞습니까?");
            Console.WriteLine("1. 예    2. 아니오");
            isRight = InputInt(Console.ReadLine());
            Console.Clear();

            if (isRight == 1)
            {
                break;
            }
            else if (isRight == 2)
            {
                isCharCreated = false;
                break;
            }
            else
            {
                Console.WriteLine("잘못된 입력입니다.");
                Thread.Sleep(500);
            }
        }
    }
}

여기서 다시 코드를 나누어서 설명하겠습니다.

캐릭터 생성이 완료될때까지, 해당 내용을 반복합니다.

 

이름과 직업을 고르는 기능입니다.

Console.WriteLine("당신의 이름은?");
inputName = Console.ReadLine();

if (inputName == null)
{
    inputName = "User";
}

Console.Clear();
Console.WriteLine("직업을 고르세요");
Console.WriteLine("1. 전사    2. 마법사    3. 도적    4. 궁수    5. 직업설명");
inputSelectedJop = InputInt(Console.ReadLine());

여기서 InputInt 라는 함수가 나오는데, 다음 함수는 이렇습니다.

//ReadLine 을 통해 int를 받을때 사용하는 함수
//int 형이면 해당값 반환, 아니면 -1 반환
private static int InputInt(string input)
{
    int returnInt;

    if(int.TryParse(input, out returnInt))
    {
        return returnInt;
    }

    else
    {
        return -1;
    }
}

텍스트 게임인만큼, int 값을 받아서 계속 선택을 고를 예정이기때문에 값 변환에 성공하면 해당값을, 실패하면 -1을 반환하는 함수를 만들어 사용해줄 예정입니다.

 

또한 사용자가 이름을 입력할때 빈값을 입력하면 캐릭터명이 User가 되도록 만들었습니다.

 

직업의 스텟을 정하는 기능입니다.

switch (inputSelectedJop - 1)
{
    case 0:
        playerChar.SetBaseStatus(inputName, Jobs.Warrior);
        korJobName = "전사";
        isCharCreated = true;
        break;
    case 1:
        playerChar.SetBaseStatus(inputName, Jobs.Wizard);
        korJobName = "마법사";
        isCharCreated = true;
        break;
    case 2:
        playerChar.SetBaseStatus(inputName, Jobs.Theif);
        korJobName = "도적";
        isCharCreated = true;
        break;
    case 3:
        playerChar.SetBaseStatus(inputName, Jobs.Archer);
        korJobName = "궁수";
        isCharCreated = true;
        break;
    case 4:
        Console.Clear();
        Console.WriteLine("전사\n초기 체력 : 120 \n초기 공격력 : 10\n초기 방어력 : 7\n매우 높은 생존력");
        Console.WriteLine();
        Console.WriteLine("마법사\n초기 체력 : 80 \n초기 공격력 : 17\n초기 방어력 : 4\n매우 낮은 생존력과 매우 높은 공격력");
        Console.WriteLine();
        Console.WriteLine("도적\n초기 체력 : 100 \n초기 공격력 : 13\n초기 방어력 : 6\n평균적인 능력치");
        Console.WriteLine();
        Console.WriteLine("궁수\n초기 체력 : 90 \n초기 공격력 : 15\n초기 방어력 : 5\n낮은 생존력과 높은 공격력");
        Console.WriteLine();
        break;
    default:
        Console.Clear();
        Console.WriteLine("잘못된 입력입니다.");
        break;
}

위 inputSelectedJob 변수에 플레이어가 선택한 값을 저장하고, 그 값에 맞도록 생성한 캐릭터의 스텟을 정해주는 기능입니다.

 

그 외에도 잘못된 입력이 들어가면 다시 선택하도록 유도하고, 5번 선택지인 설명을 고르면 직업의 초기 스텟을 보여주도록 만들었습니다.

 

마지막으로 캐릭터의 생성을 확인하는 기능입니다.

if(isCharCreated == true)
{
    int isRight = 0;
    while (isRight != 1)
    {
        Console.Clear();
        Console.WriteLine($"당신의 이름 : {inputName}");
        Console.WriteLine($"당신의 직업 : {korJobName}");

        Console.WriteLine("이 내용이 맞습니까?");
        Console.WriteLine("1. 예    2. 아니오");
        isRight = InputInt(Console.ReadLine());
        Console.Clear();

        if (isRight == 1)
        {
            break;
        }
        else if (isRight == 2)
        {
            isCharCreated = false;
            break;
        }
        else
        {
            Console.WriteLine("잘못된 입력입니다.");
            Thread.Sleep(500);
        }
    }
}

무한반복을 만드는 변수 isCharCreated 가 true일때 작동되는 코드로, 캐릭터의 이름과 직업을 확인하고 1번 예를 고르면 게임이 시작됩니다.

2번 아니오를 고르면 isCharCreated를 false를 만들고 무한반복을 끝내어 다시 캐릭터의 이름과 직업을 고르는 부분으로 넘어갑니다.

 

여기서 1,2 이외의 선택지를 고르면 0.5초간 잘못된 입력입니다를 보여준 뒤, 다시 예와 아니오를 선택하게 됩니다.

 

게임을 시작하는 연출입니다.

TextCreater("게임 시작");

해당 부분또한 함수를 만들어서 사용하였습니다.

private static void TextCreater(string text)
{
    for(int i = 0; i < text.Length; i++)
    {
        Console.Write(text[i]);
        Thread.Sleep(50);
    }

    Console.WriteLine();
}

게임진행중, 텍스트가 즉시 지워졌다 즉시 사라지지 않고 글자를 하나하나 생성하게 만드는 함수입니다.

이후 게임 진행에 사용되는 텍스트는 모두 이 기능을 사용할 예정입니다.

 

해당 내용들이 지금은 Main에 즉시 만들어진 상태이지만, 이후 캐릭터생성 함수로 빼서 다시만들어줄 가능성도 있습니다.

 


아이템 관리 클래스 ItemDatabase

아직은 사용되지않은 클래스인 아이템 관련 클래스입니다.

 

여기에서 아이템을 생성하고, 관련된 내용을 저장하여 사용할 예정입니다.

namespace TextRPG_SBH
{
    public enum ItemType
    {
        Weapon = 0,
        Armor = 1
    }

    public struct Item
    {
        public string Name;
        public string Description;
        public ItemType Type;
        public float ItemAttack;
        public float ItemDefence;
        public int ItemId;

        public Item(int id, string itemName, string itemDescription, ItemType type, int attck, int defence)
        {
            ItemId = id;
            Name = itemName;
            Description = itemDescription;
            Type = type;
            ItemAttack = attck;
            ItemDefence = defence;
        }
    }

    internal class ItemDatabase
    {
        public List<Item> items = new List<Item>();

        public void ResetItemList()
        {
            items.Add(new Item(0, "시작의 검", "처음 만들어진 검", ItemType.Weapon, 1, 0));
        }
    }
}

 

아이템의 타입을 결정하는 열거형 입니다.

public enum ItemType
{
    Weapon = 0,
    Armor = 1
}

아직은 무기타입과 방어구 타입만이 존재합니다.

 

아이템 정보를 저장하는 구조체입니다.

public struct Item
{
    public string Name;
    public string Description;
    public ItemType Type;
    public float ItemAttack;
    public float ItemDefence;
    public int ItemId;

    public Item(int id, string itemName, string itemDescription, ItemType type, int attck, int defence)
    {
        ItemId = id;
        Name = itemName;
        Description = itemDescription;
        Type = type;
        ItemAttack = attck;
        ItemDefence = defence;
    }
}

새로운 아이템을 생성할때, 생성자를 통해 아이템의 이름, 설명, 타입, 공격력, 방어력, 아이템ID를 지정하게됩니다.

 

어떤 아이템이 존재하는지 저장하는 클래스입니다.

internal class ItemDatabase
{
    public List<Item> items = new List<Item>();

    public void ResetItemList()
    {
        items.Add(new Item(0, "시작의 검", "처음 만들어진 검", ItemType.Weapon, 1, 0));
    }
}

items라는 리스트를 생성하여 이 게임에 사용될 아이템의 정보를 저장합니다.

 

리스트로 지정한 이유는, 이후 얼마나 많은 아이템이 생성될지 모르기때문에 끝을 지정하지 않아도 되는 리스트를 사용하게 되었습니다.

또한 함수 ResetItemList를 호출하여 게임이 시작될때 리스트에 아이템정보를 입력하게됩니다.

 

지금은 임시로 무기타입의 아이템 하나를 만들어두었으며, 이후 상점가격등의 내용을 추가할 예정입니다.

 


이 외에도 오늘은 1주차 강의를 모두 수강하고 과제에 관련된 내용을 정리해둔 상태입니다.

 

2024.09.19 - [내일배움캠프/강의 과제] - C#문법 종합반 - 1주차 : 간단한 계산기 만들기

 

C#문법 종합반 - 1주차 : 간단한 계산기 만들기

1주차 과제로써, 콘솔창에 내용을 입력하고 해당 입력을 출력하거나 입력된 내용을 사용하여 계산후 출력하는 내용이 과제로 주어졌습니다. 이 과제 내용을 정리해보도록 하겠습니다.문제 내

gamerscode.tistory.com

 

내일은 2주차 강의수강과 함께 2주차 과제 내용 정리

캐릭터 정보 확인 기능과 인벤토리 기능을 만들 예정입니다.