본문 바로가기
다이어리/내일배움 개발일지

게임개발캠프 - 개인과제(A) 4일차

by E.Clone 2024. 1. 4.

과정명 : 내일배움캠프 Unity 게임개발 3기

전체진행도 : 9일차

부분진행도 : Chapter2.1 - 4일차

작성일자 : 2024.01.04(목)

개발일지 목록 : 클릭


1. 진행중인 과정에 대해

간단한 던전 RPG 텍스트 콘솔 게임의 개인과제 제출이 내일 오후 6시이다.

팀원들 모두 각자 열심히 진행을 하는 중.

나는 선택 기능 구현 목록을 오늘 마무리 하였고, 현재 디버그를 하며 코드 상의 어색한 부분을 계속 수정중이다.

선택 기능 구현 모두 무게가 있는 기능이라 모든 부분에서 힘을 쓰다 보니, 800줄 가까이 되는 코드를 작성했다.

오늘의 기록을 위해 영상을 찍으면서도 글자 색 지정을 안했거나 하는 자잘한 미스도 보였다.

 

2. 오늘 학습에 대해

기능 구현을 모두 마친 후 돌아보니, 기존 작성했던 코드에서는 존재하는 아이템을 모두 shopItems 리스트에 Item 클래스 형태의 객체로 추가 해 두고, inventory 리스트에 추가하거나 빼는 방식을 사용하고 있었는데, 앞으로 개발이 계속된다면 퀘스트로 얻는 아이템이나 몬스터 드랍템 등 Shop에서 취급하지 않는 아이템도 많이 생길 것이기 때문에, 전체 아이템을 관리하는 리스트인 gameItems를 사용하는 구조로 바꾸는데에 많은 시간을 쏟았다.

 

게임 구조

기본적으로 모든 씬은 while(true) 안에 두어 플레이어에게 잘못된 입력값을 받으면 그 씬이 다시 로드되도록 하였다.

main(){
	ShowMainMenu(); // 메인 씬
}
ShowMainMenu(){
    while(true){
        (if 입력값 == 1){ 상태; }
        (if 입력값 == 2){ 상점; }
        (if 입력값 == 3){ 던전; }
        (if 입력값 == 0){ 게임종료; }
    }
}

상태(){
    while(true){
    
    	// 상태 씬 구성
        
    	(if 입력값 == 0){ break; // 뒤로가기 }
    }
}

상점(){
    while(true){
    
    	// 상점 씬 구성
        
    	(if 입력값 == 1){ 아이템구매 }
    	(if 입력값 == 2){ 아이템판매 }
    	(if 입력값 == 0){ break; // 뒤로가기 }
    }
}
...

먼저 계산을 마칠 필요성이 있는 구문들은 while문 위에 작성하거나, 아래의 코드처럼 같은 화면을 로드하지만 경고문을 같이 보여주는 경우도 자주 있다.

아이템구매(){
	alertMessage = ""
	while(true){
    
    	// 기존 아이템 구매 씬의 내용
        
        // Write를 사용하면 alertMessage가 빈 문장이어도 티가 나지 않는다
        Console.Write(alertMessage)
    
    	(if 골드부족){ alertMessage = "골드가 부족해요\n\n" }
    }
}

 

기능 구현에 관해서는 전체적으로 비슷한 무게를 두고 구현했던 기분이기 때문에, 주석을 적은 전체 코드를 아래와 같이 적어놓았다.

더보기

 

using System;
using System.Collections.Generic;

namespace Chapter2
{
    internal class Program
    {

        enum ItemType
        {
            Armor,
            Weapon,
            Potion
        }
        enum DungeonDifficulty
        {
            Easy,
            Normal,
            Hard
        }

        // 아이템 클래스 정의
        class Item
        {
            public string Name { get; set; }
            public int Attack { get; set; }
            public int Defense { get; set; }
            public string Description { get; set; }
            public bool IsEquipped { get; set; }
            public int Price { get; set; }
            public bool IsPurchased { get; set; }
            public ItemType Type { get; set; }
            public bool IsAvailableInShop { get; set; }
        }

        static int level = 1;
        static string name = "말랑단단장";
        static string job = "전사";
        static float baseAttack = 10;
        static int baseDefense = 5;
        static int health = 100;
        static int gold = 2500;

        static int currentDungeonClears = 0; // 현재 레벨에서의 던전 클리어 횟수


        // 인벤토리 리스트
        static List<Item> inventory = new List<Item>();

        // 상점 아이템 목록
        static List<Item> gameItems = new List<Item>
        {
            new Item { Name = "수련자 갑옷", Defense = 5, Type = ItemType.Armor, Description = "수련에 도움을 주는 갑옷입니다.", Price = 1000, IsPurchased = false, IsAvailableInShop = true },
            new Item { Name = "무쇠갑옷", Defense = 9, Type = ItemType.Armor, Description = "무쇠로 만들어져 튼튼한 갑옷입니다.", Price = 2000, IsPurchased = false, IsAvailableInShop = true },
            new Item { Name = "스파르타의 갑옷", Defense = 15, Type = ItemType.Armor, Description = "스파르타의 전사들이 사용했다는 전설의 갑옷입니다.", Price = 3500, IsPurchased = false, IsAvailableInShop = true },

            new Item { Name = "낡은 검", Attack = 2, Type = ItemType.Weapon, Description = "쉽게 볼 수 있는 낡은 검 입니다.", Price = 600, IsPurchased = false, IsAvailableInShop = true },
            new Item { Name = "청동 도끼", Attack = 5, Type = ItemType.Weapon, Description = "어디선가 사용됐던거 같은 도끼입니다.", Price = 1500, IsPurchased = false, IsAvailableInShop = true },
            new Item { Name = "스파르타의 창", Attack = 7, Type = ItemType.Weapon, Description = "스파르타의 전사들이 사용했다는 전설의 창입니다.", Price = 2500, IsPurchased = false, IsAvailableInShop = true },

            new Item { Name = "힘의 비약", Attack = 1, Type = ItemType.Potion, Description = "마을 뒷편의 폭포수가 담기면 영험한 기운이 솟아납니다.", Price = 200, IsPurchased = false, IsAvailableInShop = true },
            new Item { Name = "방어의 비약", Defense = 1, Type = ItemType.Potion, Description = "마을 우물의 물이 담기면 수호신의 기운이 깃듭니다.", Price = 200, IsPurchased = false, IsAvailableInShop = true }
        };

        static void Main(string[] args)
        {
            ShowMainMenu();
        }

        static void ShowMainMenu()
        {
            bool isRunning = true;
            string alertMessage = "";

            while (isRunning)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[스파르타 마을]");
                Console.ResetColor();
                Console.WriteLine("스파르타 마을에 오신 여러분 환영합니다.");
                Console.WriteLine("이곳에서 던전으로 들어가기 전 활동을 할 수 있습니다.");
                Console.WriteLine("");
                Console.WriteLine("1. 상태 보기");
                Console.WriteLine("2. 인벤토리");
                Console.WriteLine("3. 상점");
                Console.WriteLine("4. 던전 입장");
                Console.WriteLine("5. 휴식하기");
                Console.WriteLine("");
                Console.WriteLine("6. 저장하기");
                Console.WriteLine("7. 불러오기");
                Console.WriteLine("8. 초기화");
                Console.WriteLine("");
                Console.WriteLine("0. 종료");
                Console.WriteLine("");
                Console.WriteLine("");
                Console.Write(alertMessage);
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "1")
                {
                    ShowCharacterStatus();
                }
                else if (input == "2")
                {
                    ShowInventory();
                }
                else if (input == "3")
                {
                    ShowShop();
                }
                else if (input == "4")
                {
                    EnterDungeon();
                }
                else if (input == "5")
                {
                    Rest();
                }
                else if (input == "6")
                {
                    alertMessage = SaveGame();
                }
                else if (input == "7")
                {
                    alertMessage = LoadGame();
                }
                else if (input == "8")
                {
                    alertMessage = ResetGame();
                }
                else if (input == "0")
                {
                    isRunning = false; // 게임 종료
                    Console.WriteLine("");
                    Console.WriteLine(" ¯\\_(ツ)_/¯ 게임을 종료합니다 ¯\\_(ツ)_/¯");
                }
            }
        }

        static void ShowCharacterStatus()
        {
            float totalAttack = baseAttack;
            int totalDefense = baseDefense;

            foreach (var item in inventory)
            {
                if (item.IsEquipped)
                {
                    totalAttack += item.Attack;
                    totalDefense += item.Defense;
                }
            }

            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[상태 보기]");
                Console.ResetColor();
                Console.WriteLine("캐릭터의 정보가 표시됩니다.\n");
                Console.WriteLine($"Lv. {level.ToString("D2")}");
                Console.WriteLine($"{name} ( {job} )");
                Console.WriteLine($"공격력 : {totalAttack} (+{totalAttack - baseAttack})");
                Console.WriteLine($"방어력 : {totalDefense} (+{totalDefense - baseDefense})");
                Console.WriteLine($"체 력 : {health}");
                Console.WriteLine($"Gold : {gold} G");
                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "0")
                {
                    break; // [스파르타 마을]로 돌아감
                }
            }
        }

        static void ShowInventory()
        {
            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[인벤토리]");
                Console.ResetColor();
                Console.WriteLine("보유 중인 아이템을 관리할 수 있습니다.");
                Console.WriteLine("");
                Console.WriteLine("[아이템 목록]");

                if (inventory.Count == 0)
                {
                    Console.WriteLine("보유한 아이템이 없습니다.");
                }
                else
                {
                    for (int i = 0; i < inventory.Count; i++)
                    {
                        string stats = GetItemStats(inventory[i]);
                        string equipped = inventory[i].IsEquipped ? "[E]" : "";
                        Console.WriteLine($"({i + 1}) {equipped}{inventory[i].Name} | {stats} | {inventory[i].Description}");
                    }
                }

                Console.WriteLine("");
                Console.WriteLine("1. 장착 관리");
                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "1")
                {
                    ManageEquipment();
                }
                else if (input == "0")
                {
                    break; // [스파르타 마을]로 돌아감
                }
            }
        }

        static void ManageEquipment()
        {
            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[인벤토리 - 장착 관리]");
                Console.ResetColor();
                Console.WriteLine("보유 중인 아이템을 관리할 수 있습니다.");
                Console.WriteLine("");
                Console.WriteLine("[아이템 목록]");

                for (int i = 0; i < inventory.Count; i++)
                {
                    string stats = GetItemStats(inventory[i]);
                    string equipped = inventory[i].IsEquipped ? "[E]" : "";
                    Console.WriteLine($"{i + 1}. {equipped}{inventory[i].Name} | {stats} | {inventory[i].Description}");
                }

                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "0")
                {
                    break; // [인벤토리]로 돌아감
                }
                else if (int.TryParse(input, out int itemIndex) && itemIndex > 0 && itemIndex <= inventory.Count)
                {
                    Item selectedItem = inventory[itemIndex - 1];
                    if (selectedItem.Type != ItemType.Potion)
                    {
                        // 같은 타입의 다른 아이템을 해제
                        foreach (var item in inventory)
                        {
                            if (item.Type == selectedItem.Type && item != selectedItem)
                            {
                                item.IsEquipped = false;
                            }
                        }
                    }
                    // 선택한 아이템 장착 상태 토글
                    selectedItem.IsEquipped = !selectedItem.IsEquipped;
                }
            }
        }

        static void ShowShop()
        {
            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[상점]");
                Console.ResetColor();
                Console.WriteLine("필요한 아이템을 얻을 수 있는 상점입니다.");
                Console.WriteLine("");
                Console.WriteLine("[보유 골드]");
                Console.WriteLine($"{gold} G");
                Console.WriteLine("");

                Console.WriteLine("[아이템 목록]");
                foreach (var item in gameItems.Where(item => item.IsAvailableInShop))
                {
                    string stats = GetItemStats(item);
                    string purchaseStatus = item.IsPurchased ? "구매완료" : $"{item.Price} G";
                    Console.WriteLine($"- {item.Name} | {stats} | {item.Description} | {purchaseStatus}");
                }

                Console.WriteLine("");
                Console.WriteLine("1. 아이템 구매");
                Console.WriteLine("2. 아이템 판매");
                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "1")
                {
                    PurchaseItem();
                }
                else if (input == "2")
                {
                    SellItem();
                }
                else if (input == "0")
                {
                    break; // [스파르타 마을]로 돌아감
                }
            }
        }


        static void PurchaseItem()
        {
            string alertMessage = "";

            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[상점 - 아이템 구매]");
                Console.ResetColor();
                Console.WriteLine("필요한 아이템을 얻을 수 있는 상점입니다.");
                Console.WriteLine("");
                Console.WriteLine("[보유 골드]");
                Console.WriteLine($"{gold} G");
                Console.WriteLine("");

                Console.WriteLine("[아이템 목록]");
                for (int i = 0; i < gameItems.Count; i++)
                {
                    string stats = GetItemStats(gameItems[i]);
                    string purchaseStatus = gameItems[i].IsPurchased ? "구매완료" : $"{gameItems[i].Price} G";
                    Console.WriteLine($"{i + 1}. {gameItems[i].Name} | {stats} | {gameItems[i].Description} | {purchaseStatus}");
                }

                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");

                Console.Write(alertMessage);
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (int.TryParse(input, out int itemIndex) && itemIndex > 0 && itemIndex <= gameItems.Count)
                {
                    Item selectedItem = gameItems[itemIndex - 1];
                    if (selectedItem.IsPurchased)
                    {
                        alertMessage = "이미 구매한 아이템입니다.\n\n";
                    }
                    else if (gold >= selectedItem.Price)
                    {
                        selectedItem.IsPurchased = true;
                        inventory.Add(selectedItem);
                        gold -= selectedItem.Price;
                        alertMessage = "구매를 완료했습니다.\n\n";
                    }
                    else
                    {
                        alertMessage = "Gold가 부족합니다.\n\n";
                    }
                }
                else if (input == "0")
                {
                    break; // [상점]으로 돌아감
                }
            }
        }

        static void SellItem()
        {
            string alertMessage = "";
            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[상점 - 아이템 판매]");
                Console.ResetColor();
                Console.WriteLine("필요한 아이템을 판매할 수 있는 상점입니다.\n");

                Console.WriteLine("[보유 골드]");
                Console.WriteLine($"{gold} G\n");

                Console.WriteLine("[아이템 목록]");
                for (int i = 0; i < inventory.Count; i++)
                {
                    string stats = GetItemStats(inventory[i]);
                    int sellPrice = (int)(inventory[i].Price * 0.85);
                    Console.WriteLine($"{i + 1}. {inventory[i].Name} | {stats} | {inventory[i].Description} | {sellPrice} G");
                }

                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write(alertMessage);
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (int.TryParse(input, out int itemIndex) && itemIndex > 0 && itemIndex <= inventory.Count)
                {
                    Item selectedItem = inventory[itemIndex - 1];
                    int sellPrice = (int)(selectedItem.Price * 0.85);
                    gold += sellPrice;
                    selectedItem.IsEquipped = false; // 장착 해제

                    // 상점에서 구매 여부 업데이트
                    var shopItem = gameItems.FirstOrDefault(item => item.Name == selectedItem.Name);
                    if (shopItem != null)
                    {
                        shopItem.IsPurchased = false;
                    }

                    inventory.RemoveAt(itemIndex - 1); // 아이템 목록에서 제거
                    alertMessage = $"{selectedItem.Name}를 {sellPrice} G에 판매했습니다.\n\n";
                }
                else if (input == "0")
                {
                    break; // [상점]으로 돌아감
                }
            }
        }

        static void EnterDungeon()
        {
            float totalAttack = baseAttack;
            int totalDefense = baseDefense;

            foreach (var item in inventory)
            {
                if (item.IsEquipped)
                {
                    totalAttack += item.Attack;
                    totalDefense += item.Defense;
                }
            }

            while (true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[던전 입장]");
                Console.ResetColor();
                Console.WriteLine("");

                string healthStr = $"│ 체력  : {health}";
                string attackStr = $"│ 공격력 : {totalAttack}";
                string defenseStr = $"│ 방어력 : {totalDefense}";
                string goldStr = $"│ 골드  : {gold}";
                Console.WriteLine("┌──────────────────────┐");
                Console.WriteLine(PadRightToLength(healthStr, 20) + "│");
                Console.WriteLine(PadRightToLength(attackStr, 20) + "│");
                Console.WriteLine(PadRightToLength(defenseStr, 20) + "│");
                Console.WriteLine(PadRightToLength(goldStr, 20) + "│");
                Console.WriteLine("└──────────────────────┘");

                if (health <= 0) Console.WriteLine("\n※ 던전 입장을 위해 체력이 1 이상 필요합니다");
                Console.WriteLine("");
                Console.WriteLine("1. 쉬운 던전   | 방어력 5 이상 권장");
                Console.WriteLine("2. 일반 던전   | 방어력 11 이상 권장");
                Console.WriteLine("3. 어려운 던전 | 방어력 17 이상 권장");
                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                

                Console.Write("원하시는 행동을 입력해주세요.\n>> ");
                string input = Console.ReadLine();

                if (input == "1" && health>0)
                {
                    ProcessDungeon(DungeonDifficulty.Easy, 5, 1000);
                }
                else if (input == "2" && health > 0)
                {
                    ProcessDungeon(DungeonDifficulty.Normal, 11, 1700);
                }
                else if (input == "3" && health > 0)
                {
                    ProcessDungeon(DungeonDifficulty.Hard, 17, 2500);
                }
                else if (input == "0")
                {
                    break; // [스파르타 마을]로 돌아감
                }
            }
        }

        static void ProcessDungeon(DungeonDifficulty difficulty, int recommendedDefense, int baseReward)
        {
            float totalAttack = baseAttack;
            int totalDefense = baseDefense;

            foreach (var item in inventory)
            {
                if (item.IsEquipped)
                {
                    totalAttack += item.Attack;
                    totalDefense += item.Defense;
                }
            }

            bool isSuccess = true; // 던전 성공 여부
            int healthLoss = new Random().Next(20, 36); // 기본 체력 감소량
            int reward = baseReward; // 기본 보상

            // 방어력이 권장 수치보다 낮은 경우 실패 확률 적용
            if (totalDefense < recommendedDefense)
            {
                isSuccess = new Random().NextDouble() >= 0.4;
                healthLoss += recommendedDefense - totalDefense; // 체력 감소량 증가
            }
            else
            {
                healthLoss -= totalDefense - recommendedDefense; // 체력 감소량 감소
            }

            // 체력 감소 적용
            health -= Math.Max(healthLoss, 0);

            // 보상 계산
            if (isSuccess)
            {
                int attackBonus = new Random().Next((int)totalAttack, (int)(totalAttack * 2 + 1));
                reward += (int)(baseReward * (attackBonus / 100.0));
            }

            // 결과 출력
            while (true)
            {
                Console.Clear();
                if (isSuccess)
                {

                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("[던전 클리어]");
                    Console.ResetColor();
                    Console.WriteLine($"축하합니다!!\n{GetDifficultyString(difficulty)} 던전을 클리어 하였습니다.");
                    Console.WriteLine($"\n[탐험 결과]\n체력 {health + healthLoss} -> {health}\nGold {gold} G -> {gold + reward} G");
                    gold += reward;

                    // 레벨업 로직
                    currentDungeonClears++;
                    if (currentDungeonClears >= level)
                    {
                        LevelUp();
                        Console.WriteLine($"\n축하합니다! 레벨이 {level}로 상승했습니다.");
                    }
                }
                else
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("[던전 실패]");
                    Console.ResetColor();
                    Console.WriteLine($"아쉽게도 {GetDifficultyString(difficulty)} 던전을 클리어하지 못했습니다.");
                    Console.WriteLine($"\n[탐험 결과]\n체력 {health + healthLoss / 2} -> {health}");
                }
                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "0")
                {
                    break; // [던전 입장]으로 돌아가기
                }
            }
        }

        static void Rest()
        {
            string alertMessage = "";
            while(true)
            {
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("[휴식하기]");
                Console.ResetColor();
                Console.WriteLine($"500 G를 내면 체력을 회복할 수 있습니다. (보유 골드 : {gold} G)");
                Console.WriteLine("");
                Console.WriteLine("1. 휴식하기");
                Console.WriteLine("");
                Console.WriteLine("0. 나가기");
                Console.WriteLine("");
                Console.Write(alertMessage);
                Console.Write("원하시는 행동을 입력해주세요.\n>> ");

                string input = Console.ReadLine();

                if (input == "1")
                {
                    if (health == 100)
                    {
                        alertMessage = "체력이 이미 최대치입니다.\n\n";
                    }
                    else if (gold < 500)
                    {
                        alertMessage = "보유 골드가 부족합니다.\n\n";
                    }
                    else
                    {
                        gold -= 500;
                        health = 100;
                        alertMessage = "휴식을 완료했습니다. 체력이 100이 되었습니다.\n\n";
                    }
                }
                else if (input == "0")
                {
                    break; // [스파르타 마을]로 돌아가기
                }

            }
        }

        // 아이템 능력치 반환
        static string GetItemStats(Item item)
        {
            List<string> stats = new List<string>();
            if (item.Attack > 0)
            {
                stats.Add($"공격력 +{item.Attack}");
            }
            if (item.Defense > 0)
            {
                stats.Add($"방어력 +{item.Defense}");
            }
            return string.Join(" ", stats);
        }

        // 던전 Difficulty에 대한 문자열 반환
        static string GetDifficultyString(DungeonDifficulty difficulty)
        {
            switch (difficulty)
            {
                case DungeonDifficulty.Easy:
                    return "쉬운";
                case DungeonDifficulty.Normal:
                    return "일반";
                case DungeonDifficulty.Hard:
                    return "어려운";
                default:
                    return "알 수 없는";
            }
        }

        // 문자열의 우측을 공백으로 채움
        static string PadRightToLength(string str, int totalLength)
        {
            return str.PadRight(totalLength);
        }

        static void LevelUp()
        {
            level++;
            baseAttack = 10 + (level - 1) * 0.5f; // 레벨에 따른 기본 공격력 재계산
            baseDefense = 5 + (level - 1); // 레벨에 따른 기본 방어력 재계산
            currentDungeonClears = 0; // 클리어 횟수 초기화
        }

        static string SaveGame()
        {
            using (StreamWriter file = new StreamWriter("savegame.txt"))
            {
                file.WriteLine(level);
                file.WriteLine(name);
                file.WriteLine(job);
                file.WriteLine(baseAttack);
                file.WriteLine(baseDefense);
                file.WriteLine(health);
                file.WriteLine(gold);
                file.WriteLine(currentDungeonClears);

                // 인벤토리 저장 (아이템 이름과 장착 여부)
                foreach (var item in inventory)
                {
                    file.WriteLine($"{item.Name},{item.IsEquipped}");
                }
                // 상점템 구매 여부 저장
                foreach (var item in gameItems)
                {
                    if (item.IsPurchased)
                    {
                        file.WriteLine($"ShopItem,{item.Name}");
                    }
                }
            }
            return "진행도를 저장하였습니다.\n\n";
        }

        static string LoadGame()
        {
            if (File.Exists("savegame.txt"))
            {
                using (StreamReader file = new StreamReader("savegame.txt"))
                {
                    level = int.Parse(file.ReadLine());
                    name = file.ReadLine();
                    job = file.ReadLine();
                    baseAttack = float.Parse(file.ReadLine());
                    baseDefense = int.Parse(file.ReadLine());
                    health = int.Parse(file.ReadLine());
                    gold = int.Parse(file.ReadLine());
                    currentDungeonClears = int.Parse(file.ReadLine());

                    // 상점 아이템 구매 여부 초기화
                    foreach (var item in gameItems)
                    {
                        item.IsPurchased = false;
                    }

                    // 인벤토리 로드
                    inventory.Clear();
                    string line;
                    while ((line = file.ReadLine()) != null)
                    {
                        string[] parts = line.Split(',');
                        if (parts[0] == "ShopItem")
                        {
                            // 상점 아이템 구매 여부 로드
                            var itemToPurchase = gameItems.FirstOrDefault(item => item.Name == parts[1]);
                            if (itemToPurchase != null)
                            {
                                itemToPurchase.IsPurchased = true;
                            }
                        }
                        else
                        {
                            // 인벤토리 아이템 로드
                            var itemToAdd = gameItems.FirstOrDefault(item => item.Name == parts[0]);
                            if (itemToAdd != null)
                            {
                                inventory.Add(itemToAdd);
                                itemToAdd.IsEquipped = bool.Parse(parts[1]);
                            }
                        }
                    }
                }

                return "진행도가 로드되었습니다.\n\n";
            }
            else
            {
                return "저장된 데이터가 없습니다.\n\n";
            }
        }

        static string ResetGame()
        {
            level = 1;
            name = "말랑단단장"; 
            job = "전사"; 
            baseAttack = 10;
            baseDefense = 5;
            health = 100;
            gold = 2500;
            currentDungeonClears = 0;

            inventory.Clear(); // 인벤토리 초기화
            gameItems.ForEach(item => item.IsPurchased = false); // 상점 아이템 초기화

            return "진행도가 초기화되었습니다.\n\n";
        }
    }
}

 

위 코드에 대한 설명

아이템 클래스 정의
- Item 클래스에 아이템의 구성요소를 필드로 두어 관리

캐릭터 관련 기본적인 스테이터스들
- 굳이 Player 클래스로 둘 필요가 있을까 고민하다가 굳이 그렇게 할 필요성이 없어 보여 전역 필드로 사용

전체적인 게임아이템을 관리하는 List와, 인벤토리 List
- Item 클래스 형태를 멤버로 갖는 리스트 형태로 관리
- 초기화를 진행하지 않은 필드는 ["", 0, False] 등의 값을 가진다.
- 즉 IsPurchased 필드는 현재 작성된 코드처럼 굳이 false로 지정해줄 필요는 없음.

Main()
- ShowMainMenu() 메서드를 호출하여 게임 시작

ShowMainMenu()
- 모든 씬(Show메서드)은 while문을 사용하여 그리며, 잘못된 입력을 받을 시 현재 메뉴를 다시 보여줌.

ShowCharacterStatus()
- while문 안에는 최소한의 코드를 두어 자원을 절약
- $문자열 적극 사용

ShowInventory()
- 필요 시, alertMessage에 문구를 저장하여 화면 갱신 시 보여줌. 
- inventory.Count만큼 반복하여 아이템의 정보를 표시.
- GetItemStats() 메서드를 통해 아이템의 모든 능력치를 문자열로 반환받는다.
- string equipped = inventory[i].IsEquipped ? "[E]" : "";
- inventory[i].IsEquipped 가 참일 경우, "[E]"를, 거짓일 경우 ""를 equipped 에 저장한다.

ManageEquipment()
- 장비 장착 목적의 입력값에 대해
- int형으로 Parse가 가능하면 true를 반환하고, else if 문 내에서 itemIndex 변수에 저장
- 또한 itemIndex가 인벤토리 크기 범위 내인지 확인하여 만족한다면 장비를 장착하거나 해제

ShowShop(), PurchaseItem(), SellItem()
- 아이템목록 표시 시, IsPurchased 가 true일 경우, "구매완료" 문구를 함께 표시
- 아이템 구매를 성공할 경우, 해당 Item 객체는 inventory 리스트에 추가하여 관리할 수 있도록 함. 복사본이 아닌 객체 참조 형태.
- 아이템 판매를 성공할 경우, 85% 가격을 돌려받으며, IsEquipped을 false로, inventory.RemoveAt(index)를 통해 List에서 제거한다.

EnterDungeon(), ProcessDungeon()
- 던전 관련 장면을 구현. 선택한 난이도의 던전 입장 시, 캐릭터 스테이터스와 확률에 따라 성공과 실패를 한다.
- 캐릭터 스테이터스를 요구하기 때문에, 구현 기능목록에는 없었지만 캐릭터 스테이터스를 기본적으로 보여주도록 함.
- 여러가지 계산 관련하여 코드가 길어졌다.
- 체력이 0 이하일 경우, 던전에 진입 불가. 이것도 요구사항에는 없었지만 기본적으로 이 정도 제한은 당연하다고 생각하여 구현.

Rest()
- 500골드를 지불하고 체력을 100으로 만든다

string GetItemStats(Item item)
- item의 공격력, 방어력 중 0보다 높은 능력치를 집계하여 문자열로 반환

string GetDifficultyString(DungeonDifficulty difficulty)
- 던전 Difficulty에 대한 한글로 된 문자열 반환

string PadRightToLength(string str, int totalLength)
- 문자열의 우측을 공백으로 채움

LevelUp()
- 던전 탐험 시 레벨업 로직을 작성
- 게임데이터를 로드 하였을 경우에도 올바르게 적용되어야하기 때문에, '레벨에 따라' 기본 스텟을 연산하는 로직을 작성하였다.

SaveGame()
- 레벨, 이름, 직업, 기본공격력, 기본방어력, 현재체력, 골드, 경험치, 인벤토리목록+장착여부, 상점템구매여부
- savegame.txt로 저장하여 보안요소는 없다.

LoadGame()
- 인벤토리 및 장착여부와 상점아이템 구매 여부를 반영하는 데에 코드가 길어졌다.
- inventory.Clear(); 로 쉽게 리스트 비우기가 가능
- 아이템들은 모두 객체참조를 하여 아이템 내 필드를 쉽게 관리할 수 있도록 하였다.

ResetGame()
- 진행도를 초깃값으로 초기화

 

3. 시연 영상(gif, mp4)

길이가 3분정도라서, 가능하면 동영상으로 보는 게 좋을 듯 하다.

 

 

 

4. 과제에 대해

- 주어진 요구사항에는 모두 만족하지만, 플레이어 입장에서 불친절한 화면 구성이 많다. 당장 제출이 내일이기 때문에, 현재 캠프에서 이 프로젝트의 앞으로의 방향성을 듣고 난 이후 개선할 지 검토를 해봐야 한다.

- '=>' 같은 기호는 쓰기는 하는데 볼 때마다 무슨 기능인지 계속 헷갈린다. 파이썬에서도 데코레이트라던가 람다 함수라던가 필요하면 찾아는 쓰는데 볼 때마다 까먹는 요소들이 엄청 많은데 그야말로 과제요소가 아닌가 싶다. 숙련도 부족 이슈.

 

5. 참고자료

반응형