TextRpg 만들기
어제에 이어 나머지 코드를 분석해보는 시간을 갖겠다. 9일차 링크도 걸어두겠다.
https://velog.io/@artshime/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-9%EC%9D%BC%EC%B0%A8
인벤토리의 기능은 인벤토리에 들어갔을 땐 숫자 표시 없이 - 아이템 이름 | 아이템 효과 | 아이템설명이렇게 되어있다가 장착관리 기능을 사용할 때 - 숫자. [E]표시 on off | 아이템 이름 | 아이템 효과 | 아이템 설명이 되어야 한다.
public Inventory()
{
ShowInventory();
}
public void ShowInventory()
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("인벤토리");
Console.ResetColor();
Console.WriteLine("보유 중인 아이템을 관리할 수 있습니다.\n\n[아이템 목록]");
var ownedItems = ItemDatabase.Items
.Where(item => item.IsPurchased)
.ToList();
for (int i = 0; i < ownedItems.Count; i++)
{
var item = ownedItems[i];
var bonuses = new List<string>();
if (item.ATKbonus > 0) bonuses.Add($"공격력 +{item.ATKbonus}");
if (item.DEFbonus > 0) bonuses.Add($"방어력 +{item.DEFbonus}");
var bonusText = bonuses.Count > 0
? string.Join(" | ", bonuses)
: "보너스 없음";
// [E] 표시: 장착 중이면 [E], 아니면 공백
var equipMark = item.IsEquipped ? "[E] " : "";
// 최종 출력
Console.WriteLine(
$"- {equipMark}{item.Name} | {bonusText} | {item.Description}"
);
}
원래 아이템들은 각자 자신의 고유 id를 갖고 있었다.(9일차 TIL 참고) 하지만 만약 내가 4번 아이템만 샀을 때 아이템 목록에 4번 아이템밖에 없는데 4라고 뜨면 안되고 1이라는 순서로 떠야 하기 때문에 for문을 사용해 순서를 표시했다.
그리고
var ownedItems = ItemDatabase.Items
.Where(item => item.IsPurchased)
.ToList();
이 코드를 좀 더 자세히 보자. 전체 아이템 목록에서 각 아이템이 구매된 상태인지(true) 확인하고 그 필터링 된 결과를 리스트로 반환해 ownedItems에 저장한다.
현재 아이템은 공격력 또는 방어력 하나씩만 갖고 있는데(new Items(1, "수련자 갑옷", 0, 5, 1000, "수련에 도움을 주는 갑옷입니다.")), 그 하나만 출력되게끔 하기 위해 if문을 사용했다. 그리고 bounuses를 문자열 리스트로 받고 문자열이 하나 이상 담겨 있다면 string.Join(" | ", bonuses) 리스트 안 문자열들을 "|"로 이어 붙인다. 아직은 없지만 추후 공격력과 방어력을 둘 다 가진 아이템이 추가 될 시 유용하게 사용될 코드다.
private void ManageEquip()
{
// 구매된 아이템만 뽑아옵니다.
var ownedItems = ItemDatabase.Items
.Where(it => it.IsPurchased)
.ToList();
while(true)
{
Console.Clear();
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("인벤토리 - 장착 관리\n");
Console.ResetColor();
Console.WriteLine("보유 중인 아이템을 관리할 수 있습니다.\n\n[아이템 목록]");
for (int i = 0; i < ownedItems.Count; i++)
{
var item = ownedItems[i];
var bonuses = new List<string>();
if (item.ATKbonus > 0) bonuses.Add($"공격력 +{item.ATKbonus}");
if (item.DEFbonus > 0) bonuses.Add($"방어력 +{item.DEFbonus}");
var bonusText = bonuses.Count > 0
? string.Join(" | ", bonuses)
: "보너스 없음";
// [E] 표시: 장착 중이면 [E], 아니면 공백
var equipMark = item.IsEquipped ? "[E]" : "";
Console.WriteLine(
$"{i + 1}. {equipMark}{item.Name} | {bonusText} | {item.Description}"
);
}
Console.WriteLine("\n0. 나가기\n");
Console.Write("원하시는 행동을 입력해주세요.\n>> ");
string input = Console.ReadLine();
if (!int.TryParse(input, out int sel) || sel < 0 || sel > ownedItems.Count)
{
Console.WriteLine("잘못된 입력입니다.");
continue;
}
// 0 입력 시 돌아가기
if (sel == 0)
{
Console.Clear();
ShowInventory();
return;
}
잘 작동된다고 생각했는데... 다시 확인해보니 인벤토리 - 장착관리에서 아이템 목록에 없는 번호나 문자를 입력했을 때 '잘못된 입력입니다'라는 문구가 출력되어야 하는데 출력이 되지 않는 문제가 생겼다. 내 생각엔 While문 시작하자마자 Console.Clear()가 있어서 문구를 출력했는데 바로 지워버려서 확인이 안되었던 것 같다.
그래서 Console.ReadKey();를 추가해봤다. 확실히 문구는 출력되지만 아무 키를 또 눌러줘야 제대로 된 입력을 할 수 있었다. 이건 내가 원하는 방식이 아니었기에 다른 방법을 물색했다. 해결방법은 매우 간단했다!
Console.WriteLine("\n0. 나가기\n");
int sel;
while (true)
{
Console.Write("원하시는 행동을 입력해주세요.\n>> ");
string input = Console.ReadLine();
if (!int.TryParse(input, out sel) || sel < 0 || sel > ownedItems.Count)
{
Console.WriteLine("잘못된 입력입니다.\n");
continue;
}
break;
}
그냥 while문을 하나 더 만들어주는 것이다. while문 안에 또 while문을 만들어도 괜찮을까..? 라는 생각이 들긴하지만 break만 제대로 써주면 괜찮지 않을까한다.
Item스크립트에서 작성된 ItemDatabase를 토대로 아이템 목록을 나열한다. 처음엔 - 아이템 이름 | 아이템 효과 | 아이템 설명 | 가격으로 표시되지만 아이템을 구매하려고 하면 - 아이템 id | 아이템 이름 | 아이템 효과 | 아이템 설명 | 가격으로 표시되며 아이템을 구매하면 가격대신 '구매 완료'라는 문구가 표시되게 하였다.
사용한 코드는 Inventory와 비슷하고 로직도 크게 다르지 않으므로 하나만 설명해보겠다.
private void ShowItemIdStore()
{
Console.Clear();
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("상점 - 아이템 구매");
Console.ResetColor();
Console.WriteLine("필요한 아이템을 얻을 수 있는 상점입니다.\n");
Console.WriteLine("[보유 골드] \n{0} G\n", State.Gold);
foreach (var item in ItemDatabase.Items)
{
var bonuses = new List<string>();
if (item.ATKbonus > 0) bonuses.Add($"공격력 +{item.ATKbonus}");
if (item.DEFbonus > 0) bonuses.Add($"방어력 +{item.DEFbonus}");
var bonusText = bonuses.Count > 0
? string.Join(" | ", bonuses)
: "";
var priceOrStatus = item.IsPurchased ? "구매완료" : $"{item.Cost} G";
// 여기서는 ID 를 포함
Console.WriteLine(
$"- {item.Id}. {item.Name} | {bonusText} | {item.Description} | {priceOrStatus}"
);
}
}
상점 아이템 목록을 보여주는 메소드이다. 위에 Inventory스크립트를 보면 알겠지만 같은 코드를 사용했으나 for문을 사용했다. 하지만 여기는 foreach를 사용하고 있다. 왜일까?
상점이므로 그저 ItemDatabase에 저장된 목록 그대로 전부 불러오면 된다. Inventory에서는 인덱스를 사용해 선택과 장착을 처리하기 때문에 for문이 적절하고 Store는 아이템을 단순히 나열만 하기 때문에 foreach가 더 간단하다.
내가 내 코드를 평가하자면 캡슐화가 부족해 밖에서 어디서든 State의 값을 바꿀 수 있다는 단점이 있다. 그리고 클래스 간 결합도가 높다. new Inventory같이 필요하면 클래스끼리 너무 직접적으로 생성하고 있다. GameManager같은 클래스를 만들어서 사용해보는 것도 좋은 방법일 것 같다. 그리고 중복된 코드가 많다.
var bonuses = new List<string>();
if (item.ATKbonus > 0) bonuses.Add($"공격력 +{item.ATKbonus}");
if (item.DEFbonus > 0) bonuses.Add($"방어력 +{item.DEFbonus}");
var bonusText = bonuses.Count > 0
? string.Join(" | ", bonuses)
: "";
이 코드는 Inventory, Store 스크립트에서 사용되었는데 무려 네번이나 사용되었다. 중복된 부분을 따로 빼고 참조해서 썼다면 코드가 더 간결해지지 않았을까 하는 생각이 든다.(그런데 내가 할 수 있었을까??)