유니티 RPG - 33. 방어구장착 및 해제
Intro
지난번 포스팅에 이어 방어구아이템 장착과 해제를 구현해보았습니다.
현재는 무기아이템만 장착이 가능한 상황으로, Inventory 클래스의 Use 함수의 방어구 장착 부분을 추가로 구현했습니다.
방어구 아이템 구조
현재 Item - EquipmentItem - ArmorItem 의 상속구조가 이루어져있습니다.
그리고, ArmorItem에서 IEquipableItem 인터페이스를 상속받아 구현해주었습니다.
장비 장착과 해제는 방어구 아이템의 공통적인 기능입니다.
따라서 EquipmentItem 클래스에 IEquipableItem 인터페이스를 상속하고, 세부 구현은 자식 클래스인 ArmorItem과 WeaponItem에서 구현하는것으로 변경하였습니다.
/*
EquipmentItem : 장착 아이템
- Equip() : 장비 장착
- UnEquip() : 장비 해제
*/
public abstract class EquipmentItem : Item, IEquipableItem
{
public EquipmentItemData EquipmentData { get; private set; }
public EquipmentItem(EquipmentItemData data) : base(data)
{
EquipmentData = data;
}
public virtual void Equip() { }
public virtual void Unequip() { }
}
public class ArmorItem : EquipmentItem
{
public ArmorItemData ArmorData { get; private set; }
public ArmorItem(ArmorItemData data) : base(data)
{
ArmorData = data;
}
public override void Equip()
{
DataManager.Instance.GetPlayerData().EquipItem(ArmorData.Defense, ArmorData.Type);
}
public override void Unequip()
{
DataManager.Instance.GetPlayerData().UnequipItem(ArmorData.Defense, ArmorData.Type);
}
}
public class WeaponItem : EquipmentItem
{
public WeaponItemData WeaponData { get; private set; }
public WeaponItem(WeaponItemData data) : base(data)
{
WeaponData = data;
}
// 장착
public override void Equip()
{
DataManager.Instance.GetPlayerData().EquipItem(WeaponData.Damage, WeaponData.Type);
}
// 장착 해제
public override void Unequip()
{
DataManager.Instance.GetPlayerData().UnequipItem(WeaponData.Damage, WeaponData.Type);
}
}
방어구아이템의 Subtype 추가
장착가능한 아이템의 종류는 크게 두가지로(무기, 방어구) 그 안에 여러종류의 장비타입이 있습니다.
예를들어 무기는 검, 활, 스태프… 방어구는 모자, 갑옷, 신발 등
따라서 장비타입은 크게 두가지 “Weapon”과 “Armor”
세부타입은 우선 “Armor” 타입에 한해 “Top” “Gloves” “Shoes”로 구분지었습니다.
{
"Top": [
{
"id": 3001,
"itemName": "낡은 갑옷",
"itemToolTip": "낡은 갑옷이다.",
"itemIcon": "Item_ArmorItem_Top_01.png",
"defense": 5,
"type": "Armor",
"subType": "Top"
},
{
"id": 3002,
"itemName": "단단한 갑옷",
"itemToolTip": "단단한 갑옷이다.",
"itemIcon": "Item_ArmorItem_Top_02.png",
"defense": 10,
"type": "Armor",
"subType": "Top"
}
],
"Shoes": [
{
"id": 4001,
"itemName": "낡은 장화",
"itemToolTip": "낡은 신발이다.",
"itemIcon": "Item_ArmorItem_Shoes_01.png",
"defense": 3,
"type": "Armor",
"subType": "Shoes"
},
{
"id": 4002,
"itemName": "판금 장화",
"itemToolTip": "단단한 신발이다.",
"itemIcon": "Item_ArmorItem_Shoes_02.png",
"defense": 6,
"type": "Armor",
"subType": "Shoes"
}
],
"Gloves": [
{
"id": 5001,
"itemName": "허름한 장갑",
"itemToolTip": "허름한 장갑이다.",
"itemIcon": "Item_ArmorItem_Gloves_01.png",
"defense": 3,
"type": "Armor",
"subType": "Gloves"
},
{
"id": 5002,
"itemName": "부드러운 장갑",
"itemToolTip": "부드러운 장갑이다.",
"itemIcon": "Item_ArmorItem_Gloves_02.png",
"defense": 6,
"type": "Armor",
"subType": "Gloves"
}
]
}
Inventory 클래스
Inventory 클래스의 Use 함수의 방어구 아이템부분을 구현해주었습니다.
public void Use(int index)
{
if (!IsValidIndex(index)) return;
if (items[index] == null) return;
// 1. 소모 아이템일 때
if(items[index] is IUsableItem usable)
{
// 사용
bool success = usable.Use();
// 성공
if(success)
{
UpdateSlot(index);
}
}
// 2. 장비 아이템일 때
else if(items[index] is IEquipableItem equipable)
{
// 2.1. 무기 아이템일때
if(equipable is WeaponItem)
{
// 장착중인 아이템이 있으면 해제
if (equipmentUI.slotUIList[0].HasItem)
{
// 장착중인 아이템
WeaponItem prevItem = (WeaponItem)equipmentUI.items[0];
// 인벤토리에 아이템 추가
AddItem(prevItem.Data);
// 캐릭터 정보창 슬롯의 아이콘 제거
equipmentUI.slotUIList[0].RemoveItemIcon();
// 장착 해제
prevItem.Unequip();
}
WeaponItem curItem = (WeaponItem)items[index];
equipable.Equip();
equipmentUI.SetItemIcon(curItem,curItem.WeaponData.Type, curItem.Data.ItemIcon);
}
// 2.2 방어구 아이템일때
else if(equipable is ArmorItem)
{
// 장착중인 아이템이 있으면 해제
if(equipmentUI.slotUIList[TypeToIndex()].HasItem)
{
// 장착중인 아이템
ArmorItem prevItem = (ArmorItem)equipmentUI.items[TypeToIndex()];
// 인벤토리에 아이템 추가
AddItem(prevItem.Data);
// 캐릭터 정보창 슬롯의 아이콘 제거
equipmentUI.slotUIList[TypeToIndex()].RemoveItemIcon();
// 장착 해제
prevItem.Unequip();
}
ArmorItem curItem = (ArmorItem)items[index];
equipable.Equip();
equipmentUI.SetItemIcon(curItem, curItem.ArmorData.SubType, curItem.Data.ItemIcon);
}
// 방어구 타입별 인덱스
int TypeToIndex()
{
ArmorItem curItem = (ArmorItem)items[index];
int typeIndex;
switch (curItem.ArmorData.SubType)
{
case "Shoes":
typeIndex = 1;
return typeIndex;
case "Gloves":
typeIndex = 2;
return typeIndex;
case "Top":
typeIndex = 3;
return typeIndex;
default:
return 0;
}
}
}
Remove(index);
UpdateSlot(index);
}
장비를 장착하면 캐릭터 정보에 반영이 되도록 했습니다.
- 사용하려는 아이템이 장비아이템인 경우
- 장비아이템의 타입을 판별(무기, 방어구)
- 방어구의 경우 세부타입에 따라 Index(정보창 슬롯별 인덱스) 부여
- 장착중인 장비가 있는지 확인
- 있다면 장착중인 장비를 해제한 후 장착
- 없다면 장비 장착
- 장비아이템의 타입을 판별(무기, 방어구)
- 사용하려는 아이템이 소모아이템인 경우
- 아이템 소모
EquipmentUI 클래스
반대로 캐릭터 정보창에서 우클릭을 통해 장비를 해제할 수 있도록 구현했습니다.
// 마우스 눌렀을 때 처리
private void OnPointerDown()
{
// 마우스 좌클릭(Holding)
if (Input.GetMouseButtonDown(leftClick))
{
// ...
}
// 마우스 우클릭
else if (Input.GetMouseButtonDown(rightClick))
{
// 우클릭 위치의 슬롯
EquipmentSlotUI slotUI = RaycastAndgetFirstComponent<EquipmentSlotUI>();
// 장비 장착 해제
if(slotUI != null && slotUI.HasItem)
{
EquipmentItem item = (EquipmentItem)items[slotUI.index];
inventory.AddItem(item.Data);
item.Unequip();
slotUIList[slotUI.index].RemoveItemIcon();
}
}
}
- 우클릭(장비해제)을 시도한 슬롯의 아이템정보를 가져옵니다.
- 인벤토리에 그 아이템을 추가합니다.
- 장비를 통해 향상됐던 능력치를 되돌립니다.(Unequip)
- 정보창 슬롯의 아이템을 제거합니다.
또한 인벤토리와 마찬가지로 마우스를 가져다대면 그 아이템의 툴팁이 출력되도록 했습니다.
[SerializeField] private ItemTooltipUI itemTooltipUI;
private void Update()
{
ShowOrHideTooltipUI();
}
private void UpdateTooltipUI(EquipmentSlotUI slot)
{
if (!slot.HasItem)
return;
itemTooltipUI.SetItemInfo(items[slot.index].Data);
itemTooltipUI.SetUIPosition(slot.SlotRect);
}
// 아이템 툴팁 UI 활성/비활성화
private void ShowOrHideTooltipUI()
{
// 마우스가 아이템 아이콘 위에 올라가있을 때 툴팁표시
bool isValid = pointerOverSlot != null && pointerOverSlot.HasItem && (pointerOverSlot != beginDragSlot);
if (isValid)
{
UpdateTooltipUI(pointerOverSlot);
itemTooltipUI.ShowTooltipUI();
}
else
itemTooltipUI.HideTooltipUI();
}
댓글남기기