오늘은 어제에 이어 TRPG 제작을 계속해서 진행했다.
어제 작성했던 코드는 전체적으로 문제가 자꾸 생겨 아예 다시 코드를 작성했다.
이번엔 또 꼬이지 않도록 순서를 먼저 생각하고 해보고자 노력했다.
먼저 상단에는 캐릭터와 아이템의 클래스를 정의하였다.
클래스 정의
// 캐릭터 클래스
public class Character
{
public string Job { get; }
public int Level { get; }
public int Atk { get; }
public int Def { get; }
public int Hp { get; set; }
public int Gold { get; set; }
public Character(string job, int level, int atk, int def, int hp, int gold)
{
Job = job;
Level = level;
Atk = atk;
Def = def;
Hp = hp;
Gold = gold;
}
}
// 아이템 클래스
public class Item
{
public string Name { get; }
public string Description { get; } // 아이템 설명
public int Type { get; } // 무기/방어구 구분
public int Atk { get; }
public int Def { get; }
public int Gold { get; set; }
public bool IsEquip { get; set; }
public bool IsBuy { get; set; }
public static int ItemCount = 0;
public Item(string name, string description, int type, int atk, int def, int gold, bool isEquip = false)
{
Name = name;
Description = description;
Type = type;
Atk = atk;
Def = def;
Gold = gold;
IsEquip = isEquip;
IsBuy = false;
}
각각 필요한 변수들을 선언하고 get;으로 설정했으며 변할 수 있는 값들은 set;도 추가해준 다음 각 인스턴스도 생성했다.
아이템 관련 로직
// 아이템 로직
public void ShowDescription(bool itemNum = false, int idx = 0, bool price = true)
{
if (itemNum)
{
Console.Write("{0}. ", idx);
}
if (IsEquip) // 아이템 착용 시
{
Console.Write("[E]");
}
Console.Write(" | ");
// 장비 장착 시 스탯 추가
if (Atk != 0)
{
Console.Write($"Atk {(Atk >= 0 ? " + " : "")}{Atk}");
}
if (Def != 0)
{
Console.Write($"Def {(Def >= 0 ? " + " : "")}{Def}");
}
Console.Write(" | " + Description);
// 품절 상태 확인
if (price)
{
string itemStatus = IsBuy ? "구매완료" : Gold + " G";
Console.WriteLine(" | " + itemStatus);
}
Console.WriteLine();
}
// 사용할 아이템 세팅
private static void ItemSetting()
{
player = new Character("전사", 1, 10, 5, 100, 5000);
items = new Item[6];
AddItem(new Item("수련자 갑옷", "수련에 도움을 주는 갑옷입니다.", 0, 0, 5, 1000)); // 첫번째 숫자가 0이면 방어구, 1이면 무기
AddItem(new Item("무쇠 갑옷", "무쇠로 만들어진 튼튼한 갑옷입니다.", 0, 0, 9, 2000));
AddItem(new Item("스파르타의 갑옷", "스파르타의 전사들이 사용했다는 전설의 갑옷입니다.", 0, 0, 15, 3500));
AddItem(new Item("낡은 검", "쉽게 볼 수 있는 낡은 검입니다.", 1, 2, 0, 600));
AddItem(new Item("청동 도끼", "쉽게 볼 수 있는 낡은 검입니다.", 1, 5, 0, 1500));
AddItem(new Item("스파르타의 창", "스파르타의 전사들이 사용했다는 전설의 창입니다.", 1, 7, 0, 3000));
}
// 아이템 추가
static void AddItem(Item item)
{
if (Item.ItemCount == 6)
return;
items[Item.ItemCount] = item;
Item.ItemCount++;
}
장비의 인덱스, 장비의 설명, 착용 시 추가될 [E] 텍스트, 각 무기와 방어구의 추가 스탯, 상점에서 장비의 구매 여부 등이 있다.
삼항 연산자를 사용하여 구매한 장비면 구매완료 그렇지 않으면 가격이 보이도록 하였고
아래에는 사용할 장비들 추가하고 배열을 이용하여 세팅해주었다.
맨 앞의 숫자가 위에서 정의한 Type이고 0이면 방어구 1이면 무기다.
시작 메뉴
static void Start()
{
Console.Clear();
Console.WriteLine("===========================================================" +
"\n\n태초 마을에 오신 여러분 환영합니다." +
"\n이곳에서 던전으로 들어가기 전 활동을 할 수 있습니다." +
"\n\n1. 상태 보기" +
"\n2. 인벤토리" +
"\n3. 상점" +
"\n\n===========================================================");
string choice = Console.ReadLine();
switch (choice)
{
case "1":
Console.Clear();
Status();
break;
case "2":
Console.Clear();
Inventory();
break;
case "3":
Console.Clear();
Shop();
break;
default:
Console.Clear();
Start();
break;
}
}
선택지를 텍스트로 출력하고 유저의 입력을 받아 switch case문을 이용하여 각 메소드로 이동하도록 했고
중간중간 화면이 정리되면서 진행되도록 Console.Clear()를 추가하였다.
상태 보기
private static void Status()
{
Console.WriteLine("===========================================================");
Console.WriteLine($"\nLv. {player.Level}");
Console.WriteLine($"Class. {player.Job}");
// 장비 스탯 추가
int plusAtk = GetSumAtk();
int plusDef = GetSumDef();
Console.WriteLine($"공격력 : {player.Atk + plusAtk} (+{plusAtk})");
Console.WriteLine($"방어력 : {player.Def + plusDef} (+{plusDef})");
Console.WriteLine($"체력 : {player.Hp}");
Console.WriteLine($"Gold : {player.Gold}");
Console.WriteLine("\n===========================================================");
Console.WriteLine("0. 나가기");
Console.WriteLine("\n원하시는 행동을 입력해주세요.");
string choice = Console.ReadLine();
switch (choice)
{
case "0":
Start();
break;
default:
Console.WriteLine("잘못된 입력입니다.");
Status();
break;
}
}
// 추가 공격력
private static int GetSumAtk()
{
int sum = 0;
// 전체 아이템 확인
for (int i = 0; i < Item.ItemCount; i++)
{
if (items[i].IsEquip) sum += items[i].Atk; // 장착 상태인 아이템의 Atk 합산
}
return sum;
}
// 추가 방어력
private static int GetSumDef()
{
int sum = 0;
for (int i = 0; i < Item.ItemCount; i++)
{
if (items[i].IsEquip) sum += items[i].Def;
}
return sum;
}
1번 선택지인 상태 보기에서는 캐릭터의 스탯을 확인할 수 있다. 텍스트만 출력이 되고 상단에서 선언했던 변수들이 출력이 되지 않아서 여러가지 방법으로 잠시 헤맸는데 포멧팅을 이용해서 간단하게 출력할 수 있었다.
공격력과 방어력은 GetSumAtk()와 GetSumDef() 메서드로 착용한 장비에 맞게 텍스트 옆에 (+10) 과 같이 추가되도록 하였다.
인벤토리
private static void Inventory()
{
Console.WriteLine("===========================================================" +
"\n\n[아이템 목록]");
for (int i = 0; i < Item.ItemCount; i++) // 아이템의 가짓 수 만큼 출력
{
// 보유 중인 아이템 활성화
if (items[i].IsBuy)
{
items[i].ShowDescription(true, i + 1, false); // 템 번호, 인덱스, 가격 순
}
}
}
Console.WriteLine(
"\n\n0. 나가기" +
"\n1. 장착 관리" +
"\n\n원하시는 행동을 입력해주세요.");
string choice = Console.ReadLine();
switch (choice)
{
case "0":
Start();
break;
case "1":
Console.Clear();
Equip();
break;
default:
Console.Clear();
Console.WriteLine("잘못된 입력입니다.");
Inventory();
break;
}
}
// 장착 관리
private static void Equip()
{
Console.Clear();
Console.WriteLine("[아이템 목록]\n\n");
for (int i = 0; i < Item.ItemCount; i++)
{
// 구매한 아이템이 있을 경우
if (items[i].IsBuy)
{
items[i].ShowDescription(true, i + 1, false); // 템 번호, 인덱스, 가격 순
}
else
{
Console.WriteLine("-");
}
}
Console.WriteLine("\n0. 나가기");
string choice = Console.ReadLine();
int choiceNum = int.Parse(choice);
switch (choiceNum)
{
case 0:
Console.Clear();
Start();
break;
default:
EquipToggle(choiceNum - 1); // 인덱스와 유저 입력 맞추기
Equip();
break;
}
}
// 장비 장착 및 해제
private static void EquipToggle(int idx)
{
// 타입 별로 하나의 아이템만 장착
for (int i = 0; i < Item.ItemCount; i++)
{
if (i != idx && items[i].IsEquip && items[i].Type == items[idx].Type)
{
items[i].IsEquip = false;
}
}
// 새 아이템 장착 / 해제
items[idx].IsEquip = !items[idx].IsEquip;
}
인벤토리보다 상점을 먼저 구현했지만 2번 선택지이기 때문에 인벤토리를 먼저 다루겠다.
인벤토리에서 볼 수 있는 보유 중인 장비들은 for문을 이용해 상점에서 구매한 장비들을 나열하였다. ShowDescription() 괄호 안은 순서대로 아이템 번호, 인덱스, 가격표 순서이다.
구매한 아이템이 있을 경우 나열했던 장비에서 번호를 활성화하고 인덱스를 부여하고 가격은 비활성화 했고 없을 경우는 "-"으로 대체했다.
0번을 눌러 나가면 다시 처음 화면으로 돌아가고 그 외의 번호를 누르면 EquipToggle() 메서드를 통해 장비를 장착하고 해제하도록 했다.
타입 별로 하나의 아이템만 장착 가능하고 다른 장비를 장착하면 기존의 장비는 해제되도록 했다.
인벤토리에서 한가지 해결하지 못한 문제점은 장비를 장착하고 해제할 때 해당 아이템의 번호를 눌러야만 실행이 이루어져야 하는데 하나씩 옆의 번호를 입력해도 장착과 해제가 이루어지는 점이다.
예를 들면 1번과 4번 장비를 구매해서 인벤토리에서 장착하려고 하면 1과 4에 의해서만 변화가 있어야 하는데 2와 5를 눌러도 변화가 생긴다. 이 부분은 추후에 해결할 예정이다.
상점
private static void Shop()
{
Console.WriteLine("\n[보유 골드]" +
$"\n {player.Gold} G" +
"\n\n[아이템 목록]\n");
// 아이템 나열
for (int i = 0; i < Item.ItemCount; i++)
{
items[i].ShowDescription(false, i + 1);
}
Console.WriteLine("\n0. 나가기");
Console.WriteLine("1. 아이템 구매\n");
string choice = Console.ReadLine();
switch (choice)
{
case "0":
Start();
break;
case "1":
Console.Clear();
Buy();
break;
default:
Console.Clear();
Shop();
break;
}
}
// 아이템 구매
private static void Buy()
{
Console.WriteLine("\n[보유 골드]" +
$"\n {player.Gold} G" +
"\n\n[판매 목록]\n");
for (int i = 0; i < Item.ItemCount; i++)
{
items[i].ShowDescription(true, i + 1);
}
Console.WriteLine("\n0. 나가기" +
"\n\n원하는 항목을 선택해주세요.");
string choice = Console.ReadLine();
switch (choice)
{
case "0":
Start();
break;
}
int choiceNum = int.Parse(choice);
choiceNum -= 1; // 인덱스와 유저 입력 맞추기
Item selectedItem = items[choiceNum];
if (selectedItem.IsBuy)
{
Console.Clear();
Console.WriteLine("이미 구매한 아이템입니다.");
}
else if (player.Gold >= selectedItem.Gold)
{
Console.Clear();
selectedItem.IsBuy = true;
player.Gold -= selectedItem.Gold;
Console.WriteLine($"{selectedItem.Name} 구매완료");
}
else if (player.Gold <= selectedItem.Gold)
{
Console.Clear();
Console.WriteLine("\nGold가 부족합니다.");
}
else
{
Console.Clear();
Console.WriteLine("\n잘못된 입력입니다.");
}
Buy();
}
상점에서는 보유 골드와 구매 가능한 장비들을 볼 수 있고 아이템 번호는 비활성화 상태였다가 아이템 구매를 선택해서 구매 화면으로 넘어가면 활성화 되도록 하였다.
구매 화면으로 넘어가면 아이템의 번호와 가격이 활성화 되고 구매를 원하는 아이템의 번호를 입력하면 가격이 구매완료로 바뀌고 다시 사려고 하면 이미 구매했다는 텍스트가 출력되도록 했다.
아이템의 가격이 유저가 가진 금액보다 낮을 경우 구매가 되고 높을 경우 Gold가 부족하다는 텍스트가 출력되도록 했고 if문 끝에는 다시 구매 화면으로 가서 구매를 이어서 할 수 있도록 Buy() 메서드를 작성하였다.
오늘의 회고
프로젝트를 진행하면서 수정 사항이 있으면 주기적으로 커밋을 해야하는데 자꾸 까먹게 되는 것 같다. 그리고 이번 TRPG를 만들면서 가장 힘들게 느껴진 것은 반복되는 부분을 제대로 처리하지 않으면 예외가 발생하거나 프로그램이 종료돼버리는 상황이었다. F11을 수도 없이 누르면서 어디서 끊기는걸까.. 어디서 꼬이는걸까.. 하면서 무한 수정으로 보낸 시간이 대부분이었다. 막상 이렇게 정리하면 간단해 보이는데 아무것도 참고하지 않고 나 혼자 생각하면서 하려고 하면 어딘가 자꾸 꼬이고 과정도 생각나지 않았다. 결국 풀이도 참고하고 구글링도 하고 다른 사람이 만든 것도 참고를 해야지만 만들 수 있었다. 조금이지만 C#을 접해보았음에도 불구하고 어렵게 느껴지는데 아예 처음하는 비전공자들에게도 적당한 과정이 맞을까? 라는 의구심이 들기도 한다.
'게임 개발 공부 기록' 카테고리의 다른 글
11일차 - TRPG 팀 프로젝트 1 (Git특강 2회차 + 와이어 프레임) (0) | 2025.02.06 |
---|---|
10일차 - C# 5 (TRPG 마무리, 알고리즘 공부) (0) | 2025.02.05 |
8일차 - C# 3 (인터페이스~LINQ, TRPG 만들기) (0) | 2025.02.03 |
7일차 - C# 2 (배열 ~ 클래스, 객체) (0) | 2025.01.31 |
6일차 - C# 1 (기본 구조~반복문) (0) | 2025.01.27 |