
텍스트튜터님 피드백 내용)
1)
Home함수의 경우 do-while을 사용하셨지만,
1~3번이 입력되면 해당 do-while이 종료되고 내부 함수에서 다시 do-while을 실행하는 구조 보단,
Home에서 do-while로 무한루프를 계속 유지하고,
내부 함수에서는 재귀 호출(자신이 자신을 호출)을 활용해 보는 것도 좋을 것 같습니다.
<수정 후>
public void Home(Player player) // 0. 메인 화면(나기기)
{
string input; //입력값
bool loop = true; // 루프를 반복한다
do
{
Console.Clear(); // 화면 초기화
Console.WriteLine("스파르타 마을에 오신 여러분 환영합니다.\n이곳에서 던전으로 들어가기전 활동을 할 수 있습니다."); // 간단한 소개 말
Console.WriteLine("\n1. 상태 보기\n2. 인벤토리\n3. 상점\n4. 던전 입장\n5. 휴식하기\n");
Console.Write("원하시는 행동을 입력해주세요.\n>> ");
input = Console.ReadLine(); // 입력값
switch (input)
{
case "1": // 상태 보기
// loop = false; 수정 전에 이렇게 반복문을 멈췄음
Status(player);
break;
case "2": // 인벤토리 이동
// loop = false; 수정 전에 이렇게 반복문을 멈췄음
Inventory(player);
break;
case "3": // 상점 이동
// loop = false; 수정 전에 이렇게 반복문을 멈췄음
Shop(player);
break;
case "4": // 던전 이동
// loop = false; 수정 전에 이렇게 반복문을 멈췄음
Stage(player);
break;
case "5": // 휴식으로 이동
// loop = false; 수정 전에 이렇게 반복문을 멈췄음
Rest(player);
break;
default: // 1 ~ 3 이외의 문자를 입력할 시
Message("잘못된 입력입니다.");
break;
}
} while (loop); // 반복
-------------------------------
Status(player)
do
{
Console.Clear();
Console.WriteLine("상태 보기\n캐릭터의 정보가 표시됩니다.\n");
Console.WriteLine($"Lv. {player.Lv.ToString("D2")}"); // 레벨 보기
Console.WriteLine($"{player.Name} ( {player.Class} )"); // 이름 ( 직업 ) 보기
Console.WriteLine(addAtt > 0 ? $"Att: {player.Att}" + $" (+{addAtt})" : $"Att: {player.Att}"); // 공격력 보기
Console.WriteLine(addDf > 0 ? $"Df: {player.Df}" + $" (+{addDf})" : $"Df: {player.Df}"); // 방어력 보기
Console.WriteLine($"Hp: {player.Hp}"); // 체력 보기
Console.WriteLine($"Gold: {player.Gold} G\n"); // 소지금 보기
Console.WriteLine("0. 나가기\n");
Console.Write("원하시는 행동을 입력해주세요.\n>> ");
input = Console.ReadLine(); // 입력값
switch (input)
{
case "0": // 나가기
loop = false; // 반복 끝
break;
default: // "0" 이외 다른 문자를 입력할 시
Message("잘못된 입력입니다.");
break;
}
} while (loop); // 반복
}
Status(player)에서 0을 누르면 그 전의 화면으로 넘어간다.
이렇게 하면 쓸데없이 줄만 채우는 것을 방지할 수 있고
아래 Gemini가 알려주듯이 스택 오버플로우를 방지할 수 있다.
메인 화면으로 돌아가기 위해 더 이상 Home(player)를 재귀 호출하지 않습니다.
루프가 자동으로 다음 입력을 기다립니다.
이렇게 수정하면 스택 오버플로의 위험 없이 메인 화면을 계속해서 보여주고
사용자 입력을 처리할 수 있습니다.
프로그래밍을 하다 보면 흔히 마주칠 수 있는 런타임 오류 중 하나입니다.
마치 접시를 너무 높이 쌓아 올리면 와르르 무너져 버리는 것과
같은 상황이라고 생각하시면 이해하기 쉬울 거예요.
함수가 자기 자신을 계속해서 호출하는 재귀 함수에서
종료 조건이 제대로 설정되지 않아 멈추지 않고 계속 호출될 경우,
매번 새로운 스택 프레임이 쌓이게 됩니다.
결국 스택 메모리의 제한된 공간을 초과하게 되어 스택 오버플로 오류가 발생합니다.
마치 끝없이 자기 자신을 복사하는 거울과 같다고 할 수 있죠.
재귀 호출의 깊이가 너무 깊어져서 스택에 너무 많은 프레임이 쌓이는 경우에도 발생할 수 있습니다.
2)
Item.cs파일에 Player 클래스가 있는건 적절하지 않은 것 같습니다.
Player.cs를 만드는게 좋 보입니다.
<수정 후>
<Player.cs>
class Player // 플레이어 클래스
{
public string Name { get; set; } // 이름을 받아옴
public string Class { get; set; } // 직업 받아옴
public int Lv { get; set; } // 레벨 받아옴
public float Att { get; set; } // 공격력 받아옴
public int Df { get; set; } // 방어력 받아옴
public int Hp { get; set; } // 체력 받아옴
public int Gold { get; set; } // 돈 받아옴
public int clearCount { get; set; } // 던전 클리어 횟수 받아오기
public List<Item> InvenItem { get; set; } // 내 아이템 목록 받아옴
public Player() // 플레이어 정보
{
Name = " "; // 이름
Class = "전사"; // 직업
Lv = 1; // 레벨
Att = 10; // 공격력
Df = 5; // 방어력
Hp = 100; // 체력
Gold = 1500; // 돈
clearCount = 0; // 던전 클리어 횟수
InvenItem = new List<Item>(); // 내 아이템 리스트 생성
}
public void E(Item item) // 아이템 장착
{
if (!item.ItemEquip) // 아이템 장착 해제 했을 때
{
item.ItemEquip = true; // 장착
Att += item.ItemAtt; // 플레이어 공격력 + 무기 공격력
Df += item.ItemDf; // 플레이어 방어력 + 방어구 방어력
}
}
public void UnE(Item item) // 아이템 장착 해제
{
if (item.ItemEquip) // 아이템 장착 했을 때
{
item.ItemEquip = false; // 해제
Att -= item.ItemAtt; // 플레이어 공격력 - 무기 공격력
Df -= item.ItemDf; // 플레이어 방어력 - 방어구 방어력
}
}
}
----------------------------------------------------------------------
<Item.cs>
public class Item // 아이템
{
public string ItemName { get; set; } // 아이템 이름을 받아온다.
public string ItemType { get; set; } // 아이템 타입(공격력, 방어력)을 받아온다..
public int ItemAtt { get; set; } // 아이템 타입(공격력, 방어력)을 받아온다.
public int ItemDf { get; set; } // 아이템 타입(공격력, 방어력)을 받아온다.
public string ItemInfo { get; set; } // 아이템 정보를 받아온다.
public int ItemPrice { get; set; } // 아이템 가격을 받아온다.
public bool ItemPur { get; set; } // 아이템 구매 여부를 받아온다.
public bool ItemEquip { get; set; } // 아이템 장착 여부를 받아온다.
public Item(string ItmName, string ItmType, int ItmAtt, int ItmDf, string ItmInfo, int ItmPri) // 아이템 정보 저장
{
ItemName = ItmName;
ItemType = ItmType;
ItemAtt = ItmAtt;
ItemDf = ItmDf;
ItemInfo = ItmInfo;
ItemPrice = ItmPri;
ItemPur = false;
ItemEquip = false;
}
}
public class Store // 상점 아이템
{
public List<Item> List { get; set; } // 상점 아이템 목록
public Store()
{
List = new List<Item> // 아이템 리스트 생성
{
new Item("천갑옷", "방어력", 0, 2, "천으로 만든 싼 갑옷입니다.", 500), //장비 객체화, 아이템 추가
new Item("수련자 갑옷", "방어력", 0, 5, "수련에 도움을 주는 갑옷입니다.", 1000),
new Item("무쇠갑옷", "방어력", 0, 9, " 무쇠로 만들어져 튼튼한 갑옷입니다.", 2000),
new Item("스파르타의 갑옷", "방어력", 0, 15, "스파르타의 전사들이 사용했다는 전설의 갑옷입니다.", 3500),
new Item("낡은 검", "공격력", 2, 0, " 쉽게 볼 수 있는 낡은 검 입니다.", 600),
new Item("청동 도끼", "공격력", 5, 0, "어디선가 사용됐던거 같은 도끼입니다.", 1500 ),
new Item("스파르타의 창", "공격력", 7, 0, "스파르타의 전사들이 사용했다는 전설의 창입니다.", 3000),
new Item("죽 창", "공격력", 14, 0, "극강의 공격력을 가진 창.", 4444)
};
}
public static Store itemStatus = new Store(); // itemStatus 초기화, 상점Store 구매 완료 표시를 위해 생성
public void storeUpdate() // 상점에 아이템을 초기화
{
itemStatus.List = new List<Item>() // 구매 이후에 쓸 아이템 리스트
{
new Item("천갑옷", "방어력", 0, 2, "천으로 만든 싼 갑옷입니다.", 500), //장비 객체화, 아이템 추가
new Item("수련자 갑옷", "방어력", 0, 5, "수련에 도움을 주는 갑옷입니다.", 1000),
new Item("무쇠갑옷", "방어력", 0, 9, " 무쇠로 만들어져 튼튼한 갑옷입니다.", 2000),
new Item("스파르타의 갑옷", "방어력", 0, 15, "스파르타의 전사들이 사용했다는 전설의 갑옷입니다.", 3500),
new Item("낡은 검", "공격력", 2, 0, " 쉽게 볼 수 있는 낡은 검 입니다.", 600),
new Item("청동 도끼", "공격력", 5, 0, "어디선가 사용됐던거 같은 도끼입니다.", 1500 ),
new Item("스파르타의 창", "공격력", 7, 0, "스파르타의 전사들이 사용했다는 전설의 창입니다.", 3000),
new Item("죽 창", "공격력", 14, 0, "극강의 공격력을 가진 창.", 4444)
};
}
}
분리 완료
3)
Item class안에 Store class를 구현하기보단
Item 과 Store를 분리해서 정의 하는게 좀 더 객체지향 적입니다.
그래야 Store와 Item 간의 결합력이 약해져 유지보수가 쉬워집니다.