Unity遊戲框架設計之揹包管理器

珂霖發表於2024-05-01

Unity遊戲框架設計之揹包管理器

簡單介紹

揹包系統通常分為兩個部分,第一個部分是揹包的 UI 介面,第二個部分是對揹包進行邏輯操作的管理器。

在下述程式碼中,實現了對揹包的基本邏輯操作,包括向背包新增物品,從揹包中取出物品,移動揹包中的物品和使用揹包中的物品的基本操作,並將這些操作封裝為揹包管理器。

為實現完整的揹包系統,我們需要揹包 UI 資源,並基於 MVC 模式編寫揹包 UI 的指令碼,並配合揹包管理器進行實現。

程式碼設計

public class BackpackManager : SingletonMono<BackpackManager>
{
    private Dictionary<string, BackpackMetadata> _backpackMetadataSet;

    protected override void Awake()
    {
        base.Awake();
        _backpackMetadataSet = new Dictionary<string, BackpackMetadata>();
    }

    public void CreateBackpack(string backpackName, int height, int width)
    {
        if (!_backpackMetadataSet.TryAdd(backpackName, new BackpackMetadata(backpackName, height, width)))
        {
            return;
        }
    }

    public void RemoveBackpack(string backpackName)
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return;
        }
        _backpackMetadataSet.Remove(backpackName);
    }

    public (BackpackSlotMetadata metadata, int slotX, int slotY) AddItem(string backpackName, string itemID)
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return default;
        }
        BackpackMetadata backpackMetadata = _backpackMetadataSet[backpackName];
        (int putSlotX, int putSlotY) = backpackMetadata.AddItem(itemID);
        if (putSlotX == -1 || putSlotY == -1)
        {
            return (null, -1, -1);
        }
        return (backpackMetadata.SlotSet[putSlotX][putSlotY], putSlotX, putSlotY);
    }

    public BackpackSlotMetadata TakeItem(string backpackName, int x, int y)
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return default;
        }
        return _backpackMetadataSet[backpackName].TakeItem(x, y);
    }

    public (BackpackSlotMetadata from, BackpackSlotMetadata to) MoveItem(string backpackName, int fromX, int fromY, int toX, int toY)
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return default;
        }
        return _backpackMetadataSet[backpackName].MoveItem(fromX, fromY, toX, toY);
    }

    public BackpackSlotMetadata UseItem<T>(string backpackName, int x, int y, T target, bool consume = true) where T : Component
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return default;
        }
        BackpackMetadata backpackMetadata = _backpackMetadataSet[backpackName];
        BackpackSlotMetadata slotMetadata = backpackMetadata.SlotSet[x][y];
        if (slotMetadata == null)
        {
            return default;
        }
        if (ItemManager.Instance.UseItem(slotMetadata.ItemID, target))
        {
            if (consume)
            {
                return backpackMetadata.TakeItem(x, y);
            }
            else
            {
                return slotMetadata;
            }
        }
        return default;
    }

    public bool IsExistsItem(string backpackName, int x, int y)
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return default;
        }
        return _backpackMetadataSet[backpackName].SlotSet[x][y] != null;
    }

    public List<List<BackpackSlotMetadata>> GetBackpackItemSet(string backpackName)
    {
        if (!_backpackMetadataSet.ContainsKey(backpackName))
        {
            return default;
        }
        return _backpackMetadataSet[backpackName].SlotSet;
    }

    private class BackpackMetadata
    {
        public readonly string BackpackName;
        public readonly int Height;
        public readonly int Width;
        public readonly List<List<BackpackSlotMetadata>> SlotSet;

        public BackpackMetadata(string backpackName, int height, int width)
        {
            BackpackName = backpackName;
            Height = height;
            Width = width;
            SlotSet = new List<List<BackpackSlotMetadata>>(Height);
            for (int i = 0; i < Height; i++)
            {
                SlotSet.Add(new List<BackpackSlotMetadata>(Width));
                for (int j = 0; j < Width; j++)
                {
                    SlotSet[i].Add(null);
                }
            }
        }

        public (int, int) AddItem(string itemID)
        {
            (int sameItemSlotX, int sameItemSlotY) = FindSameItemSlotCoordinate(itemID);
            if (sameItemSlotX != -1 && sameItemSlotY != -1)
            {
                SlotSet[sameItemSlotX][sameItemSlotY].Quantity++;
                return (sameItemSlotX, sameItemSlotY);
            }
            (int emptySlotX, int emptySlotY) = FindNextEmptySlotCoordinate();
            if (emptySlotX == -1 || emptySlotY == -1)
            {
                return (-1, -1);
            }
            ItemMetadata itemMetadata = ItemManager.Instance.FindItemMetadataByItemID(itemID);
            SlotSet[emptySlotX][emptySlotY] = new BackpackSlotMetadata(itemID, itemMetadata.IconItemAssetPath, 1);
            return (emptySlotX, emptySlotY);
        }

        public (BackpackSlotMetadata from, BackpackSlotMetadata to) MoveItem(int fromX, int fromY, int toX, int toY)
        {
            BackpackSlotMetadata fromSlotMetadata = SlotSet[fromX][fromY];
            BackpackSlotMetadata toSlotMetadata = SlotSet[toX][toY];
            if (fromSlotMetadata != null && toSlotMetadata != null && fromSlotMetadata.ItemID.Equals(toSlotMetadata.ItemID))
            {
                ItemMetadata itemMetadata = ItemManager.Instance.FindItemMetadataByItemID(fromSlotMetadata.ItemID);
                int itemMaxStackQuantity = itemMetadata.MaxStackQuantity;
                if (itemMaxStackQuantity - toSlotMetadata.Quantity >= fromSlotMetadata.Quantity)
                {
                    SlotSet[toX][toY].Quantity += fromSlotMetadata.Quantity;
                    SlotSet[fromX][fromY] = null;
                }
                else
                {
                    int moveQuantity = itemMaxStackQuantity - toSlotMetadata.Quantity;
                    SlotSet[toX][toY].Quantity += moveQuantity;
                    SlotSet[fromX][fromY].Quantity -= moveQuantity;
                }
                return (SlotSet[fromX][fromY], SlotSet[toX][toY]);
            }
            (SlotSet[fromX][fromY], SlotSet[toX][toY]) = (SlotSet[toX][toY], SlotSet[fromX][fromY]);
            return (SlotSet[fromX][fromY], SlotSet[toX][toY]);
        }

        public BackpackSlotMetadata TakeItem(int x, int y)
        {
            BackpackSlotMetadata backpackSlotMetadata = SlotSet[x][y];
            if (backpackSlotMetadata == null)
            {
                return default;
            }
            backpackSlotMetadata.Quantity--;
            if (backpackSlotMetadata.Quantity <= 0)
            {
                SlotSet[x][y] = null;
                backpackSlotMetadata.Quantity = 0;
            }
            return backpackSlotMetadata;
        }

        private (int, int) FindSameItemSlotCoordinate(string itemID)
        {
            int maxStackQuantity = ItemManager.Instance.FindItemMetadataByItemID(itemID).MaxStackQuantity;
            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    BackpackSlotMetadata backpackSlotMetadata = SlotSet[i][j];
                    if (backpackSlotMetadata == null)
                    {
                        continue;
                    }
                    string slotItemID = backpackSlotMetadata.ItemID;
                    if (itemID.Equals(slotItemID) && backpackSlotMetadata.Quantity < maxStackQuantity)
                    {
                        return (i, j);
                    }
                }
            }
            return (-1, -1);
        }

        private (int, int) FindNextEmptySlotCoordinate()
        {
            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    BackpackSlotMetadata backpackSlotMetadata = SlotSet[i][j];
                    if (backpackSlotMetadata == null)
                    {
                        return (i, j);
                    }
                }
            }
            return (-1, -1);
        }
    }
}

程式碼說明

(一)實現建立揹包、刪除揹包的功能。

(二)實現向背包新增物品、取出物品、移動物品和使用物品的功能。

後記

由於個人能力有限,文中不免存在疏漏之處,懇求大家斧正,一起交流,共同進步。

相關文章