Eu4ng / TIL

Today I Learned
1 stars 0 forks source link

[Unity] 임시 저장 #237

Open Eu4ng opened 1 year ago

Eu4ng commented 1 year ago

ItemDefinition

using System;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
[CreateAssetMenu(fileName = "Item Definition", menuName = "Scriptable Object/Item Definition", order = int.MaxValue)]
public class ItemDefinition : ScriptableObject
{
    [SerializeField] protected string m_ItemName;
    public string ItemName => m_ItemName;

    [SerializeField] protected GameObject m_Prefab;
    public GameObject Prefab => m_Prefab;

    [SerializeField] protected int m_MaxStack;
    public int MaxStack => m_MaxStack;

    // TODO 물물교환도 가능하게 만들 예정
    [SerializeField] protected int m_Price;
    public int Price => m_Price;
}

[Serializable]
public partial class Item : IItem, IProduct
{
    [SerializeField] int m_Count;
    [SerializeField] ItemDefinition m_ItemDefinition;

    public ItemDefinition ItemDefinition
    {
        get => m_ItemDefinition;
        set => m_ItemDefinition = value;
    }

    public Item(ItemDefinition _itemDefinition)
    {
        m_ItemDefinition = _itemDefinition;
        m_Count = 1;
    }

    /* IItem 인터페이스 구현 시작 */
    public int Count
    {
        get => m_Count;
        set => m_Count = value;
    }

    public int MaxStack => m_ItemDefinition.MaxStack;

    public bool Equals(IItem _other)
    {
        var other = _other as Item;
        if (other is null) return false;
        return m_ItemDefinition == other.m_ItemDefinition;
    }

    public IItem Clone() => MemberwiseClone() as IItem;
    /* IItem 인터페이스 구현 종료 */

    /* IProduct 인터페이스 구현 시작 */
    public int Price => m_ItemDefinition.Price;
    /* IProduct 인터페이스 구현 종료 */
}
Eu4ng commented 1 year ago

Inventory

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class Inventory : MonoBehaviour, IInventory
{
    // 돈
    [SerializeField] int m_Money;
    // 아이템 인벤토리
    [SerializeField] List<InventoryItem> m_Inventories;
    [SerializeField] int m_MaxInventoryNum = 5;
    List<int> m_AvailableIndices;

    void Awake()
    {
        m_Inventories = new List<InventoryItem>(m_MaxInventoryNum);
        m_AvailableIndices = Enumerable.Range(0, m_MaxInventoryNum).ToList();
    }

    /* IInventory 인터페이스 구현 시작 */
    public int Money
    {
        get => m_Money;
        set => m_Money = value;
    }

    bool IInventory.AddItem(in IItem _item, out int _remainder)
    {
        _remainder = _item.Count;
        // 인벤토리에 이미 들어있는 아이템인 경우
        foreach(IItem inventoryItem in m_Inventories)
        {
            if (_item.Equals(inventoryItem) && !inventoryItem.IsFull())
            {
                inventoryItem.Add(_item);
            }
        }

        // 인벤토리 빈 공간에 새로 추가
        int offset;
        InventoryItem newInventoryItem;
        while (m_Inventories.Count < m_MaxInventoryNum && !_item.IsEmpty())
        {
            // 아이템 복사
            newInventoryItem = new InventoryItem()
            {
                Index = m_AvailableIndices.Min(),
                Item = _item.Clone() as IItem
            };

            // 아이템 개수 나누기
            offset = _item.Count - _item.MaxStack;
            if (offset > 0)
            {
                newInventoryItem.Count = newInventoryItem.MaxStack;
                _item.Count -= newInventoryItem.MaxStack;
            }
            else
                _item.Count = 0;

            // 인벤토리 추가
            m_Inventories.Add(newInventoryItem);
        }

        if (_remainder == _item.Count) return false;

        return true;
    }

    void IInventory.GetAvailableItem(IItem _item)
    {
        int countBuffer = 0;
        IItem inventoryItem;

        // 인벤토리 탐색
        for (int i = m_Inventories.Count() - 1; i >= 0; i--)
        {
            inventoryItem = m_Inventories[i];
            if (_item.Equals(inventoryItem))
            {
                // 인벤토리에 들어있는 아이템의 개수가 목표치보다 큰 경우
                if (inventoryItem.Count >= _item.Count)
                {
                    countBuffer = _item.Count;
                    inventoryItem.Count -= _item.Count;
                }
                else // 인벤토리에 들어있는 아이템의 개수가 목표치보다 작은 경우
                {
                    countBuffer += inventoryItem.Count;
                    m_AvailableIndices.Add(m_Inventories[i].Index); // Available Index 반환
                    m_Inventories.Remove(m_Inventories[i]); // 인벤토리에서 아이템 제거
                }

                // 아이템 개수가 목표치에 도달하면 탐색 종료
                if (countBuffer == _item.Count) break;
            }
        }

        // 가지고 있는 아이템 건네주기
        _item.Count = countBuffer;
    }

    int IInventory.CountItem(IItem _item)
    {
        int count = 0;

        // 인벤토리 탐색
        foreach (IItem inventoryItem in m_Inventories)
        {
            if (_item.Equals(inventoryItem))
                count += inventoryItem.Count;
        }

        // 아이템 개수 갱신
        return count;
    }

    /* IInventory 인터페이스 구현 종료 */

    // Inventory Item 정의
    [Serializable]
    class InventoryItem : IItem
    {
        public int Index { get; set; } 
        [field : SerializeField, SerializeReference]
        public IItem Item { get; set; }

        /* IItem 인터페이스 구현 시작 */
        public int Count
        {
            get => Item.Count;
            set => Item.Count = value;
        }
        public int MaxStack => Item.MaxStack;
        public bool Equals(IItem _other) => Item.Equals(_other);
        public IItem Clone() => MemberwiseClone() as IItem;
        /* IItem 인터페이스 구현 종료 */
    }
}

public interface IInventory
{
    public int Money { get; set; }
    bool AddItem(in IItem _item, out int _remainder);
    void GetAvailableItem(IItem _item);
    int CountItem(IItem _item);

    bool CheckItem(IItem _item)
    {
        if (CountItem(_item) >= _item.Count)
            return true;

        return false;
    }

    bool GetItem(IItem _item)
    {
        if (CheckItem(_item))
        {
            GetAvailableItem(_item);
            return true;
        }

        return false;
    }
}

public interface IItem
{
    public int Count { get; set; }
    public int MaxStack { get; }

    public bool Equals(IItem _other);
    public IItem Clone();

    // 구현 x
    public bool IsFull() => Count >= MaxStack;
    public bool IsEmpty() => Count <= 0;
    public void Add(IItem _other)
    {
        var thisItem = this as IItem;
        if (!thisItem.Equals(_other)) return; // 같은 아이템만 더한다

        int remainder = thisItem.MaxStack - thisItem.Count;
        int countToAdd = _other.Count >= remainder ? remainder : _other.Count;

        thisItem.Count += countToAdd;
        _other.Count -= countToAdd;
    }
}