오늘은 어제 설계한 Player.cs를 기반으로 적(Enemy)과 아이템(Equipment, Potion) 정보를 구조화하고, 게임 내 기능들을 분리하여 체계적으로 관리할 수 있도록 설계를 진행했다.
1. 적과 아이템 정보 구성
- 어제 만든 Player.cs에서 사용했던 Stat 클래스 구조를 재활용하여, 적(Enemy)의 능력치도 동일한 방식으로 정리했다.
- EnemyType을 enum으로 선언하여 일반/정예/보스 몬스터를 나누고, EnemyStat 클래스 내부에 Dictionary<EnemyType, Stat>으로 몬스터 정보를 저장하여 관리의 효율성을 높였다.
- 아이템 또한 Equipment.cs와 Potion.cs를 통합하여 Equipment, Potion 클래스로 설계하고, 각 아이템의 정보를 Dictionary<string, T> 형식으로 저장하여 효율적인 접근이 가능하도록 구성했다.
2. ItemManager.cs 설계 및 기능 분리
- 장비 장착, 해제, 포션 사용, 인벤토리 출력 등의 기능을 ItemManager 클래스에 정리해 게임 로직과의 의존성을 낮췄다.
- 특히, 포션 사용 시 체력과 마력을 비율로 회복하도록 구현하였고, 아이템 개수(Count)를 관리하여 소비성 아이템의 사용 여부를 제어할 수 있게 만들었다.
- 직업별 장비 필터링 기능도 구현하여, Warrior는 sword, Archer는 bow, Mage는 wand만 사용할 수 있게 제한했다.
Intro.cs와 Menu.cs 분리 및 흐름 정리
- Intro.cs: 게임 시작 시 플레이어의 직업 선택과 난이도 선택 흐름을 담당. 미구현된 난이도(쉬움, 어려움)는 안내 메시지와 함께 기본 난이도(보통)로 진행되게 설정.
- Menu.cs: 플레이어의 현재 상태와 인벤토리를 출력할 수 있는 메인 메뉴 구성. DisplayStats, ShowInventory 등을 선택지를 통해 확인할 수 있게 구성하였다.
게임 시작
↓
IntroStart() → 직업 선택
↓
IntroSelect() → 난이도 선택
↓
기본 장비 지급 (ItemManager.AddEquipment)
↓
메인 메뉴(Menu.MainMenu)
↳ 내 정보 보기 / 인벤토리 보기
지금까지 만든 게임의 흐름이다. 제출 기한이 머지 않아 구상했던 것은 대부분 구현 못하게 되었지만, 필수 기능만이라도 구현해보고자 한다.
다음은 오늘 구현한 내용들에 대해 설명하겠다.
1. 포션 기능 설계
- 포션 회복량은 퍼센트 기반 (예: 20%, 40%, 60%, 100%)
- PotionGrade 열거형(enum)으로 등급 구분
- Potion.PotionHeal() 메서드로 회복률 반환
- ItemManager.UsePotion() 메서드에서 회복 적용 및 개수 차감
- MaxHP를 통해 최대 체력 이상으로 회복되는걸 방지
- 포션 개수 관리: Count 프로퍼티를 추가하여 Dictionary에서 개수 추적
Potion클래스
public enum PotionGrade
{
Low = 0, Mid = 1, High = 2, Max = 3
}
public class Potion
{
public string Name { get; set; }
public PotionGrade Grade { get; set; }
public int Price { get; set; }
public Potion(string name, PotionGrade grade, int price)
{
Name = name;
Grade = grade;
Price = price;
}
public int Count { get; set; }
public static Dictionary<string, Potion> PotionList = new Dictionary<string, Potion>()
{
{ "low_potion", new Potion("하급 포션", PotionGrade.Low, 30) { Count = 3 } },
{ "mid_potion", new Potion("중급 포션", PotionGrade.Mid, 50) { Count = 0 } },
{ "high_potion", new Potion("상급 포션", PotionGrade.High, 80) { Count = 0 } },
{ "max_potion", new Potion("최상급 포션", PotionGrade.Max, 0) { Count = 0 } }
};
public static double PotionHeal(PotionGrade grade)
{
switch (grade)
{
case PotionGrade.Low:
return 0.2;
case PotionGrade.Mid:
return 0.4;
case PotionGrade.High:
return 0.6;
case PotionGrade.Max:
return 1.0;
default:
return 0.0;
} }}
2. 아이템 분리 및 관리
- ItemManager.cs 파일을 새로 만들어 다음 기능들을 분리함:
- 포션 사용 (UsePotion)
- 장비 착용 (EquipItem)
- 장비 해제 (Unequip)
- 인벤토리 표시 (ShowInventory)
- EquipmentList와 PotionList를 static Dictionary로 관리
그 중 일부인 포션 사용에 대한 코드이다.
public static void UsePotion(Player player, Potion potion)
{
if (potion.Count <= 0)
{
Console.WriteLine($"{potion.Name}이 없습니다!");
return;
}
double rate = Potion.PotionHeal(potion.Grade);
int healHP = (int)(rate * player.MaxHP);
int healMP = (int)(rate * player.MaxMP);
player.HP += healHP;
player.MP += healMP;
potion.Count--;
Console.WriteLine($"{potion.Name}을 사용하여 HP를 {healHP}, MP를 {healMP}만큼 회복했습니다!");
Console.WriteLine($"현재 HP: {player.HP}/{player.MaxHP} | MP: {player.MP}/{player.MaxMP}");
}
public static void AddPotion(Player player, string key)
{
if (Potion.PotionList.ContainsKey(key))
{
var potion = Potion.PotionList[key];
if (!player.PotionInventory.ContainsKey(key))
player.PotionInventory[key] = potion;
player.PotionInventory[key].Count++;
Console.WriteLine($"{potion.Name}을(를) 획득했습니다!");
}
}
Main()함수에 다른 클래스와 메서드 호출하기
static void Main(string[] args)
{
// 인트로 출력
Intro.IntroStart();
// 플레이어 생성 및 설정
Player player = new Player();
Intro.IntroSelect(player);
// 난이도에 따른 초기 장비 지급(구현을 보통 난이도밖에 못함)
ItemManager.AddEquipment(player, "common_sword");
ItemManager.AddEquipment(player, "common_armor");
// 메인 메뉴로 진입
Menu.MainMenu(player);
}
Intro.IntroStart();
Intro라는 클래스 안에 정의된 IntroStart라는 static 메서드를 실행한다.
- Intro는 클래스 이름 (정적 메서드가 정의된 클래스)
- IntroStart는 public static 메서드
- static이기 때문에 객체 생성 없이 클래스이름.메서드() 형태로 바로 호출이 가능하다.
ItemManager.AddEquipment(player, "common_sword");
- ItemManager → 아이템 관련 기능을 모아놓은 클래스 (보통 static으로 선언해서 공용으로 사용)
- AddEquipment() → 플레이어에게 장비를 추가하는 메서드
- player → 아이템을 추가할 대상 (Player 클래스의 인스턴스)
- "common_sword" / "common_armor" → 딕셔너리 키값, Equipment.EquipmentList에서 장비를 찾아서 넣는 기준
다음은 ItemManager.cs에서 일부 가져온 코드이다.
public static void AddEquipment(Player player, string key)
{
if (Equipment.EquipmentList.ContainsKey(key))
{
Equipment equip = Equipment.EquipmentList[key];
player.Inventory.Add(equip); // 예시: 인벤토리에 추가
Console.WriteLine($"{equip.Name}을(를) 획득했습니다!");
}
}
오늘 발생한 문제
주로 접근 제한자 관련 문제가 잦았다.
"보호 수준 때문에 'Menu.MainMenu(Player)'에 액세스할 수 없습니다." 라는 문제는 Menu 클래스 안의 MainMenu 메서드가 private (기본 접근 제한자)로 선언되어 있어서, Program.cs 등 외부 클래스에서 접근할 수 없었기 때문이었다.
따라서 MainMenu 메서드에 public 접근 제한자를 추가해서 외부에서 호출 가능하게 수정하여 해결하였다.
이 외에도 Player 클래스가 internal인데 UsePotion이 public이라 접근이 안되는 오류라던가, 이런 클래스간 접근제한자의 차이와 사용에 대해 더 잘 알게 된 것 같다. 그 외에도 Main() 함수에서 다른 클래스나 메서드를 호출하는 법에 대해 잘 몰랐었는데, 오늘 과제를 수행하면서 좀 알게 된 것 같아 뿌듯하다.
'본 캠프 TIL' 카테고리의 다른 글
2025.04.21 (0) | 2025.04.21 |
---|---|
2025.04.18 TextRPG-완 (0) | 2025.04.18 |
2025.04.16 상속과 다형성, 인터페이스 (0) | 2025.04.16 |
2025.04.15 클래스와 객체 (0) | 2025.04.15 |
2025.04.15 배열과 컬렉션(List, Dictionary, HashSet), 메소드 +제네릭 (0) | 2025.04.15 |