TextRpg 만들기
오늘은 부트캠프에서 내준 TextRpg를 만들어 보았다. C#을 사용하였으며 나는 필수 기능만 구현하였다. 확장성있게 작성하고 되도록 클래스를 나누면서 작업하려고 노력했다(그렇게 됐는지는 잘..) 아무튼 오늘 작업한 코드를 분석해보는 시간을 가져보겠다.
수정) 10일차 게시물과도 이어지니 링크를 달아두겠다. https://velog.io/@artshime/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-10%EC%9D%BC%EC%B0%A8
시작 → new GameStart();
메뉴 표시 (1~3번 선택)
입력에 따라:
1: new State() 실행 (플레이어 상태 출력)
2: new Inventory(State) → 인벤토리 보여주고 장착 가능
3: new Store(State) → 상점에서 아이템 구매 가능
다시 new GameStart()로 돌아가면서 반복
internal class Program
{
static void Main(string[] args)
{
new GameStart();
}
}
보다시피 Main이 있기 때문에 실행을 담당한다. 오직 코드 한줄....이다. 작동에 문제는 없지만 이런 방식이 앞으로도 좋을지는 잘 모르겠다. 규모가 작아서 가능했던 일인것 같기도
var gameMenu = new List<(int number, string name)>
{
(1, "상태보기"),
(2, "인벤토리"),
(3, "상점"),
};
foreach (var item in gameMenu)
{
Console.WriteLine($"{item.number}. {item.name}");
}
이 코드는 나름 확장성(?)을 주려고 만든 부분이다. 그냥 단순히 추가하려고만 한다면 Console.WriteLine("4. 기타기능")도 써줘야 하고 switch문에도 case 4:코드를 추가해야 하고 원래 4를 입력하면 잘못된 입력이라는 문구가 출력됐는데 이제 아니니까 그것도 수정해줘야 하고....(바로 밑에 코드를 보면 무슨 말을 하는지 이해할 수 있다.)
그런 방식은 마음에 들지 않아서 그냥 더 추가하고 싶다면 gameMenu에 (4, "기타기능")만 써도 4를 입력했을 때 잘못된 입력이라는 문구가 출력되지 않게 했다. 이렇게 하면 메뉴가 더 추가되어도 아래 설명할 while문에는 코드 변화가 없을 것이다!
int gameSelectedNumber;
while (true)
{
Console.Write("\n원하시는 행동을 입력해주세요.\n>> ");
string gameStartInput = Console.ReadLine();
// 입력값이 숫자인지 확인 (숫자가 아니라면 바로 오류 메시지 출력)
if (!int.TryParse(gameStartInput, out gameSelectedNumber))
{
Console.WriteLine("잘못된 입력입니다.");
continue;
}
// 입력한 숫자가 메뉴 항목에 있는지 검사
bool menuExists = gameMenu.Any(item => item.number == gameSelectedNumber);
if (!menuExists)
{
Console.WriteLine("잘못된 입력입니다.");
continue;
}
break;
}
while문의 Console.ReadLine에서 숫자를 입력한다. 하지만 string값으로 받기 때문에 우린 이걸 정수형으로 바꿀 필요가 있다. 그래서 변수 int gameSelectedNumber를 밖에 선언해두었다. 밖에 해둔 이유는 나중에 switch문에서 저 변수에 어떤 값이 들어있는지에 따라 불러오는 스크립트가 달라지기 때문이다. 아무튼 그래서 !int.TryParse(gameStartInput, out gameSelectedNumber)를 사용했다. 내가 입력한 값이 숫자라면 true를 반환하고 문자라면 false를 반환하라는 뜻이다. 저 if문은 문자를 입력하게 됐을 때 오류 메시지를 출력하라는 의미를 갖고 있다. 만약 문자가 아니라 숫자를 입력했는데 gameMenu항목에 있는 숫자가 아닌 생뚱맞은 숫자를 입력하면 그것또한 잘못된 입력이다. 그래서 bool menuExists = gameMenu.Any(item => item.number == gameSelectedNumber)코드로 입력한 숫자가 gameMenu에 있는지 검사하는 것도 추가해주었다. Any()는 리스트 안에 조건에 맞는 요소가 하나라도 있으면 true를 리턴하는 메서드이다. 내가 입력한 수(gameSelectNumber)가 gameMenu에 있는지 확인하는 작업인 것이다.
[상태보기]에 들어갈 내용들이다.
public static int Lv { get; set; } = 1;
public static string Name { get; set; } = "Chad";
public static string Job { get; set; } = "(전사)";
public static int ATK { get; set; } = 10;
...
static을 사용해 인스턴스를 만들지 않아도 다른 스크립트에서 State.ATK로 직접 접근 가능하게 만들었다. 편하다는 장점은 있지만, 정보은닉이 되지 않는다는 단점이 있다.
while (true)
{
Console.Write("\n원하시는 행동을 입력해주세요.\n>> ");
string input = Console.ReadLine();
int choice;
if (!int.TryParse(input, out choice) || choice != 0)
{
Console.WriteLine("잘못된 입력입니다.");
continue;
}
break;
}
GameStart스크립트에서도 나왔지만 앞으로도 자주나올 코드이다. 숫자 0 이외에 다른 숫자나 문자를 입력하면 오류 문구를 출력하는 while문이다. 여기서 작지만 어떤 문제가 있었다.
if (choice != 0 || !int.TryParse(input, out choice))처음에 이렇게 썼는데 choice에 빨간 밑줄이 생겼다. 이유를 알아보니 앞에서 초기화를 시켜주지 않았기 때문이었다. 그래서 0으로 초기화를 시켜주고 순서를 바꿔봤다. 작동은 됐지만 0이 아닌 다른 숫자를 넣어도 오류 문구를 출력하지 않았다. 0으로 초기화했으니 choice != 0은 false이고, 뒤에 있는 조건도 false이므로 아무 숫자나 넣어도 오류가 아니라고 판단한 것이다. 조건문에 넣는 조건 순서도 중요한 이유일 수 있겠다......
public class Items
{
public int Id { get; } // 메뉴 선택용 번호
public string Name { get; } // 아이템 이름
public int ATKbonus { get; } // 공격력 추가
public int DEFbonus { get; } // 방어력 추가
public int Cost { get; } // 골드 가격
public string Description { get; } // 아이템 설명
public bool IsPurchased { get; set; } // 구매 여부
public bool IsEquipped { get; set; } = false; // 장착 여부
public Items(int id, string name, int atk, int def, int cost, string desc)
{
Id = id;
Name = name;
ATKbonus = atk;
DEFbonus = def;
Cost = cost;
Description = desc;
IsPurchased = false;
IsEquipped = false;
}
}
public Items(int id, string name, int atk, int def, int cost, string desc) 생성자 괄호 안에 있는 파라미터들은 객체를 생성할 때 초기값을 세팅해주는 역할을 한다.
internal class Item
{
public static class ItemDatabase
{
// 모든 아이템을 한곳에서 관리
public static List<Items> Items { get; } = new List<Items>
{
new Items(1, "수련자 갑옷", 0, 5, 1000, "수련에 도움을 주는 갑옷입니다."),
new Items(2, "무쇠갑옷", 0, 9, 2000, "무쇠로 만들어져 튼튼한 갑옷입니다."),
new Items(3, "스파르타의 갑옷", 0, 15, 3500, "스파르타의 전사들이 사용했다는 전설의 갑옷입니다."),
new Items(4, "낡은 검", 2, 0, 600, "쉽게 볼 수 있는 낡은 검 입니다."),
new Items(5, "청동 도끼", 5, 0, 1500, "어디선가 사용됐던것 같은 도끼입니다."),
new Items(6, "스파르타의 창", 7, 0, 5000, "스파르타의 전사들이 사용했다는 전설의 창입니다."),
};
}
}
public static으로 선언해 ItemDatabase.Items처럼 클래스 이름으로 바로 접근이 가능해졌다. 즉, 아이템 리스트를 전역처럼 공유할 수 있다는 것이다. get만 있고 set이 없기 때문에 외부에서 값을 바꿀 수 없다. 이제 더 많은 아이템을 추가하고 싶다면 여기에 한 줄만 추가하면 된다!