二级域名网站权重,网站空间和域名绑定,简述网站与网页的区别,上海网站优化案例文章目录 先来看看最终效果前言配置商店系统数据创建另一个NPC绘制商店UI控制商店开关列出商品添加和删除物品功能添加商品到购物车购买商品购物车删除物品商店预览效果购买和出售切换出售功能保存商店数据快捷栏物品切换和使用完结 先来看看最终效果 前言
本期也是最好一期紧跟着上期继续来完善我们的库存系统实现商店系统和快捷栏的切换功能
配置商店系统数据
新增ShopItemList 配置商店物品列表信息
[CreateAssetMenu(menuName 商店系统/商店物品列表)]
public class ShopItemList : ScriptableObject
{// 商店物品列表[SerializeField] private ListShopInventoryItem _items;// 商店允许的最大金币数[SerializeField] private int _maxAllowedGold;// 商店出售物品的加价比例[SerializeField] private float _sellMarkUp;// 商店购买物品的加价比例[SerializeField] private float _buyMarkUp;public ListShopInventoryItem Items _items;public int MaxAllowedGold _maxAllowedGold;public float SellMarkUp _sellMarkUp;public float BuyMarkUp _buyMarkUp;// 商店库存物品结构体[System.Serializable]public struct ShopInventoryItem{// 物品数据public InventoryItemData ItemData;// 物品数量public int Amount;}
}新增ItemSlot物品槽的抽象基类
public abstract class ItemSlot : ISerializationCallbackReceiver
{// 对数据的引用使用[NonSerialized]属性表示在序列化时不包含该字段[NonSerialized] protected InventoryItemData itemData;// 用于序列化的字段[SerializeField] protected int _itemID -1;[SerializeField] protected int stackSize;// 对外暴露的物品数据和堆叠大小属性public InventoryItemData ItemData itemData;public int StackSize stackSize;// 清空物品槽public void ClearSlot(){itemData null;_itemID -1;stackSize 0;}// 分配一个物品给物品槽public void AssignItem(InventorySlot invSlot){if (itemData invSlot.ItemData){AddToStack(invSlot.StackSize);}else{itemData invSlot.ItemData;_itemID itemData.ID;stackSize 0;AddToStack(invSlot.StackSize);}}// 将当前背包的物品信息复制到克隆的背包系统中public void AssignItem(InventoryItemData data, int amount){if (itemData data) AddToStack(amount);else{itemData data;_itemID data.ID;stackSize 0;AddToStack(amount);}}// 将物品堆叠数量增加一定数量public void AddToStack(int amount){stackSize amount;}// 从物品堆叠中移除一定数量的物品public void RemoveFromStack(int amount){stackSize - amount;}// 在序列化之前的回调函数public void OnBeforeSerialize(){// 这里可以添加在序列化之前需要处理的逻辑}// 在反序列化之后的回调函数public void OnAfterDeserialize(){if (_itemID -1) return;var db Resources.LoadDatabase(Database);itemData db.GetItem(_itemID);}
}重构InventorySlot继承前面的ItemSlot物品槽的抽象基类
//用于表示背包系统中的一个物品槽位
[System.Serializable]
public class InventorySlot : ItemSlot
{// 构造函数用于创建一个带有物品和堆叠数量的槽public InventorySlot(InventoryItemData source, int amount){itemData source;_itemID itemData.ID;stackSize amount;}// 默认构造函数用于创建一个空槽public InventorySlot(){ClearSlot();}// 检查是否有足够的堆叠空间并返回剩余的可堆叠数量public bool EnoughRoomLeftInStack(int amountToAdd, out int amountRemaining){amountRemaining itemData.MaxStackSize - stackSize; // 计算剩余的可堆叠数量return EnoughRoomLeftInStack(amountToAdd);}// 检查是否有足够的堆叠空间public bool EnoughRoomLeftInStack(int amountToAdd){// 如果当前堆叠数量加上要添加的数量小于等于最大堆叠数量if (itemData null || itemData ! null stackSize amountToAdd itemData.MaxStackSize)return true;elsereturn false;}//更新库存槽的数据public void UpdateInventorySlot(InventoryItemData data, int amount){itemData data;_itemID itemData.ID;// 通过传入的物品数据获取物品IDstackSize amount;}/// summary/// 将物品堆叠一分为二。/// /summary/// param namesplitStack拆分后的新物品堆叠。/param/// returns是否成功拆分。/returnspublic bool SplitStack(out InventorySlot splitStack){// 如果物品堆叠为空或堆叠数量小于1则无法拆分if (stackSize 1){splitStack null;return false;}// 计算需要拆分出的物品堆叠数量约为原始堆叠数量的一半int halfStack Mathf.RoundToInt(stackSize / 2f);// 从原始堆叠中移除一部分物品RemoveFromStack(halfStack);// 创建一个新的物品堆叠作为拆分后的一半splitStack new InventorySlot(itemData, halfStack);return true;}
}新增ShopSlot基础ItemSlot对槽位进行了初始化操作
[System.Serializable]
public class ShopSlot: ItemSlot
{public ShopSlot(){ClearSlot();}
}新增ShopSystem定义了一个商店系统类包含了商店的物品槽列表、金币数量、购买和出售的加价率等属性以及相应的初始化和设置方法。这些属性和方法可以用于管理商店的状态和行为。
[System.Serializable]
public class ShopSystem
{// 商店的物品槽列表[SerializeField] private ListShopSlot _shopInventory;public ListShopSlot ShopInventory _shopInventory;// 商店的可用金币数量[SerializeField] private int _availableGold;public int AvailableGold _availableGold;// 商品的购买加价率和出售加价率[SerializeField] private float _buyMarkUp;public float BuyMarkUp _buyMarkUp;[SerializeField] private float _sellMarkUp;public float S _sellMarkUp;// 构造方法初始化商店的大小、金币数量以及购买和出售的加价率public ShopSystem(int size, int gold, float buyMarkUp, float sellMarkUp){_availableGold gold;_buyMarkUp buyMarkUp;_sellMarkUp sellMarkUp;SetShopSize(size);}// 设置商店的大小private void SetShopSize(int size){_shopInventory new ListShopSlot(size);// 创建一个指定大小的物品槽列表for (int i 0; i size; i){_shopInventory.Add(new ShopSlot());// 将新创建的物品槽添加到列表中}}// 向商店添加物品public void AddToShop(InventoryItemData data, int amount){if (ContainsItem(data, out ShopSlot shopSlot)){shopSlot.AddToStack(amount); // 如果商店已经存在相同的物品,则将物品堆叠数量增加}else{var freeSlot GetFreeSlot(); // 获取一个空闲的物品槽freeSlot.AssignItem(data, amount); // 在空闲的物品槽中添加新的物品}}// 获取一个空闲的物品槽private ShopSlot GetFreeSlot(){var freeSlot _shopInventory.FirstOrDefault(i i.ItemData null); // 查找第一个物品槽中没有物品的槽if (freeSlot null){freeSlot new ShopSlot(); // 如果没有空闲槽则创建一个新的物品槽_shopInventory.Add(freeSlot); // 将新创建的物品槽添加到列表中}return freeSlot;}// 检查商店是否已经存在某个物品并返回对应的物品槽public bool ContainsItem(InventoryItemData itemToAdd, out ShopSlot shopSlot){shopSlot _shopInventory.Find(i i.ItemData itemToAdd); // 查找物品槽列表中是否存在相同的物品return shopSlot ! null;}
}新增ShopKeeper定义了一个商店管理员类该类包含了商店所持有的物品列表、商店系统以及与玩家交互的方法和事件。你需要根据具体需求来编写交互逻辑和结束交互的实现。
// 需要附加UniqueID组件方可使用
[RequireComponent(typeof(UniqueID))]
public class ShopKeeper : MonoBehaviour, IInteractable
{// 商店所持有的物品列表[SerializeField] private ShopItemList _shopItemsHeld;[SerializeField] private ShopSystem _shopSystem;public static UnityActionShopSystem, PlayerInventoryHolder OnShopWindowRequested;private void Awake(){_shopSystem new ShopSystem(_shopItemsHeld.Items.Count, _shopItemsHeld.MaxAllowedGold, _shopItemsHeld.BuyMarkUp, _shopItemsHeld.SellMarkUp);foreach (var item in _shopItemsHeld.Items){//打印测试Debug.Log(${item.ItemData.DisplayName}:{item.Amount});_shopSystem.AddToShop(item.ItemData, item.Amount);//向商店添加物品}}// 当交互完成时触发的事件public UnityActionIInteractable OnInteractionComplete { get; set; }// 当与玩家进行交互时调用public void Interact(Interactor interactor, out bool interactSuccessful){var playerInv interactor.GetComponentPlayerInventoryHolder();if (playerInv ! null){OnShopWindowRequested?.Invoke(_shopSystem, playerInv);interactSuccessful true;}else{interactSuccessful false;Debug.LogError(没找到PlayerInventoryHolder);}}// 结束与玩家的交互public void EndInteraction(){//}
}运行查看是否还正常
设置物品价格 配置店铺数据配置对应的价格比例100的物品我们只能卖75因为店铺要收25%的利润 添加商品库存 修改UniqueID可以通过菜单初始化ID
[ContextMenu(生成ID)]
private void Generate()
{//。。。
}配置NPC脚本记得前面生成ID方法生成ID 打印 界面数据
创建另一个NPC
工具NPC
数据
绘制商店UI
绘制商店UI这里我就不多介绍了按自己喜欢绘制就行
控制商店开关
新增ShopKeeperDisplay 和ShoppingCartItemUI脚本
public class ShopKeeperDisplay : MonoBehaviour
{}public class ShoppingCartItemUI: MonoBehaviour
{}挂载脚本 模仿之前的InventoryUIController新增UIController控制商店窗口的显示隐藏
public class UIController : MonoBehaviour
{[SerializeField] private ShopKeeperDisplay _shopKeeperDisplay;private void Awake() {_shopKeeperDisplay.gameObject.SetActive(false);}void Update(){//activeInHierarchy 检查该对象是否处于活动状态if (_shopKeeperDisplay.gameObject.activeInHierarchy Input.GetKeyDown(KeyCode.Escape)){// 按下 ESC 键关闭物品界面_shopKeeperDisplay.gameObject.SetActive(false);}}// 注册事件监听器在脚本启用时调用private void OnEnable(){ShopKeeper.OnShopWindowRequested DisplayShopWindow;}// 取消事件监听器在脚本禁用时调用private void OnDisable(){ShopKeeper.OnShopWindowRequested - DisplayShopWindow;}// 显示商店窗口的方法响应ShopKeeper.OnShopWindowRequested事件private void DisplayShopWindow(ShopSystem shopSystem, PlayerInventoryHolder playerInventory){_shopKeeperDisplay.gameObject.SetActive(true);}
}挂载脚本UI
新增NPC添加配置记得图层进行修改因为前面我配置打开的图层是Box所以这里偷懒也用Box你也可以换成别的可以Inventory图层比较通用 效果打开关闭商店
列出商品
修改ShopSlotUI 控制商品槽UI
public class ShopSlotUI : MonoBehaviour
{[Header(商品图标组件)][SerializeField] private Image _itemSprite;[Header(商品名称的组件)][SerializeField] private TextMeshProUGUI _itemName; [Header(商品数量的组件)][SerializeField] private TextMeshProUGUI _itemCount;[Header(该UI对应的商店物品槽)][SerializeField] private ShopSlot _assignedItemSlot; public ShopSlot AssignedItemSlot _assignedItemSlot;[Header(将商品添加到购物车的按钮)][SerializeField] private Button _addItemToCartButton;[Header(从购物车中移除商品的按钮)][SerializeField] private Button _removeItemFromCartButton;// 声明关联的商店窗口显示对象和价格调整比例public ShopKeeperDisplay ParentDisplay { get; private set; }public float Markup { get; private set; }//加价比例private void Awake(){// 初始化UI元素_itemSprite.sprite null;_itemSprite.preserveAspect true;_itemSprite.color Color.clear;_itemName.text ;_itemCount.text ;// 添加按钮点击监听事件_addItemToCartButton?.onClick.AddListener(AddItemToCart);_removeItemFromCartButton?.onClick.AddListener(RemoveItemFromCart);// 获取关联的商店窗口显示对象ParentDisplay transform.parent.GetComponentInParentShopKeeperDisplay();}// 初始化商店物品槽UIpublic void Init(ShopSlot slot, float markup){_assignedItemSlot slot;Markup markup;UpdateUISlot();}// 更新商店物品槽UI的显示private void UpdateUISlot(){if (_assignedItemSlot.ItemData ! null){// 显示物品图标、名称、数量和价格_itemSprite.sprite _assignedItemSlot.ItemData.Icon;_itemSprite.color Color.white;_itemCount.text _assignedItemSlot.StackSize.ToString();_itemName.text ${_assignedItemSlot.ItemData.DisplayName} - {_assignedItemSlot.ItemData.GoldValue}G;}else{// 如果物品为空则清空显示_itemSprite.sprite null;_itemSprite.color Color.clear;_itemName.text ;_itemCount.text ;}}// 从购物车中移除物品private void RemoveItemFromCart(){Debug.Log(从购物车中移除物品);}// 将物品添加到购物车中private void AddItemToCart(){Debug.Log(向购物车中添加商品);}
}挂载脚本配置参数
修改ShopKeeperDisplay
public class ShopKeeperDisplay : MonoBehaviour
{[Header(商店槽预制体)][SerializeField] private ShopSlotUI _shopSlotPrefab;[Header(购物车物品预制体)][SerializeField] private ShoppingCartItemUI _shoppingCartItemPrefab;[Header(购买标签按钮)][SerializeField] private Button _buyTabButton;[Header(出售标签按钮)][SerializeField] private Button _sellTabButton;[Space][Header(购物车)][Header(购物车总价文本)][SerializeField] private TextMeshProUGUI _basketTotalText;[Header(玩家金币数文本)][SerializeField] private TextMeshProUGUI _playerGoldText;[Header(商店金币数文本)][SerializeField] private TextMeshProUGUI _shopGoldText;[Header(购买按钮)][SerializeField] private Button _buyButton;[Header(购买按钮文本)][SerializeField] private TextMeshProUGUI _buyButtonText;[Space][Header(物品预览区域)][Header(物品预览图像)][SerializeField] private Image _itemPreviewSprite;[Header(物品名称文本)][SerializeField] private TextMeshProUGUI _itemPreviewName;[Header(物品描述文本)][SerializeField] private TextMeshProUGUI _itemPreviewDescription;[Header(物品列表内容面板)][SerializeField] private GameObject _itemListContentPanel;[Header(购物车物品列表内容面板)][SerializeField] private GameObject _shoppingCartContentPanel;private int _basketTotal;// 购物车总价格private ShopSystem _shopSystem;// 商店系统组件private PlayerInventoryHolder _playerInventoryHolder;private DictionaryInventoryItemData, int _shoppingCart new DictionaryInventoryItemData, int();// 购物车字典private DictionaryInventoryItemData, ShoppingCartItemUI _shoppingCartUI new DictionaryInventoryItemData, ShoppingCartItemUI();// 显示购物车物品信息的字典public void DisplayShopWindow(ShopSystem shopSystem, PlayerInventoryHolder playerInventoryHolder){_shopSystem shopSystem;_playerInventoryHolder playerInventoryHolder;RefreshDisplay();}private void RefreshDisplay(){ClearSlots();_basketTotalText.enabled false;_buyButton.gameObject.SetActive(false);_basketTotal 0;_playerGoldText.text $PLayer Gold:{_playerInventoryHolder.PrimaryInventorySystem.Gold};_shopGoldText.text $Shop Gold:{_shopSystem.AvailableGold};DisplayShopInventory();}private void ClearSlots(){_shoppingCart new DictionaryInventoryItemData, int();_shoppingCartUI new DictionaryInventoryItemData, ShoppingCartItemUI();// 清空物品列表内容面板和购物车物品列表内容面板中的所有子物体foreach (var item in _itemListContentPanel.transform.CastTransform()){Destroy(item.gameObject);}foreach (var item in _shoppingCartContentPanel.transform.CastTransform()){Destroy(item.gameObject);}}private void DisplayShopInventory(){// 遍历商店物品列表显示每个物品槽foreach (var item in _shopSystem.ShopInventory){if (item.ItemData null) continue;var shopSlot Instantiate(_shopSlotPrefab, _itemListContentPanel.transform);shopSlot.Init(item, _shopSystem.BuyMarkUp);}}
}修改InventorySystem
[SerializeField] private int _gold;
public int Gold _gold;//用于创建一个具有指定大小的背包系统
public InventorySystem(int size)
{_gold 0;CreateInventory(size);
}
public InventorySystem(int size, int gold)
{_gold gold;CreateInventory(size);
}private void CreateInventory(int size)
{//根据指定的大小创建对应数量的空物品槽位inventorySlots new ListInventorySlot(size);for (int i 0; i size; i){inventorySlots.Add(new InventorySlot());}
}修改UIController
//显示商店窗口的方法响应ShopKeeper.OnShopWindowRequested事件
private void DisplayShopWindow(ShopSystem shopSystem, PlayerInventoryHolder playerInventory)
{_shopKeeperDisplay.gameObject.SetActive(true);_shopKeeperDisplay.DisplayShopWindow(shopSystem, playerInventory);
}挂载脚本配置参数
效果
添加和删除物品功能
修改ShopSlotUI
// 记录临时数量初始化为物品槽的堆叠数量
private int _tempAmount;public void Init(ShopSlot slot, float markup)
{_assignedItemSlot slot;Markup markup;_tempAmount slot.StackSize;// 将临时数量初始化为物品槽的堆叠数量UpdateUISlot();
}// 从购物车中移除物品
private void RemoveItemFromCart()
{Debug.Log(从购物车中移除物品);// 如果临时数量等于物品槽堆叠数量则不做任何操作直接返回if (_tempAmount _assignedItemSlot.StackSize)return;_tempAmount;ParentDisplay.RemoveItemFromCart(this);// 从购物车中移除该物品槽对应的物品_itemCount.text _tempAmount.ToString();// 更新物品数量的文本显示
}// 将物品添加到购物车中
private void AddItemToCart()
{Debug.Log(向购物车中添加商品);// 如果临时数量小于等于 0则不做任何操作直接返回if (_tempAmount 0) return;_tempAmount--;ParentDisplay.AddItemToCart(this);// 将该物品槽对应的物品添加到购物车中_itemCount.text _tempAmount.ToString();// 更新物品数量的文本显示
}修改ShopKeeperDisplay
// 从购物车中移除该物品槽对应的物品
public void RemoveItemFromCart(ShopSlotUI shopSlotUI)
{//
}
// 将该物品槽对应的物品添加到购物车中
public void AddItemToCart(ShopSlotUI shopSlotUI)
{//
}效果
添加商品到购物车
修改ShoppingCartItemUI控制购物车插槽的UI
public class ShoppingCartItemUI : MonoBehaviour
{[SerializeField] private TextMeshProUGUI _itemText;//设置物品信息文本public void SetItemText(string newString){_itemText.text newString;}
}挂载脚本 修改ShopKeeperDisplay
private bool _isSelling;// 是否为出售模式// 将该物品槽对应的物品添加到购物车中
public void AddItemToCart(ShopSlotUI shopSlotUI)
{// 获取物品数据var data shopSlotUI.AssignedItemSlot.ItemData;// 更新物品预览UpdateItemPreview(shopSlotUI);var price GetModifiedPrice(data, 1, shopSlotUI.MarkUp);// 获取修改后的价格if (_shoppingCart.ContainsKey(data)){_shoppingCart[data];var newString ${data.DisplayName} ({price}G)x{_shoppingCart[data]};// 如果购物车中已经有该物品增加其数量_shoppingCartUI[data].SetItemText(newString);// 更新显示购物车物品信息的文本组件}else{_shoppingCart.Add(data, 1);// 否则在购物车中添加该物品var shoppingCartTextObj Instantiate(_shoppingCartItemPrefab, _shoppingCartContentPanel.transform);// 创建一个新的显示购物车物品信息的文本组件var newString ${data.DisplayName} ({price}G)x1;shoppingCartTextObj.SetItemText(newString);_shoppingCartUI.Add(data, shoppingCartTextObj); // 添加到显示购物车物品信息的字典中}_basketTotal price;_basketTotalText.text $Total:{_basketTotal}G;// 更新购物车总价格的文本显示if (_basketTotal 0 !_basketTotalText.IsActive()){_basketTotalText.enabled true;// 如果购物车总价大于 0 且文本组件是激活状态启用文本组件_buyButton.gameObject.SetActive(true);// 激活“购买”按钮}CheckCartVsAvailableGold();
}// 更新物品预览
private void UpdateItemPreview(ShopSlotUI shopSlotUI) {}/// summary
/// 获取修改后的价格
/// /summary
/// param namedata物品数据/param
/// param nameamount物品数量/param
/// param namemarkUp加价比例/param
/// returns修改后的价格/returns
private static int GetModifiedPrice(InventoryItemData data, int amount, float markUp)
{// 计算基础价格即物品单价乘以数量var baseValue data.GoldValue * amount;// 计算调整后的价格即基础价格加上涨价比例所得到的价格, 结果向下取整return Mathf.FloorToInt(baseValue baseValue * markUp);
}//检查购物车与可用金币数之间的比较
private void CheckCartVsAvailableGold()
{// 如果是出售模式获取商店可用金币数否则获取玩家背包的金币数var goldToCheck _isSelling ? _shopSystem.AvailableGold : _playerInventoryHolder.PrimaryInventorySystem.Gold;// 如果购物车总价大于可用金币数将文本颜色设置为红色否则为白色_basketTotalText.color _basketTotal goldToCheck ? Color.red : Color.white;// 如果是出售模式或者玩家背包还有足够的空间则直接返回if (_isSelling || _playerInventoryHolder.PrimaryInventorySystem.CheckInventoryRemaining(_shoppingCart)) return;//库存不足_basketTotalText.text Insufficient inventory;_basketTotalText.color Color.red;
}修改InventorySystem
/// summary
/// 检查背包剩余空间是否足够容纳购物车中的物品
/// /summary
/// param nameshoppingCart购物车字典键为物品数据值为物品数量/param
/// returns如果背包剩余空间足够容纳购物车中的物品则返回true否则返回false/returns
public bool CheckInventoryRemaining(DictionaryInventoryItemData, int shoppingCart)
{var clonedSystem new InventorySystem(InventorySize);// 克隆一个背包系统for (int i 0; i InventorySize; i){// 将当前背包的物品信息复制到克隆的背包系统中clonedSystem.InventorySlots[i].AssignItem(InventorySlots[i].ItemData, InventorySlots[i].StackSize);}foreach (var kvp in shoppingCart)// 遍历购物车中的物品{for (int i 0; i kvp.Value; i)// 遍历购物车中每种物品的数量{// 尝试将物品添加到克隆的背包系统中如果添加失败即背包空间不足则返回falseif (!clonedSystem.AddToInventory(kvp.Key, 1)) return false;}}// 所有物品都能成功添加到背包中则返回truereturn true;
}效果添加物品购物车自动计算价格如果玩家金币不足则会显示红色
购买商品
修改ShopSlotUI
// 更新商店物品槽UI的显示
private void UpdateUISlot()
{if (_assignedItemSlot.ItemData ! null){// 。。。// 获取调整后的物品价格 var modifiedPrice ShopKeeperDisplay.GetModifiedPrice(_assignedItemSlot.ItemData, 1, MarkUp);// 显示物品名称和价格_itemName.text ${_assignedItemSlot.ItemData.DisplayName} - {modifiedPrice}G;}else{// 。。。}
}修改ShopKeeperDisplay
private void RefreshDisplay()
{if (_buyButton ! null){// 设置购买按钮的文字和点击事件_buyButtonText.text _isSelling ? Sell:Buy;_buyButton.onClick.RemoveAllListeners();if (_isSelling) _buyButton.onClick.AddListener(SellItems);else _buyButton.onClick.AddListener(BuyItems);}//。。。
}//出售点击事件
private void SellItems()
{//
}//购买点击事件
private void BuyItems()
{// 检查玩家是否有足够的金币购买所有物品if (_playerInventoryHolder.PrimaryInventorySystem.Gold _basketTotal){Debug.Log(玩家金币不足);return;}// 检查玩家背包是否有足够的空间存放购物车中的物品if (!_playerInventoryHolder.PrimaryInventorySystem.CheckInventoryRemaining(_shoppingCart)){Debug.Log(玩家背包位置不足);return;}// 逐个购买购物车中的物品并添加到玩家背包中foreach (var kvp in _shoppingCart){_shopSystem.PurchaseItem(kvp.Key, kvp.Value);for (int i 0; i kvp.Value; i){_playerInventoryHolder.PrimaryInventorySystem.AddToInventory(kvp.Key, 1);}}// 商店获得金币玩家失去金币_shopSystem.GainGold(_basketTotal);_playerInventoryHolder.PrimaryInventorySystem.SpendGold(_basketTotal);// 刷新商店界面显示RefreshDisplay();
}修改ShopSystem
public void PurchaseItem(InventoryItemData data, int amount)
{// 检查商店是否包含这种物品如果不包含直接返回if (!ContainsItem(data, out ShopSlot slot)) return;// 从物品槽中移除指定数量的物品slot.RemoveFromStack(amount);
}public void GainGold(int basketTotal)
{// 将购物车总额添加到可用金币中_availableGold basketTotal;
}修改InventorySystem
public void SpendGold(int basketTotal)
{// 从玩家金币中扣除购物车总额_gold - basketTotal;
}修改InventoryHolder
[SerializeField] protected int _gold;//玩家金币量protected virtual void Awake()
{SaveLoad.OnLoadGame LoadInventory;// 注册加载游戏事件//创建一个具有指定大小的背包系统inventorySystem new InventorySystem(inventorySize, _gold);
}为了测试先给玩家一些钱 效果购买后玩家金额减少商店金额增加
购物车删除物品
修改ShopKeeperDisplay完善购物车删除物品事件
// 从购物车中移除该物品槽对应的物品
public void RemoveItemFromCart(ShopSlotUI shopSlotUI)
{var data shopSlotUI.AssignedItemSlot.ItemData;var price GetModifiedPrice(data, 1, shopSlotUI.MarkUp);// 获取物品的价格if (_shoppingCart.ContainsKey(data))// 如果购物车中存在该物品{_shoppingCart[data]--;// 将物品数量减1// 更新该物品UI的显示var newString ${data.DisplayName}({price}G) x{_shoppingCart[data]};_shoppingCartUI[data].SetItemText(newString);// 如果移除该物品后该物品数量为0if (_shoppingCart[data] 0){// 从购物车和UI字典中移除该物品_shoppingCart.Remove(data);var tempobj _shoppingCartUI[data].gameObject;// 获取该物品UI对象_shoppingCartUI.Remove(data);Destroy(tempobj);// 销毁该物品UI对象}}_basketTotal - price; // 减去该物品的价格_basketTotalText.text $Total:{_basketTotal}G;// 更新购物车总价UI的文本显示// 如果购物车总价小于等于0且购物车总价UI处于激活状态if (_basketTotal 0 _basketTotalText.IsActive()){_basketTotalText.enabled false;// 关闭购物车总价UI的显示_buyButton.gameObject.SetActive(false);// 隐藏购买按钮ClearItemPreview();// 清空物品预览return;}// 检查购物车总价和玩家拥有的金币数是否匹配CheckCartVsAvailableGold();
}// 清空物品预览
private void ClearItemPreview()
{//
}效果
商店预览效果
修改ShopKeeperDisplay
private void RefreshDisplay()
{//...ClearItemPreview();// 清空物品预览if (_isSelling) DisplayPlayerInventory(); // 如果是卖家显示玩家的库存else DisplayShopInventory();// 否则显示商店的库存
}//显示玩家的库存
private void DisplayPlayerInventory()
{}// 清空物品预览
private void ClearItemPreview()
{_itemPreviewSprite.sprite null;_itemPreviewSprite.color Color.clear;_itemPreviewName.text ;_itemPreviewDescription.text ;
}// 更新物品预览
private void UpdateItemPreview(ShopSlotUI shopSlotUI) { var data shopSlotUI.AssignedItemSlot.ItemData;_itemPreviewSprite.sprite data.Icon;_itemPreviewSprite.color Color.white;_itemPreviewName.text data.DisplayName;_itemPreviewDescription.text data.Description;
}效果
购买和出售切换
修改ShopKeeperDisplay完善显示玩家的库存事件并添加切换事件
//显示玩家的库存
private void DisplayPlayerInventory()
{foreach (var item in _playerInventoryHolder.PrimaryInventorySystem.GetAllItemsHeld()){var tempSlot new ShopSlot();tempSlot.AssignItem(item.Key,item.Value);// 分配物品var shopSlot Instantiate(_shopSlotPrefab,_itemListContentPanel.transform);shopSlot.Init(tempSlot, _shopSystem.SellMarkUp);// 初始化槽位}
}public void OnBuyTabPressed()
{_isSelling false;RefreshDisplay();
}
public void OnSellTabPressed()
{_isSelling true;RefreshDisplay();
}修改InventorySystem
//获取玩家库存中所有持有的物品及其数量的方法
public DictionaryInventoryItemData, int GetAllItemsHeld()
{var distinctItems new DictionaryInventoryItemData, int();foreach (var item in inventorySlots){if (item.ItemData null) continue;// 如果物品数据为空跳过当前循环if (!distinctItems.ContainsKey(item.ItemData)) distinctItems.Add(item.ItemData, item.StackSize);// 如果字典中不存在该物品数据则添加到字典中else distinctItems[item.ItemData] item.StackSize; // 否则增加物品数量}return distinctItems;
}绑定点击事件 效果
出售功能
修改ShopKeeperDisplay
// 出售点击事件
private void SellItems()
{// 如果商店可用金币少于购物车中物品的总价值则无法出售if (_shopSystem.AvailableGold _basketTotal)return;// 遍历购物车中的物品foreach (var kvp in _shoppingCart){// 获取物品的调整价格var price GetModifiedPrice(kvp.Key, kvp.Value, _shopSystem.SellMarkUp);// 在商店中出售物品并减少商店金额_shopSystem.SellItem(kvp.Key, kvp.Value, price);// 玩家增加金币_playerInventoryHolder.PrimaryInventorySystem.GainGold(price);// 从玩家库存系统中移除出售的物品_playerInventoryHolder.PrimaryInventorySystem.RemoveItemsFromInventory(kvp.Key, kvp.Value);}// 刷新显示RefreshDisplay();
}修改ShopSystem
//出售物品
public void SellItem(InventoryItemData itemData, int quantity, int price)
{// 将物品添加到商店中AddToShop(itemData, quantity);// 减少商店金额ReduceGold(price);
}
private void ReduceGold(int price){// 减少商店金额_availableGold - price;
}修改InventorySystem
// 玩家增加金币
public void GainGold(int price)
{_gold price;
}//从库存中移除物品
public void RemoveItemsFromInventory(InventoryItemData data, int amount)
{// 检查库存中是否包含指定物品并获取包含该物品的所有库存槽if (ContainsItem(data, out ListInventorySlot inventorySlots)){foreach (var slot in inventorySlots){var stackSize slot.StackSize;if (stackSize amount){// 如果库存槽中的堆叠大小 大于 要移除的数量则从堆叠中移除指定数量的物品slot.RemoveFromStack(amount);// 触发库存槽变化事件OnInventorySlotChanged?.Invoke(slot);return;}else{// 否则则从堆叠中移除全部物品并更新剩余数量slot.RemoveFromStack(stackSize);amount - stackSize;// 触发库存槽变化事件OnInventorySlotChanged?.Invoke(slot);}}}
}修改ItemSlot
// 从物品堆叠中移除一定数量的物品
public void RemoveFromStack(int amount)
{stackSize - amount;// 如果堆叠大小小于等于0则清空槽位if(stackSize 0) ClearSlot();
}效果
保存商店数据
修改ShopKeeper
private string _id;// 商店管理员的唯一标识符
private ShopSaveData _shopSaveData;// 商店保存数据private void Awake(){//。。。_id GetComponentUniqueID().ID;// 获取商店管理员的唯一标识符_shopSaveData new ShopSaveData(_shopSystem);// 创建商店保存数据对象
}private void Start()
{// 如果存档管理器中不包含该商店管理员的数据则将其添加到存档管理器中if (!SaveGameManager.data._shopKeeperDictionary.ContainsKey(_id))SaveGameManager.data._shopKeeperDictionary.Add(_id, _shopSaveData);
}
private void OnEnable()
{// 注册加载游戏事件时调用加载物品方法SaveLoad.OnLoadGame LoadInventory;
}
private void OnDisable()
{// 取消注册加载游戏事件时调用加载物品方法SaveLoad.OnLoadGame - LoadInventory;
}
private void LoadInventory(SaveData data)
{// 如果存档中不包含该商店管理员的数据则直接返回if (!data._shopKeeperDictionary.TryGetValue(_id, out ShopSaveData shopSaveData)) return;// 将存档中的商店保存数据赋值给当前商店管理员_shopSaveData shopSaveData;// 更新商店系统_shopSystem _shopSaveData.ShopSystem;
}[System.Serializable]
public class ShopSaveData
{public ShopSystem ShopSystem;// 商店系统public ShopSaveData(ShopSystem shopSystem){ShopSystem shopSystem;}
}修改SaveData
//用于保存商店的数据
public SerializableDictionarystring, ShopSaveData _shopKeeperDictionary;_shopKeeperDictionary new SerializableDictionarystring, ShopSaveData();效果我先购买了5个木头保存在重新运行游戏加载商店数据
快捷栏物品切换和使用
修改InventorySlot_UI实现切换快捷栏颜色
private Image image;
//选中和不选中颜色
public Color selectedColor, notSelectedColor;private void Awake()
{//。。。image GetComponentImage();Deselect();// 初始化时取消选中
}//选择该槽位颜色修改
public void Select()
{image.color selectedColor;
}//取消选择该槽位颜色修改
public void Deselect()
{image.color notSelectedColor;
}配置参数 修改StaticInventoryDisplay
int selectedSlot -1;//快捷栏索引
private DictionaryKeyCode, int slotIndexMap new DictionaryKeyCode, int()
{{ KeyCode.Alpha1, 0 },{ KeyCode.Alpha2, 1 },{ KeyCode.Alpha3, 2 },{ KeyCode.Alpha4, 3 },{ KeyCode.Alpha5, 4 },{ KeyCode.Alpha6, 5 },{ KeyCode.Alpha7, 6 },{ KeyCode.Alpha8, 7 },{ KeyCode.Alpha9, 8 },{ KeyCode.Alpha0, 9 }
};protected override void Start()
{//。。。ChangeSelectedSlot(0);//默认选中第一个槽
}
private void Update()
{//快捷栏数字切换foreach (var kvp in slotIndexMap){if (Input.GetKeyDown(kvp.Key)){ChangeSelectedSlot(kvp.Value);break;}}//使用物品测试if (Input.GetKeyDown(KeyCode.Space)){InventorySlot_UI slot GetSelectedItem();if (slot slot.AssignedInventorySlot.ItemData){Debug.Log(使用了物品: slot.AssignedInventorySlot.ItemData.DisplayName);// 从物品堆叠中移除一定数量的物品slot.AssignedInventorySlot.RemoveFromStack(1);slot.UpdateUISlot();// 更新物品槽的显示}else{Debug.Log(没有物品可是使用!);}}
}//切换快捷栏选择
void ChangeSelectedSlot(int newValue)
{if (selectedSlot 0){slots[selectedSlot].Deselect();// 取消之前选中的槽位}slots[newValue].Select();// 选择新的槽位selectedSlot newValue;// 更新选中的槽位索引
}// 获取当前选中快捷栏信息
public InventorySlot_UI GetSelectedItem()
{if (slots.Length 0){InventorySlot_UI slot slots[selectedSlot];// 获取当前选中槽位if (slot ! null) return slot;// 如果有物品则返回物品}return null;//如果没有选中物品则返回null
}效果
完结
赠人玫瑰手有余香如果文章内容对你有所帮助请不要吝啬你的点赞评论和关注以便我第一时间收到反馈你的每一次支持都是我不断创作的最大动力。点赞越多更新越快哦当然如果你发现了文章中存在错误或者有更好的解决方法也欢迎评论私信告诉我哦
好了我是向宇https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者出于兴趣爱好于是最近才开始自习unity。如果你遇到任何问题也欢迎你评论私信找我 虽然有些问题我可能也不一定会但是我会查阅各方资料争取给出最好的建议希望可以帮助更多想学编程的人共勉~