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);
}
}
}
程式碼說明
(一)實現建立揹包、刪除揹包的功能。
(二)實現向背包新增物品、取出物品、移動物品和使用物品的功能。
後記
由於個人能力有限,文中不免存在疏漏之處,懇求大家斧正,一起交流,共同進步。