오늘은 팀 과제에 세이브 기능을 넣었다.
처음엔 개인 과제에 있는 그대로 넣을까 싶었지만, 마음에 안 드는 코드여서 새로 만들어 보았다.
하지만 Json을 썼다는 거에 이변은 없다.
Save & Load
Save 시 저장되어야 하는 데이터
- 플레이어 정보 (기본 수치, 직업, 직업별 스킬)
- 아이템 리스트
- 장착 아이템 리스트
저장 기능은 유틸성 클래스이다. 전역으로 사용할 예정이니 static 클래스로 만들었다.
internal static class DataManager
{
private static readonly string? DIRECTORY_NAME = Path.GetDirectoryName(Directory.GetCurrentDirectory());
}
저장할 폴더의 이름인 DIRECTORY_NAME 변수에 현재 콘솔앱이 실행되는 폴더의 부모 폴더를 할당했다.
개인과제에서 했던 폴더명은 직접 내가 저장하고자 했던 폴더를 하드코딩으로 할당했는데,
그렇게 하면 다른 팀원들이 세이브 기능 사용 시 오류가 나기 때문에
Path.GetDirectoryName() 메서드로 현재 콘솔앱이 실행되는 폴더를
Directory.GetCurrentDirectory() 메서드를 매개변수로 사용해 콘솔앱 폴더의 부모 폴더를 가져왔다.
굳이 부모 폴더를 가져온 이유는 단순히 그 폴더가 깨끗해서 보기가 편했기 때문이다.
Save
public static void SaveGame<T>(T data, string fileName)
{
string savePath = Path.Combine(DIRECTORY_NAME, fileName);
string dataFile = JsonSerializer.Serialize(data, new JsonSerializerOptions() { WriteIndented = true });
dataFile = Regex.Unescape(dataFile);
File.WriteAllText(savePath, dataFile);
}
세이브는 어느 타입이든 받아와 저장할 수 있게끔 제너릭을 사용했다.
T 타입의 data를 받아와
DIRECTORY_NAME 폴더에 fileName 이름으로 저장을 한다.
Serialize와 Unescape 등의 기능은 이전 TIL 에서 작성한 내용이므로 넘어간다.
실제로 사용해보자.
case "X":
case "x":
UI.ColoredWriteLine("※※※게임을 종료합니다※※※", ConsoleColor.Yellow);
SaveGame(player, "PlayerData.json");
return;
다소 코드가 생략되었지만 메인메뉴 씬에서 x를 입력해 게임종료시에 저장이 되도록 하였다.
저장될 타입으로 Player를, 파일명으로 "PlayerData.json" 을 매개변수로 넘겨줬다.
기존 개인과제에서는 저장할 데이터를 총 세개로 파일화 시켰었는데, 그게 너무 보기싫었다.
그래서 PlayerData 파일에 모든것을 넣으려 하다보니 여러가지 코드 변경이 일어났다.
public List<int> Items { get; private set; }
public List<int> EquippedItemsIndex { get; private set; }
먼저 보유 아이템, 장착 아이템 리스트들을 int 리스트화 시켜 프로퍼티로 만들었다.
int로 리스트화 시킨 이유는 아이템을 ID 저장할 것이기 때문이다.
리스트는 선언 과정에서 new List<int>() 를 사용하게 되면 Save 시, 저장이 안 된다는걸 알았다.
하지만 프로퍼티는 저장이 되길래 이 방법을 사용했다.
하지만 초기화는 해야하기에 이는 Player 생성자에서 하기로 했다.
또한 저장된 리스트들을 불러오기 시 가져와야하니 Player 생성자의 매개변수도 만들어 줬다.
this.Items = Items;
this.EquippedItemsIndex = EquippedItemsIndex;
if (this.Items == null)
this.Items = new List<int>();
if (this.EquippedItemsIndex == null)
this.EquippedItemsIndex = new List<int>();
위 처럼 캐릭터를 처음 생성할 땐, 생성자가 null 인 상태이다. 그렇기에 new List<int>() 로 초기화를 한다.
불러온 캐릭터가 있다면 null 이 아니므로 무시한다.
일단 저장은 잘 되니 이제 불러오도록 하자
public static T? LoadGame<T>(string fileName)
{
string savePath = Path.Combine(DIRECTORY_NAME, fileName);
if (!File.Exists(savePath))
return default;
string data = File.ReadAllText(savePath);
T? type = JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions() { IncludeFields = true });
return type;
}
불러올 때 역시 제너릭을 사용해 T 타입을 반환 하도록 한다.
매개변수로 fileName을 받아와 저장 폴더에 해당 파일이 있는지 File.Exists() 메서드로 확인 후,
없으면 default 값으로 null을 있으면 T 타입을 역직렬화를 통해 불러온 후 반환하도록 했다.
그럼 불러오기 해보도록 하자.
불러오기는 게임이 시작되는 EnterGame 메서드가 실행되자마자 player를 할당할 때 호출된다.
// 인게임에서 사용될 Player
private Player player = null;
#region 게임시작
public void EnterGame()
{
player = LoadGame<Player>("PlayerData.json");
if (player == null)
CharacterCreation();
else
shop.Restore(player);
}
게임이 시작될 때, 전역변수인 player에 LoadGame<Player>("PlayerData.json") 으로 PlayerData.json 파일을 찾아
Player 타입으로 저장된 플레이어를 반환, 할당해줬다.
불러오기 후 저장된 플레이어가 없다면 캐릭터 생성을
저장된 플레이어가 있다면 상점 정보를 갱신해준다.
그럼 플레이어가 불러와졌으니 다음으로 아이템 정보를 갱신해주자.
그 전에 짚고 넘어가야 할 부분이 있다.
플레이어의 보유 아이템 리스트는 상점과 연동된다.
라는 점이다.
이 말은 플레이어가 따로 아이템 리스트를 가지고 있는것이 아니라,
상점에서 구매를 한 아이템을 확인 후 콘솔창에 출력해주는 방식이다.
이는 아이템 클래스의 IsBuy 프로퍼티로 확인이 가능하다.
public bool IsBuy { get; protected set; } = false;
public void GetItem() => IsBuy = true;
이제 불러오기 후, 아이템 정보를 갱신하자.
public void Restore(Player player)
{
foreach(Item shopItem in items)
{
foreach(int playerItem in player.Items)
if (playerItem == shopItem.ID)
items[shopItem.ID].GetItem();
foreach (int equippedItem in player.EquippedItemsIndex)
if (equippedItem == shopItem.ID)
player.EquipOrUnequipItem(items[shopItem.ID]);
}
}
- 상점의 아이템과 플레이어의 보유 및 장착 아이템들을 하나씩 비교한다.
- 플레이어의 보유 아이템(index) 과 상점 아이템의 ID 가 일치하면 [구매됨] 상태로 변경한다. => GetItem();
- 플레이어의 장착 아이템(index) 과 상점 아이템의 ID 가 일치하면 [장착] 한다. => player.EquipOrUnequipItem();
아이템 장착 부분의 경우 아이템의 상태를 장착으로 바꾸고, 플레이어의 장착중인 아이템 저장을 동시에 한다.
Restore 가 끝나면, 위에서 저장되었던 ID == 1 의 아이템이 정상 장착 되어 있다.
'스파르타 내배캠' 카테고리의 다른 글
스파르타 내배캠 Unity 3기 18일차 (0) | 2024.01.17 |
---|---|
스파르타 내배캠 Unity 3기 17일차 (0) | 2024.01.16 |
스파르타 내배캠 Unity 3기 15일차 (1) | 2024.01.12 |
스파르타 내배캠 Unity 3기 14일차 (0) | 2024.01.11 |
스파르타 내배캠 Unity 3기 13일차 (1) | 2024.01.10 |