3 분 소요

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

장비를 장착하면 캐릭터 정보에 반영이 되도록 했습니다.

  • 사용하려는 아이템이 장비아이템인 경우
    1. 장비아이템의 타입을 판별(무기, 방어구)
      • 방어구의 경우 세부타입에 따라 Index(정보창 슬롯별 인덱스) 부여
    2. 장착중인 장비가 있는지 확인
      • 있다면 장착중인 장비를 해제한 후 장착
      • 없다면 장비 장착
  • 사용하려는 아이템이 소모아이템인 경우
    • 아이템 소모

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

image

댓글남기기