ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 내일배움캠프 14일차 TIL - 퀘스트 구현
    TIL/C# 2024. 5. 2. 20:48

     

    [학습목표]

    팀 프로젝트 추가 요구사항 중 퀘스트 기능을 구현한다.

    [학습내용]

    오늘은 어제 구현을 끝내지 못한 퀘스트 기능을 구현 완료하였다. 마침 팀원분이 아이템과 인벤토리 기능을 구현하셔서 아이템 클래스를 가져다가 퀘스트 보상으로 사용할 수 있었다.

     

    퀘스트 클래스는 다음과 같다.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace OceanStory
    {
        internal class Quest
        {
            public string QuestName { get; set; }               // 퀘스트 제목
            public string Description { get; set; }             // 퀘스트 설명
            public string QuestObjective { get; set; }          // 퀘스트 수행조건
            public int QuestObjectiveCount { get; set; }        // 퀘스트 목표 진행도
            public int QuestProgressCount { get; set; }         // 퀘스트 현재 진행도
            public List<Item>? QuestRewardItem { get; set; }    // 퀘스트 완료시 보상 아이템 리스트
            public int QuestRewardGold {  get; set; }           // 퀘스트 완료시 보상 골드
            public bool IsAccepted {  get; set; }               // 퀘스트 진행 중 여부
            public bool IsCleared { get; set; }                 // 퀘스트 완료 여부
    
            public Quest(string questName, string description, string questObjective, int questObejctiveCount, int questProgressCount, List<Item> questRewardItem, int questRewardGold)
            {
                QuestName = questName;
                Description = description;
                QuestObjective = questObjective;
                QuestObjectiveCount = questObejctiveCount;
                QuestProgressCount = questProgressCount;
                QuestRewardItem = questRewardItem;
                QuestRewardGold = questRewardGold;
                IsAccepted = false;
                IsCleared = false;
            }
        }
    }

     

    퀘스트를 완료했을 때 지급할 아이템을 리스트로 만들어서 저장하였다.

    아래는 퀘스트 매니저 클래스다. 게임 시작 시에 스태틱으로 하나 생성된다.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace OceanStory
    {
        internal class QuestManager
        {
            public int QuestIndex = 0;                                      // 외부에서 퀘스트 리스트 접근을 위한 인덱스
            public List<Quest> QuestList = new List<Quest>();               // 퀘스트 리스트
            public List<Item>[] QuestRewardItemList = new List<Item>[3];    // 퀘스트 보상 아이템 리스트
    
            public QuestManager()
            {
                for (int i = 0; i < 3; i++)
                {
                    QuestRewardItemList[i] = new List<Item>();
                }
    
                QuestRewardItemList[0].Add(new Item("낡은 검", "마을 주민이 미니언을 잡아준 대가로 선물한 검이다.", 10, null, ItemType.WEAPON));
                QuestRewardItemList[1].Add(new Item("튼튼해보이는 옷", "못 입고 다니는 게 안타까웠던 주민이 선물한 옷이다.", null, 10, ItemType.ARMOR));
    
                QuestList.Add(new Quest("마을을 위협하는 미니언 처치", "이봐! 마을 근처에 미니언들이 너무 많아졌다고 생각하지 않나?\n" +
                    "마을 주민의 안전을 위해서라도 저것들 수를 좀 줄여야 한다고!\n" +
                    "모험가인 자네가 좀 처치해주게!", "미니언 5마리 처치", 5, 0, QuestRewardItemList[0], 100));
                QuestList.Add(new Quest("장비를 장착해보자", "떼잉, 쯔쯧... 그렇게 약해빠진 장비를 가지고서 미니언이나 잡겠나?\n" +
                    "좀 더 반듯한 장비를 마련해서 오라고!", "새로운 장비 아무거나 1개 장착", 1, 0, QuestRewardItemList[1], 100));
                QuestList.Add(new Quest("더욱 더 강해지기!", "무언갈 지키기 위해선 강해져야 하는 법이지.\n" +
                    "몬스터를 사냥하고 지금보다 레벨을 올려서 오게나.", "현재보다 레벨 2 증가", 2, 0, QuestRewardItemList[2], 500));
            }
    
            // 퀘스트 진행도 증가
            public void ProgressQuest(int QuestIndex)
            {
                if (QuestList[QuestIndex].IsAccepted) // 퀘스트가 진행 중 상태이면
                {
                    QuestList[QuestIndex].QuestProgressCount++; // 퀘스트 진행도 1 증가
                    if (QuestList[QuestIndex].QuestProgressCount >= QuestList[QuestIndex].QuestObjectiveCount) // 목표 진행도에 도달하면 퀘스트 완료
                    {
                        ClearQuest(QuestIndex);
                    }
                }
            }
    
            // 퀘스트 완료
            public void ClearQuest(int QuestIndex)      
            {
                foreach (Item questRewardItem in QuestList[QuestIndex].QuestRewardItem) // 보상 아이템 지급
                {
                    Program.Inventory.itemList.Add(questRewardItem);
                }
                Program.Character.Gold += QuestList[QuestIndex].QuestRewardGold; // 보상 골드 지급
                QuestList[QuestIndex].IsAccepted = false; // 퀘스트 진행중 -> 완료 표시
                QuestList[QuestIndex].IsCleared = true;
            }
        }
    }

     

    가운데에 보면 퀘스트 내용을 직접 집어넣어줬는데, 코드가 많이 더러워진 느낌이 든다. 퀘스트 정보에 대한 것들을 따로 파일이라던지로 저장해놓았다가 불러올 수 있는 방법을 생각해봐야겠다.

    그리고 퀘스트 진행도를 나타내기 위해서 IsAccepted(진행중)와  IsCleared(완료)를 boolean값으로 체크하게 했는데, 다시 생각해보니 둘은 공존할 수 없기 때문에 차라리 정수형 변수로 해서 여러가지 상태를 표현할 수 있게 하면 어땠을까 생각이 들었다.

    아래 두 코드는  퀘스트 관련 씬이다.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace OceanStory.Scenes
    {
        // 퀘스트 화면
        internal class QuestScene : Scene
        {
            public override void RunScene()
            {
                List<Quest> questList = Program.QuestManager.QuestList;
                while (true)
                {
                    Console.Clear();
                    Console.WriteLine("Quest!!\n");
                    // 퀘스트 리스트 표시
                    if (questList != null)
                    {
                        for (int i = 0; i < questList.Count(); i++)
                        {
                            Console.WriteLine("{1}. {0}{2}", questList[i].IsCleared?"[완]":"", i + 1, questList[i].QuestName);
                        }
                    }
                    Console.WriteLine("\n0. 돌아가기");
                    // 퀘스트 선택 입력 받기
                    int input = Program.SceneManager.GetUserInput(questList.Count(), "원하시는 퀘스트를 선택해주세요", 0);
                    Program.QuestManager.QuestIndex = input - 1;
                    if (input == 0 || input == -1) return;
                    // 입력받은 퀘스트 정보 화면으로 이동
                    else Program.SceneManager.ChangeScene("QuestDetailScene");
                }
            }
        }
    }

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Transactions;
    
    namespace OceanStory.Scenes
    {
        // 퀘스트 정보 화면
        internal class QuestDetailScene : Scene
        {
            public override void RunScene()
            {
                // 전역 변수 반복 접근을 줄이기 위한 변수
                List<Quest> questList = Program.QuestManager.QuestList;
                int questIndex = Program.QuestManager.QuestIndex;
                while (true)
                {
                    Console.Clear();
                    Console.WriteLine("Quest!!\n");
                    Console.WriteLine(questList[questIndex].QuestName + "\n");
                    Console.WriteLine(questList[questIndex].Description + "\n");
                    Console.WriteLine("- {0} ({1}/{2})\n",
                        questList[questIndex].QuestObjective,
                        questList[questIndex].QuestProgressCount,
                        questList[questIndex].QuestObjectiveCount);
                    Console.WriteLine("- 보상");
                    // 보상 아이템 리스트 표시
                    foreach(Item reward in questList[questIndex].QuestRewardItem)
                    {
                        Console.WriteLine(reward.Name);
                    }
                    Console.WriteLine("{0} G", questList[questIndex].QuestRewardGold);
    
                    // 퀘스트가 완료 상태라면
                    if (questList[questIndex].IsCleared)
                    {
                        Console.WriteLine("\n완료된 퀘스트입니다.");
                        Console.WriteLine("\n0. 뒤로 가기");
                        int input = Program.SceneManager.GetUserInput(0);
                        if (input == 0) break;
                    }
                    else
                    {
                        // 퀘스트가 진행 중이 아니라면
                        if (!questList[questIndex].IsAccepted)
                        {
                            Console.WriteLine("\n1. 수락\n2. 거절");
                            int input = Program.SceneManager.GetUserInput(2);
                            if (input == 1)
                                questList[questIndex].IsAccepted = true;
                            break;
                        }
                        // 퀘스트가 진행 중이라면
                        else
                        {
                            Console.WriteLine("\n현재 진행중인 퀘스트입니다.");
                            Console.WriteLine("\n0. 뒤로 가기");
                            int input = Program.SceneManager.GetUserInput(0);
                            if (input == 0) break;
                        }
                    }
                    
                }
            }
        }
    }

     

    씬은 있는 것을 출력하기만 하면 됐기 때문에 구현에 어려운 점은 없었다.

    퀘스트 진행도를 출력해주는 기능이 있기 때문에, 퀘스트 조건을 한 번 수행할 때마다 카운트가 오르도록 구현했다. 특이한 조건을 가지는 퀘스트를 구현해야하는 상황이 오면 진행도를 어떻게 해야 할지 다시 생각해봐야 할 것 같다.

    [결과물]

    퀘스트 화면
    퀘스트 디테일 화면
    진행도가 올라간 모습
    퀘스트 완료
    퀘스트 보상으로 '낡은 검'이 지급된 모습

     

    https://github.com/Gambling7l7l7JackPot/OceanStory

     

    GitHub - Gambling7l7l7JackPot/OceanStory

    Contribute to Gambling7l7l7JackPot/OceanStory development by creating an account on GitHub.

    github.com

    [회고]

    TIL을 작성하면서 느끼는 건데, 새로운 걸 배운다기보다는 기존의 알던 것을 어떻게 활용하는지를 배우는 느낌이 큰 거 같다. 주말을 제외하면 내일이 팀 프로젝트를 실질적으로 진행할 수 있는 마지막 날이기 때문에 내일은 마무리작업에 들어가야할 거 같다.