【Unity实战】最全面的库存系统(五)

news2024/11/29 12:48:49

文章目录

  • 先来看看最终效果
  • 前言
  • 配置商店系统数据
  • 创建另一个NPC
  • 绘制商店UI
  • 控制商店开关
  • 列出商品
  • 添加和删除物品功能
  • 添加商品到购物车
  • 购买商品
  • 购物车删除物品
  • 商店预览效果
  • 购买和出售切换
  • 出售功能
  • 保存商店数据
  • 快捷栏物品切换和使用
  • 完结

先来看看最终效果

在这里插入图片描述
在这里插入图片描述

前言

本期也是最好一期,紧跟着上期,继续来完善我们的库存系统,实现商店系统和快捷栏的切换功能

配置商店系统数据

新增ShopItemList ,配置商店物品列表信息

[CreateAssetMenu(menuName = "商店系统/商店物品列表")]
public class ShopItemList : ScriptableObject
{
    // 商店物品列表
    [SerializeField] private List<ShopInventoryItem> _items;

    // 商店允许的最大金币数
    [SerializeField] private int _maxAllowedGold;

    // 商店出售物品的加价比例
    [SerializeField] private float _sellMarkUp;

    // 商店购买物品的加价比例
    [SerializeField] private float _buyMarkUp;

	public List<ShopInventoryItem> 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.Load<Database>("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;
        else
            return false;
    }

    //更新库存槽的数据
    public void UpdateInventorySlot(InventoryItemData data, int amount)
    {
        itemData = data;
        _itemID = itemData.ID;// 通过传入的物品数据获取物品ID
        stackSize = amount;
    }

    /// <summary>
    /// 将物品堆叠一分为二。
    /// </summary>
    /// <param name="splitStack">拆分后的新物品堆叠。</param>
    /// <returns>是否成功拆分。</returns>
    public 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 List<ShopSlot> _shopInventory;
    public List<ShopSlot> 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 List<ShopSlot>(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 UnityAction<ShopSystem, 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 UnityAction<IInteractable> OnInteractionComplete { get; set; }

    // 当与玩家进行交互时调用
    public void Interact(Interactor interactor, out bool interactSuccessful)
    {
        var playerInv = interactor.GetComponent<PlayerInventoryHolder>();
        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.GetComponentInParent<ShopKeeperDisplay>();
    }

    // 初始化商店物品槽UI
    public 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 Dictionary<InventoryItemData, int> _shoppingCart = new Dictionary<InventoryItemData, int>();// 购物车字典
    private Dictionary<InventoryItemData, ShoppingCartItemUI> _shoppingCartUI = new Dictionary<InventoryItemData, 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 Dictionary<InventoryItemData, int>();
        _shoppingCartUI = new Dictionary<InventoryItemData, ShoppingCartItemUI>();
        // 清空物品列表内容面板和购物车物品列表内容面板中的所有子物体
        foreach (var item in _itemListContentPanel.transform.Cast<Transform>())
        {
            Destroy(item.gameObject);
        }
        foreach (var item in _shoppingCartContentPanel.transform.Cast<Transform>())
        {
            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 List<InventorySlot>(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 name="data">物品数据</param>
/// <param name="amount">物品数量</param>
/// <param name="markUp">加价比例</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 name="shoppingCart">购物车字典,键为物品数据,值为物品数量</param>
/// <returns>如果背包剩余空间足够容纳购物车中的物品,则返回true;否则返回false</returns>
public bool CheckInventoryRemaining(Dictionary<InventoryItemData, 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++)// 遍历购物车中每种物品的数量
        {
            // 尝试将物品添加到克隆的背包系统中,如果添加失败(即背包空间不足),则返回false
            if (!clonedSystem.AddToInventory(kvp.Key, 1)) return false;
        }
    }
    // 所有物品都能成功添加到背包中,则返回true
    return 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);
        // 如果移除该物品后,该物品数量为0
        if (_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 Dictionary<InventoryItemData, int> GetAllItemsHeld()
{
    var distinctItems = new Dictionary<InventoryItemData, 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 List<InventorySlot> 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= GetComponent<UniqueID>().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 SerializableDictionary<string, ShopSaveData> _shopKeeperDictionary;

_shopKeeperDictionary = new SerializableDictionary<string, ShopSaveData>();

效果,我先购买了5个木头保存,在重新运行游戏,加载商店数据
在这里插入图片描述

快捷栏物品切换和使用

修改InventorySlot_UI,实现切换快捷栏颜色

private Image image;
//选中和不选中颜色
public Color selectedColor, notSelectedColor;

private void Awake()
{
    //。。。

    image = GetComponent<Image>();
    Deselect();// 初始化时取消选中
}

//选择该槽位颜色修改
public void Select()
{
    image.color = selectedColor;
}

//取消选择该槽位颜色修改
public void Deselect()
{
    image.color = notSelectedColor;
}

配置参数
在这里插入图片描述
修改StaticInventoryDisplay

int selectedSlot = -1;//快捷栏索引
private Dictionary<KeyCode, int> slotIndexMap = new Dictionary<KeyCode, 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。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1172040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新 vie-vite框架下 jtopo安装使用

官方地址 官方源码 安装下载 1.官方好像都没有给git地址&#xff0c;尝试npm安装报错 2.找到1.0.5之前的版本npm i jtopo2&#xff0c;安装成功后使用报错&#xff0c;应该是版本冲突了 1.本地引入&#xff0c; 点击官方源码下载&#xff0c;需要jtopo_npm文件 2.引入到本…

【wp】2023鹏城杯初赛 Web web1(反序列化漏洞)

考点&#xff1a; 常规的PHP反序列化漏洞双写绕过waf 签到题 源码&#xff1a; <?php show_source(__FILE__); error_reporting(0); class Hacker{private $exp;private $cmd;public function __toString(){call_user_func(system, "cat /flag");} }class A {p…

使用Renesas Flash Programmer(RFP)修改Option Byte及刷写程序

文章目录 前言配置Project修改OPBT程序刷写其他操作总结 前言 瑞萨RH850 P1H-C系列&#xff0c;在之前不知道OPBT对程序影响这么大&#xff0c;导致意外操作了其中的寄存器&#xff0c;板子锁死&#xff0c;不能再次刷写程序。本文记录下使用RFP工具刷写Option Byte需要注意的…

【C语法学习】10 - scanf()函数

文章目录 0 前言1 函数原型2 参数2.1 格式字符串2.1.1 转换说明 2.2 参数列表 3 返回值4 读取机制4.1 基本概念4.2 转换说明4.3 读取过程4.4 读取示例4.5 多参数 6 示例6.1 示例16.2 示例26.3 示例36.4 示例4 0 前言 scanf()函数虽然使用起来较为灵活&#xff0c;但是其读取机…

项目实战:分页功能实战

1、在index.html添加点击事件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src"scr…

VX-3R APRS发射试验

VX-3R本身是不带APRS功能的&#xff0c;不过可能通过外加TNC实现APRS功能。 有大佬已经用Arduino实现了相应的发射功能&#xff1a; https://github.com/handiko/Arduino-APRS 我要做的&#xff0c;就是简单修改一下代码&#xff0c;做一个转接板。 YEASU官方没有给出VX-3R的音…

基于单片机的智能饮水机系统

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、系统设计方案分析2.1 设计功能及性能分析2.2设计方案分析 二、系统的硬件设计3.1 系统设计框图系统软件设计4.1 总体介绍原理图 四、 结论 概要 现在很多学校以及家庭使用的饮水机的功能都是比较单一的&#…

【完美世界】石昊拒绝云曦相认,爱而不得,云曦悲伤无助

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料《完美世界云曦篇》最新一集&#xff0c;为了云曦&#xff0c;石昊不远十万里&#xff0c;亲自送她回家&#xff0c;这份感情之真挚&#xff0c;绝对毋庸置疑。然而&#xff0c;令人感到不解的是&…

读书笔记:《图解机械原理与构造》

通用零部件 轴&#xff1a;支撑回转零件 转轴&#xff1a;弯矩和转矩转动轴&#xff1a;转矩心轴&#xff1a;弯矩直轴曲轴软轴 轴承&#xff1a;支撑轴旋转 滑动轴承&#xff1a;承载高速旋转 径向滑动轴承&#xff1a;径向载荷推力滑动轴承&#xff1a;轴向载荷 滚动轴承&am…

故障诊断 | MATLAB实现GRNN广义回归神经网络故障诊断

故障诊断 | MATLAB实现GRNN广义回归神经网络故障诊断 目录 故障诊断 | MATLAB实现GRNN广义回归神经网络故障诊断故障诊断基本介绍模型描述预测过程程序设计参考资料故障诊断 基本介绍 MATLAB实现GRNN广义回归神经网络故障诊断 数据为多特征分类数据,输入12个特征,分3

modesim verilog仿真验证基本流程(新建工程方式)

文章目录 环境搭建一、在modelsim里创建一个新的工程二、新建verilog设计文件及仿真激励文件三、仿真结果本文演示如何使用modelsim新建工程进行功能仿真。 环境搭建 本文中采用的modelsim版本如下: modelsim altera 10.3d一、在modelsim里创建一个新的工程 打开modelsim软…

双链表详解(初始化、插入、删除、遍历)(数据结构与算法)

1. 单链表与双链表的区别 单链表&#xff08;Singly Linked List&#xff09;和双链表&#xff08;Doubly Linked List&#xff09;是两种常见的链表数据结构&#xff0c;它们在节点之间的连接方式上有所区别。 单链表&#xff1a; 单链表的每个节点包含两个部分&#xff1a;数…

利用大语言模型(LLM )提高工作效率

日常工作就是面向 google/ 百度编程&#xff0c;除了给变量命名是手动输入&#xff0c;大多时候就是通过搜索引擎拷贝别人的代码&#xff0c;或者找到旧项目一段代码拷贝过来使用。这无疑是开发人员的真实写照&#xff1b;然而&#xff0c;通过搜索引擎搜索答案&#xff0c;无疑…

【嵌入式开发工具】STM32+Keil实现软件工程搭建与开发调试

本篇文章介绍了使用Keil来对STM32F103C8芯片进行初始工程搭建&#xff0c;以及开发与工程调试的完整过程&#xff0c;帮助读者能够在实战中体会到Keil这个开发环境的使用方法&#xff0c;了解一个嵌入式工程从无到有的过程&#xff0c;并且具备快速搭建一个全新芯片对应最小软件…

CMU/MIT/清华/Umass提出生成式机器人智能体RoboGen

文章目录 导读1. Introduction2. 论文地址3. 项目主页4. 开源地址5. RoboGen Pipeline6. Experimental Results作者介绍Reference 导读 CMU/MIT/清华/Umass提出的全球首个生成式机器人智能体RoboGen&#xff0c;可以无限生成数据&#xff0c;让机器人7*24小时永不停歇地训练。…

CVE-2023-21839 weblogic rce漏洞复现

文章目录 一、漏洞影响版本二、漏洞验证三、启动反弹shell监听切换路径到jdk1.8 四、启动ldap服务五、使用CVE-2023-21839工具来进行攻击测试六、反弹shell 一、漏洞影响版本 CVE-2023-21839是Weblogic产品中的远程代码执行漏洞&#xff0c;由于Weblogic IIOP/T3协议存在缺陷&…

Python基础入门例程35-NP35 朋友的年龄是否相等(运算符)

最近的博文&#xff1a; Python基础入门例程34-NP34 除法与取模运算&#xff08;运算符&#xff09;-CSDN博客 Python基础入门例程33-NP33 乘法与幂运算&#xff08;运算符&#xff09;-CSDN博客 Python基础入门例程32-NP32 牛牛的加减器&#xff08;运算符&#xff09;-CSD…

【源码解析】聊聊SpringBean是如何初始化和创建

我们知道通过类进行修复不同的属性&#xff0c;比如单例、原型等&#xff0c;而具体的流程是怎么样的呢&#xff0c;这一篇我们开始从源码的视角分析以下。 刷新方法 在刷新容器中有一个方法&#xff0c;其实就是 Bean创建的过程。 finishBeanFactoryInitialization(beanFact…

大数据分析:基于时间序列的股票预测于分析 计算机竞赛

1 简介 Hi&#xff0c;大家好&#xff0c;这里是丹成学长&#xff0c;今天向大家介绍一个大数据项目 大数据分析&#xff1a;基于时间序列的股票预测于分析 2 时间序列的由来 提到时间序列分析技术&#xff0c;就不得不说到其中的AR/MA/ARMA/ARIMA分析模型。这四种分析方法…

一篇文章带你学会MybatisPlus~

实现MybatisPlus的简单使用&#xff1a; 数据库准备部分&#xff1a; //创建名为mybatisPlus的数据库 create database mybatisPlus;//使用该数据库 use mybatisPlus;//创建user表 CREATE TABLE user( id bigint(20) NOT NULL COMMENT 主键ID , name varchar(30) DEFAULT NUL…