내일배움캠프 Unity 17일차 TIL - 팀 조랑말 - 힐링포션 구현 / JsonSerializerSettings

Wooooo·2023년 11월 21일
0

내일배움캠프Unity

목록 보기
19/94

오늘의 키워드

오늘은 상점 작업을 마무리하고 힐링포션을 구현했다.
힐링포션을 구현했던 과정을 정리하고, 힐링포션을 Json 데이터로 직렬화, 역직렬화 하는 과정에서 새롭게 알게 된 JsonSerializerSettings에 대해서도 정리해본다.


힐링 포션 구현

1. 설계

이미 프로젝트엔 Item 클래스를 상속받는 ConsumeItem 클래스가 있다.
ConsumeItem의 기능으로는 인벤토리에 Add 될 때 동일한 아이템이 있다면 개수가 합쳐져서 누적되는 기능이 있다.
ConsumeItem을 상속받아서, 사용 시 사용자의 체력을 회복시켜주는 HealingPotion 클래스를 정의하기로 했다.

2. ConsumeItem 클래스 변경

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;
        }

3. HealingPotion 클래스 구현

    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() : 자기 자신의 깊은 복사를 진행

4. 생성

전역 객체인 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),
    };

JsonSerializerSettings

이번 프로젝트에도 플레이어 데이터 저장/불러오기 기능이 구현됐다! 다른 분이 작업해주셨는데,
내가 저번 개인과제에서 했던 방법이랑은 달라서 코드도 재밌게 읽었다.
나는 최소한의 데이터만 따로 뽑아 저장하고 불러오기 할 때 하나하나 인스턴싱을 해줬는데,
이번 프로젝트에서 작업해주신 분은 플레이어의 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 클래스가 참조하는 아이템이 저장됐다가, 다시 불러와졌을 때 서로 다른 객체로 인스턴싱 돼서 문제가 발생했기 때문에 추가했다.

Properties

JsonSerializerSettings의 프로퍼티들 중에서 자주 쓸 것 같은 프로퍼티들을 정리해보겠다.

NameDescriptionDefault
ConstructorHandling역직렬화 시, 비공개 생성자 사용을 허용할지 여부사용 안함
Error(역)직렬화 중 에러 발생 시 호출될 EventHandler
FloatFormatHandlingNaN , PositiveInfinity 및 NegativeInfinity 와 같은 특수 부동 소수점 숫자가 JSON으로 작성되는 방법문자열
FloatParseHandlingJSON 텍스트를 읽을 때 부동 소수점 숫자(예: 1.0 및 9.9)를 구문 분석하는 방법Double
FormattingJson Text 출력 시, 들여쓰기 여부사용 안함
NullValueHandling(역)직렬화 시, null값이 처리되는 방법포함
PreserveReferencesHandling객체 참조를 유지하는지 여부사용 안함
ReferenceLoopHandling객체 참조 무한루프 발생 시 처리 방법에러 발생
TypeNameHandling(역)직렬화 시, 객체의 타입 정보를 처리하는 방법사용 안함

공식 문서에 나와 있는 표에서 이제 사용되지 않는 것들을 제외하고, 뭔가 자주 쓰일 것 같은 프로퍼티들을 가져와봤다.


참고 문헌

https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonSerializerSettings.htm

profile
game developer

0개의 댓글