Unity游戏框架设计之背包管理器

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

代码说明

(一)实现创建背包、删除背包的功能。

(二)实现向背包添加物品、取出物品、移动物品和使用物品的功能。

后记

由于个人能力有限,文中不免存在疏漏之处,恳求大家斧正,一起交流,共同进步。

热门相关:天龙剑尊   重生之男神追妻忙   纨绔仙医   恶魔总裁霸道宠:老婆,甜似火   别吃那个鬼