오늘은 상점 작업을 마무리하고 힐링포션을 구현했다.
힐링포션을 구현했던 과정을 정리하고, 힐링포션을 Json 데이터로 직렬화, 역직렬화 하는 과정에서 새롭게 알게 된 JsonSerializerSettings에 대해서도 정리해본다.
이미 프로젝트엔 Item
클래스를 상속받는 ConsumeItem
클래스가 있다.
이 ConsumeItem
의 기능으로는 인벤토리에 Add 될 때 동일한 아이템이 있다면 개수가 합쳐져서 누적되는 기능이 있다.
ConsumeItem
을 상속받아서, 사용 시 사용자의 체력을 회복시켜주는 HealingPotion
클래스를 정의하기로 했다.
HealingPotion
클래스를 정의하기에 앞서서, 모든 소모품에 공통적으로 적용 될 가상 메서드를 ConsumeItem
클래스에 정의해두기로 했다.
public virtual void UseEffect(Character owner)
{
if (StackCount > 1)
StackCount--;
else
Game.Player.Inventory.Remove(this);
}
모든 소모품은 사용되면 개수가 1개 줄어들고 0개가 된다면 인벤토리에서 지워진다!
그리고 이 메서드를 생성자에서 Item.Use()
가 호출되면 실행 될 이벤트인 OnUsed
에 구독시켜줬다.
public ConsumeItem()
{
EffectDesc = string.Empty;
OnAdded += MergeItem;
OnUsed += UseEffect;
}
public class HealingPotion : ConsumeItem
{
private int healValue;
public int HealValue { get => healValue; set { healValue = value; EffectDesc = $"체력 {healValue} 회복"; } }
public HealingPotion() : base()
{
}
public HealingPotion(int id, string name, string description, int price, int stackCount, int healValue, ItemType itemType = ItemType.ConsumeItem, string effectDesc = null) : base(id, name, description, price, stackCount, itemType, effectDesc)
{
HealValue = healValue;
}
public HealingPotion(HealingPotion reference) : base(reference)
{
HealValue = reference.HealValue;
}
public override void UseEffect(Character owner)
{
base.UseEffect(owner);
owner.OnDamaged(-healValue);
}
public override Item DeepCopy() => new HealingPotion(this);
}
다음과 같이 구현해봤다.
healValue
UseEffect()
: base의 UseEffect도 같이 호출하여 포션의 개수를 줄이고, 사용자의 체력을 회복시킴DeepCopy()
: 자기 자신의 깊은 복사를 진행전역 객체인 Game
객체에 게임 진행의 아이템 정보들을 담아둔 리스트인 items
가 있다. 이곳에 원래 ConsumeItem
타입으로 생성돼 있던 깡통 체력포션을 이제 제 기능을 하는 HealingPotion
타입으로 바꿔주면 끝이다.
public static Item[] Items =
{
new Gear(1, "철제 검", "공격에 사용되는 무기", 1500, GearType.Weapon, 12, 0, 0, 0),
new Gear(2, "원형 방패", "적의 공격을 방어하는 방패", 1000, GearType.Shield, 0, 6, 0, 0),
new Gear(3, "사슬 갑옷", "철사 고리를 엮어 만든 갑옷", 2000, GearType.Armor, 0, 3, 0, 5),
new Gear(4, "다이아몬드 검", "더 쎈 무기", 3000, GearType.Weapon, 33, 0, 12, 0),
new Gear(5, "다이아몬드 방패", "더 단단한 방패", 2000, GearType.Shield, 0, 15, 0, 0),
new Gear(6, "다이아몬드 갑옷", "딴딴 갑옷 ", 4000, GearType.Armor, 8, 8, 8, 8),
new HealingPotion(7, "체력 포션", "체력을 회복하는 물약", 150, 1, 10),
new ConsumeItem(8, "마나 포션", "마나를 회복하는 물약", 150, 1),
};
이번 프로젝트에도 플레이어 데이터 저장/불러오기 기능이 구현됐다! 다른 분이 작업해주셨는데,
내가 저번 개인과제에서 했던 방법이랑은 달라서 코드도 재밌게 읽었다.
나는 최소한의 데이터만 따로 뽑아 저장하고 불러오기 할 때 하나하나 인스턴싱을 해줬는데,
이번 프로젝트에서 작업해주신 분은 플레이어의 Character
클래스를 통째로 직렬화 하셔서 저장하셨다!
내가 짠 Inventory
클래스는 Character
클래스와 참조 루프가 있어서 개인과제 때 고생 좀 했는데, 팀 프로젝트에서 작업해주신 분은 JsonSerializerSettings
를 이용해서 한 번에 해결하셨다...
난 이런게 있는 줄도 몰랐다. 이번에 알게 됐으니 이 녀석에 대해 정리해보려 한다.
var settings = new JsonSerializerSettings {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.All,
PreserveReferencesHandling = PreserveReferencesHandling.All,
};
string jsonStr = JsonConvert.SerializeObject(data, settings);
File.WriteAllText(path, jsonStr);
이 녀석의 사용법은 생성하고 내부 프로퍼티에 원하는 설정값들을 세팅한 다음, JsonConvert.SerializeObject()
를 호출할 때 매개변수로 넣어주면 된다.
마찬가지로 역직렬화 할때도 JsonConvert.DeserializeObject()
에 같은 settings를 넣어주면 된다.
위 코드에서는 4개의 프로퍼티가 사용됐는데, 각각 설명해보자면,
ReferenceLoopHandling.Ignore
: 참조 루프가 발생하면 무시한다.
Formatting.Indented
: Json string화 할 때, 들여쓰기를 한다.
TypeNameHandling.All
: 객체의 타입을 Json data로 같이 (역)직렬화한다.
PreserveReferencesHandling.All
: 모든 객체 끼리의 참조를 보존한다.
위에서 3번째와 4번째 옵션은 내가 추가했다.
3번쨰 옵션은 HealingPotion
클래스가 저장됐다가, 다시 불러와졌을 때 Item
타입으로 인스턴싱 돼서 포션의 기능을 잃어버렸기 때문에 추가해줬다.
4번째 옵션은 Equipment
클래스가 참조하는 장착된 아이템과 Inventory
클래스가 참조하는 아이템이 저장됐다가, 다시 불러와졌을 때 서로 다른 객체로 인스턴싱 돼서 문제가 발생했기 때문에 추가했다.
JsonSerializerSettings
의 프로퍼티들 중에서 자주 쓸 것 같은 프로퍼티들을 정리해보겠다.
Name | Description | Default |
---|---|---|
ConstructorHandling | 역직렬화 시, 비공개 생성자 사용을 허용할지 여부 | 사용 안함 |
Error | (역)직렬화 중 에러 발생 시 호출될 EventHandler | |
FloatFormatHandling | NaN , PositiveInfinity 및 NegativeInfinity 와 같은 특수 부동 소수점 숫자가 JSON으로 작성되는 방법 | 문자열 |
FloatParseHandling | JSON 텍스트를 읽을 때 부동 소수점 숫자(예: 1.0 및 9.9)를 구문 분석하는 방법 | Double |
Formatting | Json Text 출력 시, 들여쓰기 여부 | 사용 안함 |
NullValueHandling | (역)직렬화 시, null값이 처리되는 방법 | 포함 |
PreserveReferencesHandling | 객체 참조를 유지하는지 여부 | 사용 안함 |
ReferenceLoopHandling | 객체 참조 무한루프 발생 시 처리 방법 | 에러 발생 |
TypeNameHandling | (역)직렬화 시, 객체의 타입 정보를 처리하는 방법 | 사용 안함 |
공식 문서에 나와 있는 표에서 이제 사용되지 않는 것들을 제외하고, 뭔가 자주 쓰일 것 같은 프로퍼티들을 가져와봤다.
https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonSerializerSettings.htm