Unity3D仿星露谷物语开发19之库存栏丢弃及交互道具
1、目标
从库存栏中把道具拖到游戏场景中,库存栏中道具数相应做减法或者删除道具。同时在库存栏中可以交换两个道具的位置。
2、UIInventorySlot设置Raycast属性
在UIInventorySlot中,我们只希望最外层的UIInventorySlot响应Raycast,他下面的2个子对象不需要相应Raycast,所以需要取消InventoryHighlight和Text的Raycast Target设置。
3、创建可拖动对象
(1)优化Tags.cs
为了可以方便获取Items对象,新增一个Tag名为ItemsParentTransform,并且将Items标记为ItemsParentTransform。
同时在Assets -> Scripts -> Misc的Tags.cs代码中,新增:
public const string ItemsParentTransform = "ItemsParentTransform";
此时Tags.cs的完整代码是:
using UnityEngine;public static class Tags
{public const string BoundsConfiner = "BoundsConfiner";public const string ItemsParentTransform = "ItemsParentTransform";
}
(2)优化Play.cs
修改Assets -> Scripts -> Player -> Player.cs文件。
当我们用鼠标拖动道具到场景中时,我们不希望角色跟着鼠标移动。
改动1:将已有逻辑移到判断PlayerInputIsDisabled下,当输入不被禁用时才移动。
private void Update()
{#region Player Inputif (!PlayerInputIsDisabled){ResetAnimationTrigger();PlayerMovementInput();PlayerWalkInput();// Send event to any listeners for player movement inputEventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,isPickingRight, isPickingLeft, isPickingUp, isPickingDown,isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,false, false, false, false);}#endregion Player Input
}
改动2:创建禁用/启动输入的函数
public void DisablePlayerInpupt(){PlayerInputIsDisabled = true;}public void EnablePlayerInput(){PlayerInputIsDisabled = false;}
改动3:对外提供的函数,禁用输入,并且设置用户为静止状态。
public void DisablePlayerInputAndResetMovement()
{DisablePlayerInpupt();ResetMovement();// Send event to any listeners for player movement inputEventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,isPickingRight, isPickingLeft, isPickingUp, isPickingDown,isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,false, false, false, false);
}private void ResetMovement()
{// Reset movementxInput = 0f;yInput = 0f;isRunning = false;isWalking = false;isIdle = true;
}
Player.cs完整的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : SingletonMonobehaviour<Player>
{private float xInput;private float yInput;private bool isWalking;private bool isRunning;private bool isIdle;private bool isCarrying = false;private ToolEffect toolEffect = ToolEffect.none;private bool isUsingToolRight;private bool isUsingToolLeft;private bool isUsingToolUp;private bool isUsingToolDown;private bool isLiftingToolRight;private bool isLiftingToolLeft;private bool isLiftingToolUp;private bool isLiftingToolDown;private bool isPickingRight;private bool isPickingLeft;private bool isPickingUp;private bool isPickingDown;private bool isSwingToolRight;private bool isSwingToolLeft;private bool isSwingToolUp;private bool isSwingToolDown;private Camera mainCamera;private Rigidbody2D rigidbody2D;private Direction playerDirection;private float movementSpeed;private bool _playerInputIsDisabled = false;public bool PlayerInputIsDisabled { get => _playerInputIsDisabled; set => _playerInputIsDisabled = value; }protected override void Awake(){base.Awake();rigidbody2D = GetComponent<Rigidbody2D>();mainCamera = Camera.main;}private void Update(){#region Player Inputif (!PlayerInputIsDisabled){ResetAnimationTrigger();PlayerMovementInput();PlayerWalkInput();// Send event to any listeners for player movement inputEventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,isPickingRight, isPickingLeft, isPickingUp, isPickingDown,isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,false, false, false, false);}#endregion Player Input}private void FixedUpdate(){PlayerMovement();}private void PlayerMovement(){Vector2 move = new Vector2(xInput * movementSpeed * Time.deltaTime, yInput * movementSpeed * Time.deltaTime);rigidbody2D.MovePosition(rigidbody2D.position + move);}private void ResetAnimationTrigger(){toolEffect = ToolEffect.none;isUsingToolRight = false;isUsingToolLeft = false;isUsingToolUp = false;isUsingToolDown = false;isLiftingToolRight = false;isLiftingToolLeft = false;isLiftingToolUp = false;isLiftingToolDown = false;isPickingRight = false;isPickingLeft = false;isPickingUp = false;isPickingDown = false;isSwingToolRight = false;isSwingToolLeft = false;isSwingToolUp = false;isSwingToolDown = false;}private void PlayerMovementInput(){xInput = Input.GetAxisRaw("Horizontal");yInput = Input.GetAxisRaw("Vertical");// 斜着移动if (xInput != 0 && yInput != 0) {xInput = xInput * 0.71f;yInput = yInput * 0.71f;}// 在移动if (xInput != 0 || yInput != 0) {isRunning = true;isWalking = false;isIdle = false;movementSpeed = Settings.runningSpeed;// Capture player direction for save gameif (xInput < 0){playerDirection = Direction.left;}else if (xInput > 0){playerDirection = Direction.right;}else if (yInput < 0){playerDirection = Direction.down;}else{playerDirection = Direction.up;}}else if(xInput == 0 && yInput == 0){isRunning = false;isWalking = false;isIdle = true;}}// 按住Shift键移动为walkprivate void PlayerWalkInput(){if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)){isRunning = false;isWalking = true;isIdle = false;movementSpeed = Settings.walkingSpeed;}else{isRunning = true;isWalking = false;isIdle = false;movementSpeed = Settings.runningSpeed;}}public Vector3 GetPlayerViewportPosition(){// Vector3 viewport position for player (0,0) viewport bottom left, (1,1) viewport top rightreturn mainCamera.WorldToViewportPoint(gameObject.transform.position);}public void DisablePlayerInputAndResetMovement(){DisablePlayerInpupt();ResetMovement();// Send event to any listeners for player movement inputEventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,isPickingRight, isPickingLeft, isPickingUp, isPickingDown,isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,false, false, false, false);}private void ResetMovement(){// Reset movementxInput = 0f;yInput = 0f;isRunning = false;isWalking = false;isIdle = true;}public void DisablePlayerInpupt(){PlayerInputIsDisabled = true;}public void EnablePlayerInput(){PlayerInputIsDisabled = false;}
}
(3)修改UIInventoryBar.cs
创建对可拖动物体的引用。
public GameObject inventoryBarDraggedItem;
当我们从UIInventorySlot中取出道具时会填充该字段。
此时UIInventoryBar.cs的完整代码为:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class UIInventoryBar : MonoBehaviour
{[SerializeField] private Sprite blank16x16sprite = null; // 插槽默认图案[SerializeField] private UIInventorySlot[] inventorySlot = null; // 所有插槽集合public GameObject inventoryBarDraggedItem;private RectTransform rectTransform;private bool _isInventoryBarPositionBottom = true;public bool IsInventoryBarPositionBottom { get { return _isInventoryBarPositionBottom;} set { _isInventoryBarPositionBottom = value; } }private void Awake(){rectTransform = GetComponent<RectTransform>();}private void OnDisable(){EventHandler.InventoryUpdatedEvent -= InventoryUpdated;}private void InventoryUpdated(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList){if (inventoryLocation == InventoryLocation.player){ClearInventorySlots();if (inventorySlot.Length > 0 && inventoryList.Count > 0){for (int i = 0; i < inventorySlot.Length; i++){if (i < inventoryList.Count){int itemCode = inventoryList[i].itemCode;ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);if (itemDetails != null){// add images and details to inventory item slotinventorySlot[i].inventorySlotImage.sprite = itemDetails.itemSprite;inventorySlot[i].textMeshProUGUI.text = inventoryList[i].itemQuantity.ToString();inventorySlot[i].itemDetails = itemDetails;inventorySlot[i].itemQuantity = inventoryList[i].itemQuantity;}}else{break;}}}}}private void ClearInventorySlots(){if(inventorySlot.Length > 0){// loop through inventory slots and update with blank spritefor(int i = 0; i < inventorySlot.Length; i++){inventorySlot[i].inventorySlotImage.sprite = blank16x16sprite;inventorySlot[i].textMeshProUGUI.text = "";inventorySlot[i].itemDetails = null;inventorySlot[i].itemQuantity = 0;}}}private void OnEnable(){EventHandler.InventoryUpdatedEvent += InventoryUpdated;}private void Update(){// Switch inventory bar position depending on player positionSwitchInventoryBarPosition();}private void SwitchInventoryBarPosition(){Vector3 playerViewportPosition = Player.Instance.GetPlayerViewportPosition();if (playerViewportPosition.y > 0.3f && IsInventoryBarPositionBottom == false){rectTransform.pivot = new Vector2(0.5f, 0f);rectTransform.anchorMin = new Vector2(0.5f, 0f);rectTransform.anchorMax = new Vector2(0.5f, 0f);rectTransform.anchoredPosition = new Vector2(0f, 2.5f);IsInventoryBarPositionBottom = true;}else if (playerViewportPosition.y <= 0.3f && IsInventoryBarPositionBottom == true) {rectTransform.pivot = new Vector2(0.5f, 1f);rectTransform.anchorMin = new Vector2(0.5f, 1f);rectTransform.anchorMax = new Vector2(0.5f, 1f);rectTransform.anchoredPosition = new Vector2(0f, -2.5f);IsInventoryBarPositionBottom = false;}}
}
(4)创建Dragged Item预制体
在Hierarchy -> PersistentScene -> UI -> MainGameUICanvas -> UICanvasGroup -> UIInventoryBar下创建InventoryDraggedItem空物体。设置其属性如下:
设置Sort Order=2是为了:当在UIInventoryBar上拖动时,能够更高优先级展示,从而不被Slot上的物体覆盖。
在InventoryDraggedItem下再创建Item空对象,其属性设置如下:
然后将InventoryDraggedItem拖到Assets -> Prefabs -> UI下
然后再删除Hierarchy中的InventoryDraggedItem对象。
点击InventoryDraggedItem预制体,设置其Render Mode为Screen Space - Overlay,勾选Pixel Perfect。
(5)设置inventoryBarDraggedItem属性
点击Hierarchy中的UIInventoryBar对象,将InventoryDraggedItem预制体拖入inventoryBarDraggedItem属性。
4、优化UIInventorySlot脚本
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;public class UIInventorySlot : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{private Camera mainCamera;private Transform parentItem; // 场景中的物体父类private GameObject draggedItem; // 被拖动的物体public Image inventorySlotHighlight;public Image inventorySlotImage;public TextMeshProUGUI textMeshProUGUI;[SerializeField] private UIInventoryBar inventoryBar = null;[SerializeField] private GameObject itemPrefab = null;[HideInInspector] public ItemDetails itemDetails;[HideInInspector] public int itemQuantity;private void Start(){mainCamera = Camera.main;parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;}public void OnBeginDrag(PointerEventData eventData){if(itemDetails != null) {// Disable keyboard inputPlayer.Instance.DisablePlayerInputAndResetMovement();// Instatiate gameobject as dragged itemdraggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);// Get image for dragged itemImage draggedItemImage = draggedItem.GetComponentInChildren<Image>();draggedItemImage.sprite = inventorySlotImage.sprite;}}public void OnDrag(PointerEventData eventData){// move game object as dragged itemif(!draggedItem != null){draggedItem.transform.position = Input.mousePosition;}}public void OnEndDrag(PointerEventData eventData){// Destroy game object as dragged itemif (draggedItem != null) {Destroy(draggedItem);// if drag ends over inventory bar, get item drag is over and swap thenif (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null) {}else{// else attemp to drop the item if it can be droppedif (itemDetails.canBeDropped){DropSelectedItemAtMousePosition();}}// Enable player inputPlayer.Instance.EnablePlayerInput();}}/// <summary>/// Drops the item(if selected) at the current mouse position. called by the DropItem event/// </summary>private void DropSelectedItemAtMousePosition(){if(itemDetails != null){Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));// Create item from prefab at mouse positionGameObject itemGameObject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, parentItem);Item item = itemGameObject.GetComponent<Item>();item.ItemCode = itemDetails.itemCode;// Remove item from player's inventoryInventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);}}
}
1)针对draggedItemImage.sprite = inventorySlotImage.sprite;的代码:
当点击/拖动某个具体Slot时,就会执行该Slot对应的UIInventorySlot脚本,也可以获取到对应的Slot Image信息。
2)针对item.ItemCode = itemDetails.itemCode;
itemDetails属性为UIInventorySlot的public属性。
当添加一个物体时,会触发CallInventoryUpdatedEvent事件,从而更新每个Slot的itemDetails值。
5、优化InventoryManager脚本
增加RemoveItem和RemoveItemAtPosition两个函数。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class InventoryManager : SingletonMonobehaviour<InventoryManager>
{private Dictionary<int, ItemDetails> itemDetailsDictionary;public List<InventoryItem>[] inventoryLists; // 每个位置的库存清单// 每个位置的库存数。 The index of the array is the inventory list(from the// InventoryLocation enum), and the value is the capacity of that inventory list[HideInInspector] public int[] inventoryListCapacityIntArray; [SerializeField] private SO_ItemList itemList = null;protected override void Awake(){base.Awake();// Create Inventory listsCreateInventoryLists();// Create item details dictionaryCreateItemDetailsDictionary();}private void CreateInventoryLists(){inventoryLists = new List<InventoryItem>[(int)InventoryLocation.count];for (int i = 0; i < (int)InventoryLocation.count; i++){inventoryLists[i] = new List<InventoryItem>();}// initialize inventory list capacity arrayinventoryListCapacityIntArray = new int[(int)InventoryLocation.count];// initialize player inventory list capacityinventoryListCapacityIntArray[(int)InventoryLocation.player] = Settings.playerInitialInventoryCapacity;}/// <summary>/// Populates the itemDetailsDictionary from the scriptable object items list/// </summary>private void CreateItemDetailsDictionary(){itemDetailsDictionary = new Dictionary<int, ItemDetails>();foreach (ItemDetails itemDetails in itemList.itemDetails) {itemDetailsDictionary.Add(itemDetails.itemCode, itemDetails);}}/// <summary>/// Add an item to the inventory list for the inventoryLocation and then destroy the gameObjectToDelete/// 角色拾取到物品后,物品需要消失掉/// </summary>/// <param name="inventoryLocation"></param>/// <param name="item"></param>/// <param name="gameObjectToDelete"></param>public void AddItem(InventoryLocation inventoryLocation, Item item, GameObject gameObjectToDelete){AddItem(inventoryLocation, item);Destroy(gameObjectToDelete);}/// <summary>/// Add an item to the inventory list for the inventoryLocation/// </summary>/// <param name="inventoryLocation"></param>/// <param name="item"></param>public void AddItem(InventoryLocation inventoryLocation, Item item){int itemCode = item.ItemCode;List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];// Check if inventory already contains the itemint itemPosition = FindItemInInventory(inventoryLocation, itemCode);if(itemPosition != -1){AddItemPosition(inventoryList, itemCode, itemPosition);}else{AddItemPosition(inventoryList, itemCode);}// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]); }/// <summary>/// Add item to position in the inventory/// </summary>/// <param name="inventoryList"></param>/// <param name="itemCode"></param>/// <param name="position"></param>/// <exception cref="NotImplementedException"></exception>private void AddItemPosition(List<InventoryItem> inventoryList, int itemCode, int position){InventoryItem inventoryItem = new InventoryItem();int quantity = inventoryList[position].itemQuantity + 1;inventoryItem.itemQuantity = quantity;inventoryItem.itemCode = itemCode;inventoryList[position] = inventoryItem;Debug.ClearDeveloperConsole();//DebugPrintInventoryList(inventoryList);}/// <summary>/// Remove an item from the inventory, and create a game object at the position it was dropped/// </summary>/// <param name="inventoryLocation"></param>/// <param name="itemCode"></param>public void RemoveItem(InventoryLocation inventoryLocation, int itemCode){List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];// Check if inventory already contains the itemint itemPosition = FindItemInInventory(inventoryLocation, itemCode);if(itemPosition != -1){RemoveItemAtPosition(inventoryList, itemCode, itemPosition);}// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);}private void RemoveItemAtPosition(List<InventoryItem> inventoryList, int itemCode, int position){InventoryItem inventoryItem = new InventoryItem();int quantity = inventoryList[position].itemQuantity - 1;if(quantity > 0){inventoryItem.itemQuantity = quantity;inventoryItem.itemCode = itemCode;inventoryList[position] = inventoryItem;}else{inventoryList.RemoveAt(position); }}private void DebugPrintInventoryList(List<InventoryItem> inventoryList){foreach(InventoryItem inventoryItem in inventoryList){Debug.Log("Item Description:" + InventoryManager.Instance.GetItemDetails(inventoryItem.itemCode).itemDescription + " Item Quantity:" + inventoryItem.itemQuantity);}Debug.Log("*******************************************************************************");}/// <summary>/// Add item to the end of the inventory /// </summary>/// <param name="inventoryList"></param>/// <param name="itemCode"></param>/// <exception cref="NotImplementedException"></exception>private void AddItemPosition(List<InventoryItem> inventoryList, int itemCode){InventoryItem inventoryItem = new InventoryItem(); inventoryItem.itemCode = itemCode;inventoryItem.itemQuantity = 1;inventoryList.Add(inventoryItem);//DebugPrintInventoryList(inventoryList);}/// <summary>/// Find if an itemCode is already in the inventory. Returns the item position/// in the inventory list, or -1 if the item is not in the inventory/// </summary>/// <param name="inventoryLocation"></param>/// <param name="itemCode"></param>/// <returns></returns>/// <exception cref="NotImplementedException"></exception>private int FindItemInInventory(InventoryLocation inventoryLocation, int itemCode){List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];for (int i = 0; i < inventoryList.Count; i++){if(inventoryList[i].itemCode == itemCode){return i;}}return -1;}/// <summary>/// Returns the itemDetails (from the SO_ItemList) for the itemCode, or null if the item doesn't exist/// </summary>/// <param name="itemCode"></param>/// <returns></returns>public ItemDetails GetItemDetails(int itemCode) {ItemDetails itemDetails;if(itemDetailsDictionary.TryGetValue(itemCode, out itemDetails)){return itemDetails;}else{return null;}}
}
6、填充InventorySlot面板属性
这两个属性没法从prefab中直接选择对象进行设置,所以需要在InventorySlot实例化之后的对象们中一一进行设置。
先同时选中UIInventorySlot0~11,然后设置属性如下:
此时运行程序,即可实现从库存栏中拖拽物品到游戏场景中。
7、交换库存栏中商品的位置
(1)修改UIInventorySlot脚本
首先,添加slotNumber用于标记插槽的序列号。
[SerializeField] private int slotNumber = 0; // 插槽的序列号
然后当点击结束处为slot对象时,交换两个slot的位置。
此时UIInventorySlot的完整代码如下:
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;public class UIInventorySlot : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{private Camera mainCamera;private Transform parentItem; // 场景中的物体父类private GameObject draggedItem; // 被拖动的物体public Image inventorySlotHighlight;public Image inventorySlotImage;public TextMeshProUGUI textMeshProUGUI;[SerializeField] private UIInventoryBar inventoryBar = null;[SerializeField] private GameObject itemPrefab = null;[SerializeField] private int slotNumber = 0; // 插槽的序列号[HideInInspector] public ItemDetails itemDetails;[HideInInspector] public int itemQuantity;private void Start(){mainCamera = Camera.main;parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;}public void OnBeginDrag(PointerEventData eventData){if(itemDetails != null) {// Disable keyboard inputPlayer.Instance.DisablePlayerInputAndResetMovement();// Instatiate gameobject as dragged itemdraggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);// Get image for dragged itemImage draggedItemImage = draggedItem.GetComponentInChildren<Image>();draggedItemImage.sprite = inventorySlotImage.sprite;}}public void OnDrag(PointerEventData eventData){// move game object as dragged itemif(!draggedItem != null){draggedItem.transform.position = Input.mousePosition;}}public void OnEndDrag(PointerEventData eventData){// Destroy game object as dragged itemif (draggedItem != null) {Destroy(draggedItem);// if drag ends over inventory bar, get item drag is over and swap thenif (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null) {// get the slot number where the drag endedint toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;// Swap inventory items in inventory listInventoryManager.Instance.SwapInventoryItems(InventoryLocation.player, slotNumber, toSlotNumber);}else{// else attemp to drop the item if it can be droppedif (itemDetails.canBeDropped){DropSelectedItemAtMousePosition();}}// Enable player inputPlayer.Instance.EnablePlayerInput();}}/// <summary>/// Drops the item(if selected) at the current mouse position. called by the DropItem event/// </summary>private void DropSelectedItemAtMousePosition(){if(itemDetails != null){Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));// Create item from prefab at mouse positionGameObject itemGameObject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, parentItem);Item item = itemGameObject.GetComponent<Item>();item.ItemCode = itemDetails.itemCode;// Remove item from player's inventoryInventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);}}
}
(2)修改InventoryManager脚本
新增如下代码:
public void SwapInventoryItems(InventoryLocation inventoryLocation, int fromItem, int toItem)
{// if fromItem index and toItemIndex are within the bounds of the list, not the same, and greater than or equal to zeroif(fromItem < inventoryLists[(int)inventoryLocation].Count && toItem < inventoryLists[(int)inventoryLocation].Count&& fromItem != toItem && fromItem >= 0 && toItem >= 0){InventoryItem fromInventoryItem = inventoryLists[(int)inventoryLocation][fromItem];InventoryItem toInventoryItem = inventoryLists[(int)inventoryLocation][toItem];inventoryLists[(int)inventoryLocation][toItem] = fromInventoryItem;inventoryLists[(int)inventoryLocation][fromItem] = toInventoryItem;// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);}
}
此时InventoryManager.cs完整代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class InventoryManager : SingletonMonobehaviour<InventoryManager>
{private Dictionary<int, ItemDetails> itemDetailsDictionary;public List<InventoryItem>[] inventoryLists; // 每个位置的库存清单// 每个位置的库存数。 The index of the array is the inventory list(from the// InventoryLocation enum), and the value is the capacity of that inventory list[HideInInspector] public int[] inventoryListCapacityIntArray; [SerializeField] private SO_ItemList itemList = null;protected override void Awake(){base.Awake();// Create Inventory listsCreateInventoryLists();// Create item details dictionaryCreateItemDetailsDictionary();}private void CreateInventoryLists(){inventoryLists = new List<InventoryItem>[(int)InventoryLocation.count];for (int i = 0; i < (int)InventoryLocation.count; i++){inventoryLists[i] = new List<InventoryItem>();}// initialize inventory list capacity arrayinventoryListCapacityIntArray = new int[(int)InventoryLocation.count];// initialize player inventory list capacityinventoryListCapacityIntArray[(int)InventoryLocation.player] = Settings.playerInitialInventoryCapacity;}/// <summary>/// Populates the itemDetailsDictionary from the scriptable object items list/// </summary>private void CreateItemDetailsDictionary(){itemDetailsDictionary = new Dictionary<int, ItemDetails>();foreach (ItemDetails itemDetails in itemList.itemDetails) {itemDetailsDictionary.Add(itemDetails.itemCode, itemDetails);}}/// <summary>/// Add an item to the inventory list for the inventoryLocation and then destroy the gameObjectToDelete/// 角色拾取到物品后,物品需要消失掉/// </summary>/// <param name="inventoryLocation"></param>/// <param name="item"></param>/// <param name="gameObjectToDelete"></param>public void AddItem(InventoryLocation inventoryLocation, Item item, GameObject gameObjectToDelete){AddItem(inventoryLocation, item);Destroy(gameObjectToDelete);}/// <summary>/// Add an item to the inventory list for the inventoryLocation/// </summary>/// <param name="inventoryLocation"></param>/// <param name="item"></param>public void AddItem(InventoryLocation inventoryLocation, Item item){int itemCode = item.ItemCode;List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];// Check if inventory already contains the itemint itemPosition = FindItemInInventory(inventoryLocation, itemCode);if(itemPosition != -1){AddItemPosition(inventoryList, itemCode, itemPosition);}else{AddItemPosition(inventoryList, itemCode);}// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]); }/// <summary>/// Add item to position in the inventory/// </summary>/// <param name="inventoryList"></param>/// <param name="itemCode"></param>/// <param name="position"></param>/// <exception cref="NotImplementedException"></exception>private void AddItemPosition(List<InventoryItem> inventoryList, int itemCode, int position){InventoryItem inventoryItem = new InventoryItem();int quantity = inventoryList[position].itemQuantity + 1;inventoryItem.itemQuantity = quantity;inventoryItem.itemCode = itemCode;inventoryList[position] = inventoryItem;Debug.ClearDeveloperConsole();//DebugPrintInventoryList(inventoryList);}/// <summary>/// Remove an item from the inventory, and create a game object at the position it was dropped/// </summary>/// <param name="inventoryLocation"></param>/// <param name="itemCode"></param>public void RemoveItem(InventoryLocation inventoryLocation, int itemCode){List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];// Check if inventory already contains the itemint itemPosition = FindItemInInventory(inventoryLocation, itemCode);if(itemPosition != -1){RemoveItemAtPosition(inventoryList, itemCode, itemPosition);}// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);}private void RemoveItemAtPosition(List<InventoryItem> inventoryList, int itemCode, int position){InventoryItem inventoryItem = new InventoryItem();int quantity = inventoryList[position].itemQuantity - 1;if(quantity > 0){inventoryItem.itemQuantity = quantity;inventoryItem.itemCode = itemCode;inventoryList[position] = inventoryItem;}else{inventoryList.RemoveAt(position); }}/// <summary>/// Swap item at fromItem index with item at toItem index in inventoryLocation inventory list/// </summary>/// <param name="inventoryLocation"></param>/// <param name="fromItem"></param>/// <param name="toItem"></param>public void SwapInventoryItems(InventoryLocation inventoryLocation, int fromItem, int toItem){// if fromItem index and toItemIndex are within the bounds of the list, not the same, and greater than or equal to zeroif(fromItem < inventoryLists[(int)inventoryLocation].Count && toItem < inventoryLists[(int)inventoryLocation].Count&& fromItem != toItem && fromItem >= 0 && toItem >= 0){InventoryItem fromInventoryItem = inventoryLists[(int)inventoryLocation][fromItem];InventoryItem toInventoryItem = inventoryLists[(int)inventoryLocation][toItem];inventoryLists[(int)inventoryLocation][toItem] = fromInventoryItem;inventoryLists[(int)inventoryLocation][fromItem] = toInventoryItem;// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);}}private void DebugPrintInventoryList(List<InventoryItem> inventoryList){foreach(InventoryItem inventoryItem in inventoryList){Debug.Log("Item Description:" + InventoryManager.Instance.GetItemDetails(inventoryItem.itemCode).itemDescription + " Item Quantity:" + inventoryItem.itemQuantity);}Debug.Log("*******************************************************************************");}/// <summary>/// Add item to the end of the inventory /// </summary>/// <param name="inventoryList"></param>/// <param name="itemCode"></param>/// <exception cref="NotImplementedException"></exception>private void AddItemPosition(List<InventoryItem> inventoryList, int itemCode){InventoryItem inventoryItem = new InventoryItem(); inventoryItem.itemCode = itemCode;inventoryItem.itemQuantity = 1;inventoryList.Add(inventoryItem);//DebugPrintInventoryList(inventoryList);}/// <summary>/// Find if an itemCode is already in the inventory. Returns the item position/// in the inventory list, or -1 if the item is not in the inventory/// </summary>/// <param name="inventoryLocation"></param>/// <param name="itemCode"></param>/// <returns></returns>/// <exception cref="NotImplementedException"></exception>private int FindItemInInventory(InventoryLocation inventoryLocation, int itemCode){List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];for (int i = 0; i < inventoryList.Count; i++){if(inventoryList[i].itemCode == itemCode){return i;}}return -1;}/// <summary>/// Returns the itemDetails (from the SO_ItemList) for the itemCode, or null if the item doesn't exist/// </summary>/// <param name="itemCode"></param>/// <returns></returns>public ItemDetails GetItemDetails(int itemCode) {ItemDetails itemDetails;if(itemDetailsDictionary.TryGetValue(itemCode, out itemDetails)){return itemDetails;}else{return null;}}
}
(3)更新Inspector中的slotNumber值
依次修改UIInventorySlot0~11中的SlotNumber值。
相关文章:

Unity3D仿星露谷物语开发19之库存栏丢弃及交互道具
1、目标 从库存栏中把道具拖到游戏场景中,库存栏中道具数相应做减法或者删除道具。同时在库存栏中可以交换两个道具的位置。 2、UIInventorySlot设置Raycast属性 在UIInventorySlot中,我们只希望最外层的UIInventorySlot响应Raycast,他下面…...
Kafka优势剖析-消费者组、并行消费
目录 1. 消费者组(Consumer Group) 1.1 什么是消费者组? 1.2 消费者组的工作原理 1.3 消费者组的优势 2. 并行消费(Parallel Consumption) 2.1 什么是并行消费? 2.2 并行消费的工作原理 2.3 并行消…...
Docker+Jmeter+InfluxDB+Grafana 搭建性能监控平台
当今互联网发展迅速,应用程序的性能监控显得越来越重要。 DockerJmeterInfluxDBGrafana 是一种常用的性能监控平台,可以帮助开发者快速搭建一套可靠的监控体系。在本文中,我们将介绍如何使用这些工具搭建性能监控平台,以便开发人…...

Maven 详细配置:Maven settings 配置文件的详细说明
Maven settings 配置文件是 Maven 环境的重要组成部分,它用于定义用户特定的配置信息和全局设置,例如本地仓库路径、远程仓库镜像、代理服务器以及认证信息等。settings 文件分为全局配置文件(settings.xml)和用户配置文件&#x…...
【文本分类】bert二分类
import os import torch from torch.utils.data import DataLoader, Dataset from transformers import BertTokenizer, BertForSequenceClassification, AdamW from sklearn.metrics import accuracy_score, classification_report from tqdm import tqdm# 自定义数据集 class…...
单例模式-如何保证全局唯一性?
以下是几种实现单例模式并保证全局唯一性的方法: 1. 饿汉式单例模式 class Singleton { private:// 私有构造函数,防止外部创建对象Singleton() {}// 静态成员变量,存储单例对象static Singleton instance; public:// 公有静态成员函数&…...
设计模式学习笔记——结构型模式
文章目录 适配器模式 Adapter适用场景UML 桥接模式 Bridge适用场景UML 组合模式 Composite装饰模式 Decorator外观模式 Facade享元模式 Flyweight代理模式 Proxy 适配器模式 Adapter 适用场景 希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用…...

WEB攻防-通用漏洞_文件上传_黑白盒审计流程
目录 前置知识点 Finecms-CMS文件上传 编辑 Cuppa-Cms文件上传 Metinfo-CMS 文件上传 前置知识点 思路: 黑盒就是寻找一切存在文件上传的功能应用 1 、个人用户中心是否存在文件上传功能 2 、后台管理系统是否存在文件上传功能 3 、字典目录扫描探针文件上传构…...

RabbitMQ基本介绍及简单上手
(一)什么是MQ MQ(message queue)本质上是队列,满足先入先出,只不过队列中存放的内容是消息而已,那什么是消息呢? 消息可以是字符串,json也可以是一些复杂对象 我们应用场…...
服务器证书不受信任是什么问题?
用户在访问某些网站时,可能会遇到“服务器证书不受信任”的警告。这一问题不仅影响用户的浏览体验,更可能对网站的信誉和安全性产生深远影响。那么服务器证书不受信任是什么问题呢? 服务器证书的基本概念 服务器证书是由证书颁发机构(CA)签…...
spring mvc源码学习笔记之十
前面的文章介绍了用 WebApplicationInitializer 或者 AbstractAnnotationConfigDispatcherServletInitializer 来代替 web.xml 。 我们学 java web 的时候就知道,servlet 容器会自动加载 web.xml。 那么,疑问就来了,WebApplicationInitialize…...

Ubuntu 下载安装 elasticsearch7.17.9
参考 https://blog.csdn.net/qq_26039331/article/details/115024218 https://blog.csdn.net/mengo1234/article/details/104989382 过程 来到 Es 的版本发布列表页面:https://www.elastic.co/downloads/past-releases#elasticsearch 根据自己的系统以及要安装的…...
Qt笔记:网络编程Tcp
一、铺垫 1.以下只是告诉诸位怎样去构建服务器与客户端;客户端这样构建肯定没问题;但是服务端不可能这样写,因为他是布置在Linux上的,纯数据类处理服务器,根本不可能用Qt写;这在Qt的http类中就表明了&…...
C++单例模式跨DLL调用问题梳理
问题案例: 假设有这样一个单例模式的代码 //test.h header class Test { public:static Test &instance() {static Test ins;return ins;}void foo(); };void testFoo();//test.cpp source #include "test.h"void Test::foo() {printf("%p\n&q…...

oracle闪回版本查询
闪回版本查询(Flashback Versions Query)是Oracle数据库提供的一种功能,允许用户查看某个表在特定时间范围内的所有版本。这对于审计和调试数据修改问题非常有用。通过闪回版本查询,你可以了解表中的数据在某个时间段内的变化历史…...

C#用winform窗口程序操作服务+不显示Form窗体,只显示右下角托盘图标+开机时自启动程序【附带项目地址】
服务的文章在:https://blog.csdn.net/weixin_43768573/article/details/144957941 一、用winform窗口程序操作服务 1、点击“创建新项目”,选择“Windows 服务(.NET Framework)” 2、给项目命名 3、右击项目->添加->新建项,选择“应用程序清单文件(仅限Windo…...

UOS系统和windows系统wps文档显示差异问题解决
最近在使用UOS系统的过程中,发现了一个很有意思的现象。就是在UOS系统上编辑的文档,发到windows系统上,会出现两个文档显示差异很大的情况,文档都是使用一样的wps软件打开的。到底是什么原因导致这种现象的呢?该如何解…...

JS中函数基础知识之查漏补缺(写给小白的学习笔记)
函数 函数是ECMAScript中 最有意思的部分之一, 主要是因为函数实际上是对象.-- 每个函数 都是Function类型的实例,Function也有属性和方法. 因为函数是对象,所以函数名就是指向函数对象的指针. 常用的定义函数的语法: ①函数声明 ②函数表达式 ③箭头函数 function sum (n…...
蓝桥杯训练
1对于一个字母矩阵,我们称矩阵中的一个递增序列是指在矩阵中找到两个字母,它们在同一行,同一列,或者在同一 45 度的斜线上,这两个字母从左向右看、或者从上向下看是递增的。 例如,如下矩阵中 LANN QIAO有…...

前端学习DAY33(外边距的折叠)
垂直外边距的重叠 在网页中相邻的垂直方向的外边距,会发生外边距的重叠 兄弟元素 兄弟元素之间的相邻外边距会取(绝对值)最大值,而不是取和,谁大取谁 特殊情况:如果相邻的外边距一正一负,则取两…...
ArcGIS Pro 3.4 二次开发 - 地图探索
环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 地图探索1 地图视图1.1 测试视图是否为3D1.2 设置视图模式1.3 启用视图链接2 更新地图视图范围2.1 返回上一个相机视图2.2 切换到下一个相机视角2.3 缩放到全图范围2.4 固定放大2.5 固定缩小2.6 缩放到范围2.7 缩放到一个点2.8 缩放…...
powershell 安装 .netframework3.5
在 PowerShell 中安装 .NET Framework 3.5 可以通过几种不同的方法实现,取决于你的操作系统版本。以下是几种常见的方法: 方法1:使用 DISM 命令 对于 Windows 10 和 Windows 8.1,你可以使用 DISM(Deployment Image Se…...

链表好题-多种实现
143. 重排链表 - 力扣(LeetCode) 这道题非常经典,很多大厂都作为面试题。 方法一:寻找中点翻转链表合并链表 class Solution { public:void reorderList(ListNode* head) {if (head nullptr) {return;}ListNode* mid middleNo…...

Docke启动Ktransformers部署Qwen3MOE模型实战与性能测试
docker运行Ktransformers部署Qwen3MOE模型实战及 性能测试 最开始拉取ktransformers:v0.3.1-AVX512版本,发现无论如何都启动不了大模型,后来发现是cpu不支持avx512指令集。 由于本地cpu不支持amx指令集,因此下载avx2版本镜像: …...

Sklearn 机器学习 缺失值处理 填充数据列的缺失值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 💡使用 Scikit-learn 处理数据缺失值的完整指南 在机器学习项目中,数据缺失是不可避…...

Python分形几何可视化—— 复数迭代、L系统与生物分形模拟
Python分形几何可视化—— 复数迭代、L系统与生物分形模拟 本节将深入探索分形几何的奇妙世界,实现Mandelbrot集生成器和L系统分形树工具,并通过肺部血管分形案例展示分形在医学领域的应用。我们将使用Python的NumPy进行高效计算,结合Matplo…...

每日Prompt:云朵猫
提示词 仰视,城镇的天空,一片形似猫咪的云朵,用黑色的简笔画,勾勒出猫咪的形状,可爱,俏皮,极简...
RTOS学习之重难点
📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…...

基于springboot的藏文古籍系统
博主介绍:高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了多年的设计程序开发,开发过上千套设计程序,没有什么华丽的语言,只有实实在…...

SDC命令详解:使用set_propagated_clock命令进行约束
相关阅读 SDC命令详解https://blog.csdn.net/weixin_45791458/category_12931432.html?spm1001.2014.3001.5482 目录 指定端口列表/集合 简单使用 注意事项 传播时钟是在进行了时钟树综合后,使用set_propagated_clock命令可以将一个理想时钟转换为传播时钟&#x…...