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 Input
if (!PlayerInputIsDisabled)
{
ResetAnimationTrigger();
PlayerMovementInput();
PlayerWalkInput();
// Send event to any listeners for player movement input
EventHandler.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 input
EventHandler.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 movement
xInput = 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 Input
if (!PlayerInputIsDisabled)
{
ResetAnimationTrigger();
PlayerMovementInput();
PlayerWalkInput();
// Send event to any listeners for player movement input
EventHandler.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 game
if (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键移动为walk
private 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 right
return mainCamera.WorldToViewportPoint(gameObject.transform.position);
}
public void DisablePlayerInputAndResetMovement()
{
DisablePlayerInpupt();
ResetMovement();
// Send event to any listeners for player movement input
EventHandler.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 movement
xInput = 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 slot
inventorySlot[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 sprite
for(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 position
SwitchInventoryBarPosition();
}
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 input
Player.Instance.DisablePlayerInputAndResetMovement();
// Instatiate gameobject as dragged item
draggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);
// Get image for dragged item
Image draggedItemImage = draggedItem.GetComponentInChildren<Image>();
draggedItemImage.sprite = inventorySlotImage.sprite;
}
}
public void OnDrag(PointerEventData eventData)
{
// move game object as dragged item
if(!draggedItem != null)
{
draggedItem.transform.position = Input.mousePosition;
}
}
public void OnEndDrag(PointerEventData eventData)
{
// Destroy game object as dragged item
if (draggedItem != null)
{
Destroy(draggedItem);
// if drag ends over inventory bar, get item drag is over and swap then
if (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null)
{
}
else
{
// else attemp to drop the item if it can be dropped
if (itemDetails.canBeDropped)
{
DropSelectedItemAtMousePosition();
}
}
// Enable player input
Player.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 position
GameObject itemGameObject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, parentItem);
Item item = itemGameObject.GetComponent<Item>();
item.ItemCode = itemDetails.itemCode;
// Remove item from player's inventory
InventoryManager.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 lists
CreateInventoryLists();
// Create item details dictionary
CreateItemDetailsDictionary();
}
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 array
inventoryListCapacityIntArray = new int[(int)InventoryLocation.count];
// initialize player inventory list capacity
inventoryListCapacityIntArray[(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 item
int itemPosition = FindItemInInventory(inventoryLocation, itemCode);
if(itemPosition != -1)
{
AddItemPosition(inventoryList, itemCode, itemPosition);
}
else
{
AddItemPosition(inventoryList, itemCode);
}
// Send event that inventory has been updated
EventHandler.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 item
int itemPosition = FindItemInInventory(inventoryLocation, itemCode);
if(itemPosition != -1)
{
RemoveItemAtPosition(inventoryList, itemCode, itemPosition);
}
// Send event that inventory has been updated
EventHandler.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 input
Player.Instance.DisablePlayerInputAndResetMovement();
// Instatiate gameobject as dragged item
draggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);
// Get image for dragged item
Image draggedItemImage = draggedItem.GetComponentInChildren<Image>();
draggedItemImage.sprite = inventorySlotImage.sprite;
}
}
public void OnDrag(PointerEventData eventData)
{
// move game object as dragged item
if(!draggedItem != null)
{
draggedItem.transform.position = Input.mousePosition;
}
}
public void OnEndDrag(PointerEventData eventData)
{
// Destroy game object as dragged item
if (draggedItem != null)
{
Destroy(draggedItem);
// if drag ends over inventory bar, get item drag is over and swap then
if (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null)
{
// get the slot number where the drag ended
int toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;
// Swap inventory items in inventory list
InventoryManager.Instance.SwapInventoryItems(InventoryLocation.player, slotNumber, toSlotNumber);
}
else
{
// else attemp to drop the item if it can be dropped
if (itemDetails.canBeDropped)
{
DropSelectedItemAtMousePosition();
}
}
// Enable player input
Player.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 position
GameObject itemGameObject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, parentItem);
Item item = itemGameObject.GetComponent<Item>();
item.ItemCode = itemDetails.itemCode;
// Remove item from player's inventory
InventoryManager.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 zero
if(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 updated
EventHandler.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 lists
CreateInventoryLists();
// Create item details dictionary
CreateItemDetailsDictionary();
}
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 array
inventoryListCapacityIntArray = new int[(int)InventoryLocation.count];
// initialize player inventory list capacity
inventoryListCapacityIntArray[(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 item
int itemPosition = FindItemInInventory(inventoryLocation, itemCode);
if(itemPosition != -1)
{
AddItemPosition(inventoryList, itemCode, itemPosition);
}
else
{
AddItemPosition(inventoryList, itemCode);
}
// Send event that inventory has been updated
EventHandler.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 item
int itemPosition = FindItemInInventory(inventoryLocation, itemCode);
if(itemPosition != -1)
{
RemoveItemAtPosition(inventoryList, itemCode, itemPosition);
}
// Send event that inventory has been updated
EventHandler.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 zero
if(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 updated
EventHandler.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值。