본문 바로가기
게임 개발

Text Dungeon - 세나몬 잡기 (3)

by chobbo 2024. 5. 2.

오늘은 코드 리팩토링을 진행하고 

전투 포기기능, 전투 중 포션 사용 기능을 구현하였다.

 

코드 리팩토링

우리 팀의 코드에 가장 큰 문제가 있었다.

바로 중구난방으로 쓰인 static ....

스크립트 간 쉬운 참조를 사용하기 위해 하나 둘 static을 사용하다보니 전체 코드가 static 범벅이 되어있었다.

 

팀 회의를 거치고, 튜터님의 자문을 얻어

코드 리팩토링을 진행했다.

 

해결 방법

 

위는 우리 팀의 코드 파일이다

우선 Manager폴더에 있는 여러 Manager 스크립트들을 총괄 관리해 줄 Manager.cs 스크립트를 생성하였다.

이 Manager.cs의 인스턴스를 생성하고 그  안에서 각각의 매니저 스크립트의 생성자를 만들었다.

 

public class Manager
{
    private static Manager instance;
    public static Manager Instance
    {
        get
        {
            if (instance == null) instance = new Manager();
            return instance;
        }
    }

    public GameManager gameManager = new GameManager();
    public FileManager fileManager = new FileManager();
    public BattleManager battleManager = new BattleManager();
    public UserDataManager userDataManager = new UserDataManager();
    public QuestManager questManager = new QuestManager();
    public InventoryManager inventoryManager = new InventoryManager();
}

 

그 후 모든 코드들을 돌며 static을 지워주었다..

정말 많은 시간이 걸렸다..............

오류가 150개는 넘었는데 일일히 고치는 것이 너무 힘들었다.

이번 경험을 통해 처음부터 좋은 코드를 작성하는 것에 대한 중요성을 깊이 깨달았다. 흑흑

 

 

발생한 문제점

위의 과정이 끝나고 GameManager.cs 내에서 기타 스크립트들의 생성자를 만들어서 참조하고자 했다.

    public class GameManager
    {
        public Battle battle = new Battle();
        public Status status = new Status();
        public Village village = new Village();
        public Boss boss = new Boss();
        public Rest rest = new Rest();
        public Inventory inventory = new Inventory();
        
        // 기타 코드 ..

 

수많은 오류를 전부 고치고 실행한 결과.. !!

코드가 터졌다.

원인은 스택 오버플로우

이러지마

 

처음에는 입력을 받는 코드에서 함수 탈출 조건을 명확히 쓰지 않아 무한 루프가

돌며 스택이 계속 쌓여 발생하는 오류라고 생각했다.

실제로 이 부분도 수정이 필요한 부분이긴 했다.

그러나 입력값을 받는 코드들을 전부 수정해도 동일한 오류가 계속 발생했다.

 

계속 머리를 박다가 GameManager와 Battle 스크립트 사이에서 스택 오버플로우가 발생하는 것을 깨달았다.

원인은 Battle 스크립트에서 생성한 GameManager의 User 객체를 생성자로 만든 것이었다.

문제의 코드 👇

 public User user = Manager.Instance.gameManager.user;

 

두 스크립트를 참조할때마다 서로 생성자를 만드니 순환참조하며 무한으로 스택이 쌓인 것이다.

이 부분을 삭제하고 직접 Manager.Instance.gameManager.user로 코드를 고쳐 오류를 해결하였다.

 

 

 

전투 포기기능

 public void GiveUP()
 {
     // 몬스터 잡으면 보상 바로 들어가는지 확인 -> 바로 들어가면 리스트로 관리
     Console.WriteLine("전투를 포기하시겠습니까? HP가 회복되지 않으며 보상을 얻을 수 없습니다.");
     Console.WriteLine("0. 포기하기 1. 계속하기");

     while (true)
     {
         int input;
         bool isValidNum = int.TryParse(Console.ReadLine(), out input);

         if (isValidNum)
         {
             switch (input)
             {
                 case 0:
                     Manager.Instance.gameManager.village.ShowVillage();
                     return;
                 case 1:
                     Manager.Instance.gameManager.battle.ShowBattle(false);
                     return;
                 default:
                     Console.WriteLine("잘못된 입력입니다.");
                     break;
             }
         }
         else
         {
             Console.WriteLine("숫자를 입력해주세요");
         }
     }
 }

 

- 구현해두었던 함수들을 활용하여 쉽게 구현 가능했다.

 

 

전투 중 포션 사용 기능

// 포션 먹기
public void UsePotion()
{
    Manager.Instance.gameManager.battle.ShowPlayerStat();

    Console.WriteLine("1. HP 포션 먹기");
    Console.WriteLine("2. MP 포션 먹기");
    Console.WriteLine("0. 뒤로가기");

    while (true)
    {
        int input;
        bool isValidNum = int.TryParse(Console.ReadLine(), out input);

        if (isValidNum)
        {
            switch (input)
            {
                case 0:
                    Manager.Instance.gameManager.battle.ShowBattle(false);
                    return;
                case 1:
                    foreach(Item item in Manager.Instance.inventoryManager.items)
                    {
                        if(item.Itemtype == Item.ItemType.HPPotion)
                        {
                            Manager.Instance.inventoryManager.RemoveItem(item);
                            if (Manager.Instance.gameManager.user.HP + item.ItemStat > Manager.Instance.gameManager.user.MaxHP)
                            {
                                Console.WriteLine($"HP +{Manager.Instance.gameManager.user.MaxHP - Manager.Instance.gameManager.user.HP}");
                                Manager.Instance.gameManager.user.HP = Manager.Instance.gameManager.user.MaxHP;
                            }
                            else
                            {
                                Console.WriteLine($"HP +{item.ItemStat}");
                                Manager.Instance.gameManager.user.HP += item.ItemStat;
                            }
                            Thread.Sleep(1000);
                            Manager.Instance.gameManager.battle.ShowBattle(false);
                            return;
                        }
                    }
                    Console.WriteLine("소지중인 HP 포션이 없습니다.");
                    break;
                case 2:
                    foreach (Item item in Manager.Instance.inventoryManager.items)
                    {
                        if (item.Itemtype == Item.ItemType.MPPotion)
                        {
                            Manager.Instance.inventoryManager.RemoveItem(item);
                            if (Manager.Instance.gameManager.user.MP + item.ItemStat > Manager.Instance.gameManager.user.MaxMP)
                            {
                                Console.WriteLine($"MP +{Manager.Instance.gameManager.user.MaxMP - Manager.Instance.gameManager.user.MP}");
                                Manager.Instance.gameManager.user.MP = Manager.Instance.gameManager.user.MaxMP;
                            }
                            else
                            {
                                Console.WriteLine($"MP +{item.ItemStat}");
                                Manager.Instance.gameManager.user.MP += item.ItemStat;
                            }
                            Thread.Sleep(1000);
                            Manager.Instance.gameManager.battle.ShowBattle(false);
                            return;
                        }
                    }
                    Console.WriteLine("소지중인 MP 포션이 없습니다.");
                    break;
                default:
                    Console.WriteLine("잘못된 입력입니다.");
                    break;
            }
        }
        else
        {
            Console.WriteLine("숫자를 입력해주세요");
        }
    }
}

 

- 포션 먹기를 선택하면 유저 인벤토리에 해당 포션 종류가 있는지 확인한다

- 포션이 없으면 "소지중인 포션이 없습니다." 출력

- 포션이 있으면 현재 아이템 삭제 -> 플레이어의 maxHP, maxMP 값을 넘지 않는 선에서 체력이나 마나를 채워준다.