[TIL] 오버라이딩이 안된다???
📕학습 개요
유니티 캠프 14일차.
TRPG 과제도 어느덧 막바지에 접어들었다. 생각보다 기능들이 잘 구현된 것 같아서 뿌듯하다.
물론 아직도 손 볼게 많다… 플레이어/몬스터 새부 스탯, 몬스터 드랍 테이블, 퀘스트 디테일, 보스 몬스터 스킬 등등… 내일이면 벌써 발표준비를 해야 하는데 조금 막막한 부분이 없지 않아 있다. 아무래도 오늘은 TIL작성 후 작업을 조금 더 하다 자야겠다.
📖학습 내용
오늘 한 일 정리
- 소비, 기타 아이템 거래 기능 구현
- 몬스터를 잡을 때 마다 보상(골드, 아이템, 경험치)지급 구현
- 소비 아이템 사용 구현(인벤토리)
- 아이템 JSON 파일 대거 수정
오늘 겪은 문제 및 해결 방법
오버라이딩이 안된다?
포션 클래스는 소비 아이템을 의미하는 Usable 클래스를 상속하도록 구현되었다.
public class Potion : Usable
{
public int HealAmount { get; private set; }
public Potion(ItemInfo itemInfo) : base(itemInfo)
{
HealAmount = itemInfo.HealAmount;
}
public override void Use(Player player)
{
base.Use(player);
player.RecoverHP(HealAmount);
Console.WriteLine($"{Name}를 사용했습니다. {(UsableType == UsableType.HealPotion ? "체력을" : "마나를")} {HealAmount} 회복했습니다.");
}
모든 소비 아이템은 대부분 Usable 객체로 관리되기 때문에 Use 메서드는 당연히 추상 메서드로 구현되었다.
public virtual void Use(Player player)
{
ChangeItemCount(-1);
}
문제는 상점에서 구매한 포션을 아무리 사용해도 Potion 클래스의 Use 메서드가 호출되지 않는 것이다. 아무리 봐도 오버라이딩은 제대로 한 것 같은데,
아마 무조건 객체가 Potion이 아닌 Usable을 참조해서 생긴 일이라고 생각했다. 그래서 포션 객체가 생성되는 부분을 자세히 살펴봤다.
아래 코드는 게임 시작 시 상점에서 판매할 아이템을 알아서 등록하는 과정이다.
foreach (ItemInfo itemInfo in ItemDataBase.ShopItems)
{
ITradable createdItem = ItemFactory.CreateItem(itemInfo);
switch (itemInfo.ItemType)
{
case ItemType.Equipment:
equipments.Add(createdItem);
break;
case ItemType.Usable:
usables.Add(createdItem);
break;
case ItemType.Other:
others.Add(createdItem);
break;
}
}
문제는 여전히 발견되지 않았다. ItemFactory는 아이템을 그 타입에 알맞게 알아서 생성한 객체를 반환해주기 때문이다. 즉 상점에서 구매한 포션은 Potion 클래스의 객체가 맞았다!
//ItemInfo를 기반으로 아이템 객체를 만들어주는 기능 제공
public static class ItemFactory
{
public static ITradable CreateItem(ItemInfo info)
{
switch (info.ItemType)
{
case ItemType.Equipment:
return new Equipment(info);
case ItemType.Usable:
switch (info.UsableType)
{
case UsableType.HealPotion:
case UsableType.ManaPotion:
return new Potion(info);
default:
return new Usable(info);
}
case ItemType.Other:
return new OtherItem(info);
}
return info.ItemType switch
{
ItemType.Equipment => new Equipment(info),
ItemType.Usable => new Usable(info),
ItemType.Other => new OtherItem(info),
_ => throw new ArgumentException($"지원하지 않는 아이템 타입입니다: {info.ItemType}")
};
}
}
그렇다면 뭐가 문제일까… 혼자 고민하다 지쳐 튜터님께 보여주는 과정에서 자연스래 깨닳게 되었다. 역시 코드는 남에게 보여줄 때 가장 잘 보이는 듯 하다.
문제는 오버라이딩도, 객체 생성도 아니었다. 내가 만들어놓은 소비, 기타아이템의 ‘특수성’ 때문이었다.
소비, 기타 아이템은 개체수가 많기 때문에 모든 객체를 생성하는 것이 아닌, 하나의 객체에 ItemCount 값을 추가하여 관리하기로 했다. 그리고 이 아이템을 거래할 시,
이동할 공간에 해당 아이템 객체가 존재하지 않을 시, 새로운 객체를 복사하여 전달한다. 문제는 여기서 발생한다.
public Usable CloneItem(int itemCount) //상점, 인벤토리에 전달 시, 새로운 객체를 복사하여 전달
{
var clone = new Usable(itemInfo);
clone.ItemCount = itemCount;
return clone;
}
객체를 복사할 때 Usable 객체로 만들어진다! 따라서 상점에 있던 포션은 Potion 객체가 맞지만, 인벤토리로 복사되어 들어가는 객체는 Usable객체인 것이다.
public override Usable CloneItem(int itemCount)
{
var clone = new Potion(itemInfo);
clone.ItemCount = itemCount;
return clone;
}
이 메서드도 추상 클래스로 만든 뒤, 오버라이딩 해줘서 해결하였다.
댓글남기기