当前位置: 首页 > news >正文

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、目标 从库存栏中把道具拖到游戏场景中&#xff0c;库存栏中道具数相应做减法或者删除道具。同时在库存栏中可以交换两个道具的位置。 2、UIInventorySlot设置Raycast属性 在UIInventorySlot中&#xff0c;我们只希望最外层的UIInventorySlot响应Raycast&#xff0c;他下面…...

Kafka优势剖析-消费者组、并行消费

目录 1. 消费者组&#xff08;Consumer Group&#xff09; 1.1 什么是消费者组&#xff1f; 1.2 消费者组的工作原理 1.3 消费者组的优势 2. 并行消费&#xff08;Parallel Consumption&#xff09; 2.1 什么是并行消费&#xff1f; 2.2 并行消费的工作原理 2.3 并行消…...

Docker+Jmeter+InfluxDB+Grafana 搭建性能监控平台

当今互联网发展迅速&#xff0c;应用程序的性能监控显得越来越重要。 DockerJmeterInfluxDBGrafana 是一种常用的性能监控平台&#xff0c;可以帮助开发者快速搭建一套可靠的监控体系。在本文中&#xff0c;我们将介绍如何使用这些工具搭建性能监控平台&#xff0c;以便开发人…...

Maven 详细配置:Maven settings 配置文件的详细说明

Maven settings 配置文件是 Maven 环境的重要组成部分&#xff0c;它用于定义用户特定的配置信息和全局设置&#xff0c;例如本地仓库路径、远程仓库镜像、代理服务器以及认证信息等。settings 文件分为全局配置文件&#xff08;settings.xml&#xff09;和用户配置文件&#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…...

单例模式-如何保证全局唯一性?

以下是几种实现单例模式并保证全局唯一性的方法&#xff1a; 1. 饿汉式单例模式 class Singleton { private:// 私有构造函数&#xff0c;防止外部创建对象Singleton() {}// 静态成员变量&#xff0c;存储单例对象static Singleton instance; public:// 公有静态成员函数&…...

设计模式学习笔记——结构型模式

文章目录 适配器模式 Adapter适用场景UML 桥接模式 Bridge适用场景UML 组合模式 Composite装饰模式 Decorator外观模式 Facade享元模式 Flyweight代理模式 Proxy 适配器模式 Adapter 适用场景 希望使用某个类&#xff0c; 但是其接口与其他代码不兼容时&#xff0c; 可以使用…...

WEB攻防-通用漏洞_文件上传_黑白盒审计流程

目录 前置知识点 Finecms-CMS文件上传 ​编辑 Cuppa-Cms文件上传 Metinfo-CMS 文件上传 前置知识点 思路&#xff1a; 黑盒就是寻找一切存在文件上传的功能应用 1 、个人用户中心是否存在文件上传功能 2 、后台管理系统是否存在文件上传功能 3 、字典目录扫描探针文件上传构…...

RabbitMQ基本介绍及简单上手

&#xff08;一&#xff09;什么是MQ MQ&#xff08;message queue&#xff09;本质上是队列&#xff0c;满足先入先出&#xff0c;只不过队列中存放的内容是消息而已&#xff0c;那什么是消息呢&#xff1f; 消息可以是字符串&#xff0c;json也可以是一些复杂对象 我们应用场…...

服务器证书不受信任是什么问题?

用户在访问某些网站时&#xff0c;可能会遇到“服务器证书不受信任”的警告。这一问题不仅影响用户的浏览体验&#xff0c;更可能对网站的信誉和安全性产生深远影响。那么服务器证书不受信任是什么问题呢&#xff1f; 服务器证书的基本概念 服务器证书是由证书颁发机构(CA)签…...

spring mvc源码学习笔记之十

前面的文章介绍了用 WebApplicationInitializer 或者 AbstractAnnotationConfigDispatcherServletInitializer 来代替 web.xml 。 我们学 java web 的时候就知道&#xff0c;servlet 容器会自动加载 web.xml。 那么&#xff0c;疑问就来了&#xff0c;WebApplicationInitialize…...

Ubuntu 下载安装 elasticsearch7.17.9

参考 https://blog.csdn.net/qq_26039331/article/details/115024218 https://blog.csdn.net/mengo1234/article/details/104989382 过程 来到 Es 的版本发布列表页面&#xff1a;https://www.elastic.co/downloads/past-releases#elasticsearch 根据自己的系统以及要安装的…...

Qt笔记:网络编程Tcp

一、铺垫 1.以下只是告诉诸位怎样去构建服务器与客户端&#xff1b;客户端这样构建肯定没问题&#xff1b;但是服务端不可能这样写&#xff0c;因为他是布置在Linux上的&#xff0c;纯数据类处理服务器&#xff0c;根本不可能用Qt写&#xff1b;这在Qt的http类中就表明了&…...

C++单例模式跨DLL调用问题梳理

问题案例&#xff1a; 假设有这样一个单例模式的代码 //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闪回版本查询

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

C#用winform窗口程序操作服务+不显示Form窗体,只显示右下角托盘图标+开机时自启动程序【附带项目地址】

服务的文章在:https://blog.csdn.net/weixin_43768573/article/details/144957941 一、用winform窗口程序操作服务 1、点击“创建新项目”,选择“Windows 服务(.NET Framework)” 2、给项目命名 3、右击项目->添加->新建项,选择“应用程序清单文件(仅限Windo…...

UOS系统和windows系统wps文档显示差异问题解决

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

JS中函数基础知识之查漏补缺(写给小白的学习笔记)

函数 函数是ECMAScript中 最有意思的部分之一, 主要是因为函数实际上是对象.-- 每个函数 都是Function类型的实例,Function也有属性和方法. 因为函数是对象,所以函数名就是指向函数对象的指针. 常用的定义函数的语法: ①函数声明 ②函数表达式 ③箭头函数 function sum (n…...

蓝桥杯训练

1对于一个字母矩阵&#xff0c;我们称矩阵中的一个递增序列是指在矩阵中找到两个字母&#xff0c;它们在同一行&#xff0c;同一列&#xff0c;或者在同一 45 度的斜线上&#xff0c;这两个字母从左向右看、或者从上向下看是递增的。 例如&#xff0c;如下矩阵中 LANN QIAO有…...

前端学习DAY33(外边距的折叠)

垂直外边距的重叠 在网页中相邻的垂直方向的外边距&#xff0c;会发生外边距的重叠 兄弟元素 兄弟元素之间的相邻外边距会取&#xff08;绝对值&#xff09;最大值&#xff0c;而不是取和&#xff0c;谁大取谁 特殊情况&#xff1a;如果相邻的外边距一正一负&#xff0c;则取两…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

论文阅读:Matting by Generation

今天介绍一篇关于 matting 抠图的文章&#xff0c;抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法&#xff0c;已经有很多的工作和这个任务相关。这两年 diffusion 模型很火&#xff0c;大家又开始用 diffusion 模型做各种 CV 任务了&am…...

在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7

在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤&#xff1a; 第一步&#xff1a; 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为&#xff1a; // 改为 v…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

基于单片机的宠物屋智能系统设计与实现(论文+源码)

本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢&#xff0c;连接红外测温传感器&#xff0c;可实时精准捕捉宠物体温变化&#xff0c;以便及时发现健康异常&#xff1b;水位检测传感器时刻监测饮用水余量&#xff0c;防止宠物…...