TIL(Today I Learned)

텍스트 RPG 게임 만들기 (2) - TIL#9

Najdorf 2024. 1. 4. 21:43
728x90

오늘은 어제까지 했던 것과 다르게

C# 문법에 대해 공부를 따로 하지는 않고

4주차 과제인 '텍스트 RPG 게임 만들기'의 필수 기능을

구현을 최대한 하기 위한 시간을 가지도록 했다.


지난번에 코드의 뼈대만 진짜 간략하게 만들어봤는데...

게임? 이라고 하기도 뭐한 뼈만 남은 닭다리였다.

 

오늘 추가한 기능과 코드를 설명하는 방식으로 살펴보고,

마지막에 전체 코드를 싣기로 하였다.

 

 

먼저, 가장 중요한 Player 클래스

'플레이어와 관련 있는 행동'을 메서드로 추가하고,

인벤토리를 만들었다. (아이템 정보는 GameScene 클래스에 있다.)

public class Player
{
    // 플레이어의 능력치
    public int level = 1;
    public string name = "Chad";
    public string job = "전사";
    public int atk = 10;
    public int def = 5;
    public int hp = 100;
    public int gold = 1500;

    // 플레이어의 인벤토리
    public bool[] inventory = new bool[10];


    // 플레이어의 최초 생성 시 설정값
    public void PlayerStart()
    {
        for (int i = 0; i < inventory.Length; i++)
        {
            inventory[i] = false;
        }
    }

    // 플레이어의 행동
    public int InputAction()
    {
        // 입력 받는 부분
        Console.Write("원하시는 행동을 입력해주세요.\n>>");
        string input = Console.ReadLine();
        int num = int.Parse(input); // 숫자 이외 입력 시 에러 안나게 버그 수정할 것
        return num;
    }

    public void AddInventory(int select)
    {
        inventory[select] = true;
    }

    public void PayGold(int amount)
    {
        gold -= amount;
    }

}
  • inventory 배열은 bool 자료형을 저장하고, 10칸이다.
  • inventory 배열의 인덱스 값은 이후 설명할 아이템 정보가 들어있는 itemsArr 배열의 아이템 종류를 반영한다.
  • inventory 배열의 어느 인덱스에 저장된 값이 true면 해당 아이템 종류가 player에게 있다는 것을 의미하며, false는 그 반대이다.
  • InputAction 메서드는 콘솔 입력을 받아 해당되는 int형 자료를 반환한다. 주로 행동 결정에 쓰인다. (버그 있음)
  • AddInventory 메서드는 외부 클래스에서 아이템을 inventory 배열에 저장할 때 쓰인다.
  • PayGold 메서드는 외부 클래스에서 아이템 값을 지불한 후의 보유 골드량을 저장할 때 쓰인다.

 

InputAction 메서드는 위에서도 설명했듯이

각 Scene에서 입력을 받는 용도로 만들었는데,

 

아직은 의도한 int로 변환 가능한 자료만을 받지 않으면

에러가 발생하는 문제가 있어서

내일은 int.Parse 대신 TryParse를 이용해 코드를 수정해 볼 생각이다.

 

 

 

다음은 Item 클래스이다.

Item 클래스에는 아이템이 공통적으로 가질 수 있는 정보들을 정의해놨다.

public class Item
{
    public string name = "";
    public int point = 0;
    public string comment = "";
    public int reqGold = 0;

    public bool isWeapon = false; // false면 보호구(Armor), true면 무기류(Weapon)
    public bool isBought = false;
    public bool isEquiped = false;
}
  • name이름을 저장한다.
  • point는 아이템의 공격력/방어력을 저장한다. 아이템이 무기류일 때는 공격력을 반영하고, 보호구일 때는 방어력을 반영한다. 무기류인지 보호구인지에 관한 구분은 이후 isWeapon 값에 의해 결정된다.
  • comment아이템 설명을 저장한다.
  • reqGold는 상점에서 해당 아이템을 얼마의 골드를 지불하고 사야하는 지의 값을 저장한다.
  • isWeapon은 아이템의 유형을 나타낸다. true면 무기류이고, false면 보호구이다.
  • isBought는 상점해서 해당 아이템이 구매됐는 지의 여부이다.
  • isEquiped는 플레이어가 현재 장비하고 있는 지의 여부이다.

 

 

다음은 게임의 장면이나 장소라고 할 수 있는

scene을 표현한 GameScene 클래스이다.

 

게임에서 가장 중요한 역할을 하고 있다고 생각한다.

(아래에서 정의한 메서드들의 내용은 길어지는 관계로 모두 생략했다.)

(글 마지막에 전체 코드를 확인할 수 있다.)

class GameScene
{
    Player player = new Player();
    Item[] itemsArr = new Item[10];

    bool isInvenDisplay = true; // true면 Display, false면 Equip Mode
    bool isShopDisplay = true; // true면 Display, false면 Buy Mode

	// 아래 메서드들은 모두 내용이 생략 되어있음
    void MakeItems()
    public void GameInit()


    // 게임 Scene 관련 메서드
    public void ViewMain()

    void ViewStatus()

    void ViewInventory()
    void InvenDisplayMode()
    void InvenEquipMode()

    void ViewShop()
    void ShopDisplayMode()
    void ShopBuyMode()

}

 

  • player은 Player 클래스를 해당 클래스 내에서 쓰기 위해서 정의했다.
  • itemsArr는 Item 클래스의 형식을 따라가는 10칸 짜리 배열이다.
  • isInvenDisplayisShopDisplay는 인벤토리와 상점의 상태를 관리하는 토글형 변수이다.
  • MakeItems 메서드는 itemsArr에 아이템 정보를 저장하는 함수이다.
  • GameInit 메서드는 초기에 게임이 로드될 때 필요한 변수나 메서드들을 함께 로드하는 메서드이다.
  • ViewMain 메서드는 메인 화면을 표시한다.
  • ViewStatus 메서드는 플레이어와 관련된 상태 화면을 표시한다.
  • ViewInventory 메서드는 인벤토리를 표시한다. isInvenDisplay의 값에 따라 단순 표시, 장비 착용 모드로 전환할 수 있다.
  • ViewShop 메서드는 상점을 표시한다. isShopDisplay의 값에 따라 단순 진열, 아이템 구매 모드로 전환할 수 있다.

 

isInvenDisplay 와 같은 bool 형 변수를 만들면서

인벤토리나 상점을 토글 식으로 모드를 변경하자 생각했던 것은

 

분명히 겹치는 부분이 있고,

코드를 재사용하는 것이 가독성 면에서도 이득일 것이라고 생각했기 때문이다.

(Player 클래스의 InputAction 메서드도 같은 이유이다.)

 

실제로도 비슷한 구조의 정보를 표시하는 것에 있어서

bool형 변수의 값만 바꿔주면 되기 때문에

여러 개의 Scene을 만드는 것보다는

확실히 구현까지의 시간이 줄어든 것 같은 느낌이었다.

 

 

 

 


내일 구현해야 할 남은 필수 요구사항은 다음과 같다.

  • 상점에서 아이템 구매 시 골드가 부족한 경우 처리할 기능 구현
  • 아이템 장비 시 상태 화면에서 추가된 능력치 표시하는 기능 구현

그리고 개인적으로 아쉽다고 느껴서 보완할 부분은 다음과 같다.

  • InputAction의 int형으로 변환 못할 시 예외 처리 기능
  • 아이템 종류 더 추가하기

 

 

아래는 전체 코드다.

더보기
// 프로그래밍 기초 개인 과제 - 텍스트 RPG 게임 만들기

using System.Diagnostics.Contracts;

public class Player
{
    // 플레이어의 능력치
    public int level = 1;
    public string name = "Chad";
    public string job = "전사";
    public int atk = 10;
    public int def = 5;
    public int hp = 100;
    public int gold = 1500;

    // 플레이어의 인벤토리
    public bool[] inventory = new bool[10];


    // 플레이어의 최초 생성 시 설정값
    public void PlayerStart()
    {
        for (int i = 0; i < inventory.Length; i++)
        {
            inventory[i] = false;
        }
    }

    // 플레이어의 행동
    public int InputAction()
    {
        // 입력 받는 부분
        Console.Write("원하시는 행동을 입력해주세요.\n>>");
        string input = Console.ReadLine();
        int num = int.Parse(input); // 숫자 이외 입력 시 에러 안나게 버그 수정할 것
        return num;
    }

    public void AddInventory(int select)
    {
        inventory[select] = true;
    }

    public void PayGold(int amount)
    {
        gold -= amount;
    }

}

public class Item
{
    public string name = "";
    public int point = 0;
    public string comment = "";
    public int reqGold = 0;

    public bool isWeapon = false; // false면 보호구(Armor), true면 무기류(Weapon)
    public bool isBought = false;
    public bool isEquiped = false;
}

class GameScene
{
    Player player = new Player();
    Item[] itemsArr = new Item[10];

    bool isInvenDisplay = true; // true면 Display, false면 Equip Mode
    bool isShopDisplay = true; // true면 Display, false면 Buy Mode

    void MakeItems()
    {
        // 아이템 배열 생성
        for (int i = 0; i < itemsArr.Length; i++)
        {
            itemsArr[i] = new Item();
        }

        // 아이템 정보 입력
        itemsArr[0].name = "수련자 갑옷";
        itemsArr[0].comment = "수련에 도움을 주는 갑옷입니다.";
        itemsArr[0].point = 5;
        itemsArr[0].reqGold = 1000;

        itemsArr[1].name = "무쇠갑옷";
        itemsArr[1].comment = "무쇠로 만들어져 튼튼한 갑옷입니다.";
        itemsArr[1].point = 9;
        itemsArr[1].reqGold = 1500;
    }

    public void GameInit()
    {
        MakeItems(); // 아이템 생성
        player.PlayerStart(); // 플레이어 초기값 생성
    }



    // 게임 Scene 관련 메서드
    public void ViewMain()
    {
        Console.Clear(); // 콘솔 창 깔끔하게 하기

        Console.Write("스파르타 마을에 오신 여러분 환영합니다.\n이곳에서 던전으로 들어가기 전 활동을 할 수 있습니다.\n\n");
        Console.WriteLine("1. 상태 보기");
        Console.WriteLine("2. 인벤토리");
        Console.WriteLine("3. 상점\n");

        int choice = player.InputAction();
        switch (choice)
        {
            case 1: ViewStatus(); break;
            case 2: ViewInventory(); break;
            case 3: ViewShop(); break;
            default: 
                Console.WriteLine("잘못된 입력입니다."); 
                Console.ReadLine(); // 사용자 입력까지 대기
                ViewMain();
                break;
        }
    }

    void ViewStatus()
    {
        Console.Clear();

        Console.Write("상태 보기\n캐릭터의 정보가 표시됩니다.\n\n");
        Console.WriteLine("Lv. " + player.level);
        Console.WriteLine("{0} ( {1} )", player.name, player.job);
        Console.WriteLine("공격력 : " + player.atk);
        Console.WriteLine("방어력 : " + player.def);
        Console.WriteLine("체 력 : " + player.hp);
        Console.WriteLine("Gold : {0} G\n", player.gold);
        Console.WriteLine("0. 나가기\n");

        int choice = player.InputAction();
        if (choice == 0)
        {
            ViewMain();
        }
        else
        {
            Console.WriteLine("잘못된 입력입니다.");
            Console.ReadLine(); // 사용자 입력까지 대기
            ViewStatus();
        }
    }

    void ViewInventory()
    {
        Console.Clear();
        Console.Write("인벤토리\n보유 중인 아이템을 관리할 수 있습니다.\n\n");
        Console.WriteLine("[아이템 목록]");

        if (isInvenDisplay)
        {
            InvenDisplayMode();
        }
        else
        {
            InvenEquipMode();
        }
    }
    void InvenDisplayMode()
    {
        for (int i = 0; i < player.inventory.Length; i++)
        {
            if (player.inventory[i]) 
            {
                Console.WriteLine("- {0}{1}    | {2} +{3}  | {4}",
                ((itemsArr[i].isEquiped) ? "[E]" : ""),
                itemsArr[i].name,
                ((itemsArr[i].isWeapon) ? "공격력" : "방어력"),
                itemsArr[i].point,
                itemsArr[i].comment);
            }
        }

        Console.WriteLine("\n1. 장착 관리");
        Console.WriteLine("0. 나가기\n");

        int choice = player.InputAction();
        if (choice == 0)
        {
            ViewMain();
        }
        else if (choice == 1)
        {
            isInvenDisplay = false;
        }
        else
        {
            Console.WriteLine("잘못된 입력입니다.");
            Console.ReadLine();
        }
        ViewInventory();
    }
    void InvenEquipMode()
    {
        for (int i = 0; i < player.inventory.Length; i++)
        {
            if (player.inventory[i])
            {
                Console.WriteLine("- {0} {1}{2}    | {3} +{4}  | {5}",
                (i+1),
                ((itemsArr[i].isEquiped) ? "[E]" : ""),
                itemsArr[i].name,
                ((itemsArr[i].isWeapon) ? "공격력" : "방어력"),
                itemsArr[i].point,
                itemsArr[i].comment);
            }
        }

        Console.WriteLine("\n0. 나가기\n");

        int choice = player.InputAction();
        if (choice == 0)
        {
            isInvenDisplay = true;
            ViewInventory();
        }
        else
        {
            int select = choice - 1;

            if (itemsArr[select].isEquiped)
            {
                itemsArr[select].isEquiped = false;
                Console.WriteLine("{0}을(를) 장착 해제했습니다.", itemsArr[select].name);
            }
            else if (itemsArr[select].isEquiped == false)
            {
                itemsArr[select].isEquiped= true;
                Console.WriteLine("{0}을(를) 장착 완료했습니다.", itemsArr[select].name);
            }
            else
            {
                Console.WriteLine("잘못된 입력입니다.");
            }
            Console.ReadLine();
            ViewInventory();
        }
    }

    void ViewShop()
    {
        Console.Clear();

        Console.Write("상점\n필요한 아이템을 얻을 수 있는 상점입니다.\n\n");
        Console.WriteLine("[보유 골드]\n{0} G\n", player.gold);
        Console.WriteLine("[아이템 목록]");

        if (isShopDisplay)
        {
            ShopDisplayMode();
        }
        else
        {
            ShopBuyMode();
        }

    }
    void ShopDisplayMode()
    {
        for (int i = 0; i < itemsArr.Length; i++)
        {
            if (itemsArr[i].name != "") // 아이템 이름이 공백이 아닌 경우만
            {
                Console.WriteLine("- {0}    | {1} +{2}  | {3}    | {4}",
                itemsArr[i].name,
                ((itemsArr[i].isWeapon) ? "공격력" : "방어력"), // isWeapon이 참이면 공격력, 거짓이면 방어력 반환
                itemsArr[i].point,
                itemsArr[i].comment,
                ((itemsArr[i].isBought == false) ? (itemsArr[i].reqGold + " G") : "구매완료")); // isBought가 거짓이면 가격, 참이면 구매완료 표시
            }
        }

        Console.WriteLine("\n1. 아이템 구매");
        Console.WriteLine("0. 나가기\n");

        int choice = player.InputAction();
        if (choice == 0)
        {
            ViewMain();
        }
        else if (choice == 1)
        {
            isShopDisplay = false;
        }
        else
        {
            Console.WriteLine("잘못된 입력입니다.");
            Console.ReadLine(); // 사용자 입력까지 대기
        }
        ViewShop();
    }
    void ShopBuyMode()
    {
        for (int i = 0; i < itemsArr.Length; i++)
        {
            if (itemsArr[i].name != "") 
            {
                Console.WriteLine("- {0} {1}    | {2} +{3}  | {4}    | {5}",
                (i+1),
                itemsArr[i].name,
                ((itemsArr[i].isWeapon) ? "공격력" : "방어력"), 
                itemsArr[i].point,
                itemsArr[i].comment,
                ((itemsArr[i].isBought == false) ? (itemsArr[i].reqGold + " G") : "구매완료"));
            }
        }

        Console.WriteLine("\n0. 나가기\n");

        int choice = player.InputAction();
        if (choice == 0)
        {
            isShopDisplay = true;
            ViewShop();
        }
        else
        {
            int select = choice - 1;

            if (itemsArr[select].isBought == false)
            {
                // 보유 골드 체크 기능 추가할 것
                player.AddInventory(select);
                player.PayGold(itemsArr[select].reqGold);
                itemsArr[select].isBought = true;
                Console.WriteLine("구매를 완료했습니다!");
            }
            else
            {
                Console.WriteLine("이미 구매한 아이템입니다.");
            }

            Console.ReadLine();
            ViewShop();
        }
    }

}



class Program
{
    static void Main(string[] args)
    {
        GameScene MainScene = new GameScene();
        MainScene.GameInit(); // 게임 초기값 로드
        MainScene.ViewMain(); // 메인 화면 로드
    }
}
728x90