• [Unity独立/合作开发]实现背包系统中物品的拾取拖拽掉落还有换位置


    米娜桑扩你急哇,大家好久不见,由于最近一直在忙活比赛的项目,什么画画啊写代码啊一直都没时间跟大伙更新一期视频,今天就来点大家想看的东西,我们来实现背包系统中物品的拾取拖拽掉落还有换位置。

    学习目标:

      首先学习之前所需要的必备知识有:ScriptableObject,数据结构链表的使用,以及一些涉及到UnityEditor相关便于我们开发的,射线相关的UI以及EventSystems的命名空间,那么现在就开始吧。


    学习内容:

      首先我们需要整体的对物品进行一个描述,如它是什么类型的,是可以浇花的,还是可以种地的,还是可以攻击的,我们将在一个只用来写枚举的C#脚本中创建它,

    1. public enum ItemType
    2. {
    3. Seed,
    4. Commodity,
    5. Watering_tool,
    6. Hoeing_tool,
    7. Chopping_tool,
    8. Breaking_tool,
    9. Reping_tool,
    10. Collecting_tool,
    11. Reapable_scenery,
    12. Furinture,
    13. None,
    14. Count,
    15. }

    只有枚举没有类名,

    首先我们先写展现Item细节类的脚本ItemDetails,我们使用
    [System.Serializable]让它即使不能被挂载到游戏对象上,也能被其它类识别然后调用,

    1. using UnityEngine;
    2. [System.Serializable]
    3. public class ItemDetails
    4. {
    5. public int itemCode;
    6. public ItemType itemType;
    7. public string itemDescription;
    8. public Sprite itemSprite;
    9. public string itemLongDescription;
    10. public short itemUseGridRadius;
    11. public float itemUseRadius;
    12. public bool isStartingItem;
    13. public bool canBePickUp;
    14. public bool canBeDropped;
    15. public bool canBeEaten;
    16. public bool canBeCarried;
    17. }

    然后我们就可以开始写Item类了,(这里[ItemCodeDescription]需要自定义的,一会讲)ItemCode属性是非常重要的东西,每一个独一无二的Item都将拥有属于自己ItemCode,然后我们就可以在ItemCode中调用它

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. public class Item : MonoBehaviour
    6. {
    7. [ItemCodeDescription][SerializeField] private int _itemCode;
    8. private SpriteRenderer spriteRenderer;
    9. public int ItemCode
    10. {
    11. get
    12. {
    13. return _itemCode;
    14. }
    15. set
    16. {
    17. _itemCode = value;
    18. }
    19. }
    20. private void Awake()
    21. {
    22. spriteRenderer = GetComponentInChildren();
    23. }
    24. private void Start()
    25. {
    26. if(ItemCode != 0)
    27. {
    28. Init(ItemCode);
    29. }
    30. }
    31. public void Init(int itemCodeParams)
    32. {
    33. }
    34. }

    (这里[ItemCodeDescription]需要自定义的,一会讲)

    弄完后我们需要一个链表数组来存储所有的Item

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. [CreateAssetMenu(fileName = "so_Itemlist", menuName = "Scriptable Objects/Item list")]
    5. public class SO_ItemList : ScriptableObject
    6. {
    7. public List itemDetails;
    8. }

    回到Unity,我们为每一个Item创建独一无二的数据,这里就根据你的需求来填写这些数据了。

     

     

    那么这个 iItemCodeDescription是从哪里来的呢,这里就设计到我们的UnityEditor知识了。

    在你的脚本文件夹中创建两个如下名字的脚本

    先 打开Attribute,我们只需要让它继承PropertyAttribute即可

    1. using UnityEngine;
    2. public class ItemCodeDescriptionAttribute : PropertyAttribute
    3. {
    4. }

    剩下的脚本则需要引用UnityEditor命名空间,我们将在GUI上更改信息

    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEditor;
    4. [CustomPropertyDrawer(typeof(ItemCodeDescriptionAttribute))] //告诉系统自定义特性名叫ItemCodeDescriptionAttribute
    5. public class ItemCodeDescriptionDrawer : PropertyDrawer
    6. {
    7. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    8. {
    9. return EditorGUI.GetPropertyHeight(property) * 2;
    10. }
    11. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    12. {
    13. EditorGUI.BeginProperty(position, label, property);
    14. if(property.propertyType == SerializedPropertyType.Integer)
    15. {
    16. EditorGUI.BeginChangeCheck();
    17. var newValue = EditorGUI.IntField(new Rect(position.x, position.y, position.width, position.height / 2),label,property.intValue);
    18. EditorGUI.LabelField(new Rect(position.x, position.y + position.height /2 , position.width, position.height / 2), "Item Description",
    19. GetItemDescription(property.intValue));
    20. //如果Item的ItemCode的值发生改变,则面板上的值也要发生变化
    21. if (EditorGUI.EndChangeCheck())
    22. {
    23. property.intValue = newValue;
    24. }
    25. }
    26. EditorGUI.EndProperty();
    27. }
    28. private string GetItemDescription(int itemCode)
    29. {
    30. SO_ItemList so_itemList;
    31. so_itemList = AssetDatabase.LoadAssetAtPath("Assets/Scriptable Object Assets/Items/so_Itemlist.asset", typeof(SO_ItemList)) as SO_ItemList;
    32. List itemDetailLists = so_itemList.itemDetails;
    33. ItemDetails itemDetail = itemDetailLists.Find(x => x.itemCode == itemCode);
    34. if(itemDetail!= null)
    35. {
    36. return itemDetail.itemDescription;
    37. }
    38. else
    39. {
    40. return "";
    41. }
    42. }
    43. }

     我们会用到链表系统SO_ItmeList,并且用 AssetDatabase.LoadAssetAtPath获取本地路径,找到SO_ItmeList里面所有的链表,然后用Find函数寻找相同itemCode的ItemDetails,如果找到了就返回它的描述字符串,否则返回空双引号"";

    接下来就可以用你自定义的特性了

     接下来我们创建一个父预制体,如下所示添加Item类,

      

     把Item做成预制体后创建它的子对象操作如下

     

    比如我做了一个南瓜Item,可以看到,我们给南瓜对应的编号,底下的Decsription就会跟着改变,我是10001,描述的内容就是南瓜 

     我们可以添加一些效果给场景中的Item类,例如有些Item的属性中是不能被拾取,例如这些草仅仅是风景用途

     我们可以让在经过草的时候摆动一下。

    写个新脚本用协程来实现

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ItemNudge : MonoBehaviour
    5. {
    6. [SerializeField] private float pauseTime = 0.04f;
    7. private WaitForSeconds pause;
    8. private bool isAnimating = false;
    9. private void Awake()
    10. {
    11. pause = new WaitForSeconds(pauseTime);
    12. }
    13. private void OnTriggerEnter2D(Collider2D collision)
    14. {
    15. if (!isAnimating)
    16. {
    17. if(gameObject.transform.position.x < collision.transform.position.x) //如果在左边
    18. {
    19. StartCoroutine(nameof(RotateAndClock));
    20. }
    21. else
    22. {
    23. StartCoroutine(nameof(RotateClock));
    24. }
    25. }
    26. }
    27. private IEnumerator RotateAndClock()
    28. {
    29. isAnimating = true;
    30. for (int i = 0; i < 4; i++)
    31. {
    32. gameObject.transform.GetChild(0).Rotate(0f, 0f, 2f);
    33. yield return pause;
    34. }
    35. for (int i = 0; i < 5; i++)
    36. {
    37. gameObject.transform.GetChild(0).Rotate(0f, 0f, -2f);
    38. yield return pause;
    39. }
    40. gameObject.transform.GetChild(0).Rotate(0f, 0f, 2f);
    41. yield return pause;
    42. isAnimating = false;
    43. }
    44. private IEnumerator RotateClock()
    45. {
    46. isAnimating = true;
    47. for (int i = 0; i < 4; i++)
    48. {
    49. gameObject.transform.GetChild(0).Rotate(0f, 0f, -2f);
    50. yield return pause;
    51. }
    52. for (int i = 0; i < 5; i++)
    53. {
    54. gameObject.transform.GetChild(0).Rotate(0f, 0f, 2f);
    55. yield return pause;
    56. }
    57. gameObject.transform.GetChild(0).Rotate(0f, 0f, -2f);
    58. yield return pause;
    59. isAnimating = false;
    60. }
    61. }

     再回到Item类在初始化方法中判断是不是ItemType.Reapable_scenery,是的话就给它添加我们刚刚新建的脚本

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. public class Item : MonoBehaviour
    6. {
    7. [ItemCodeDescription][SerializeField] private int _itemCode;
    8. private SpriteRenderer spriteRenderer;
    9. public int ItemCode
    10. {
    11. get
    12. {
    13. return _itemCode;
    14. }
    15. set
    16. {
    17. _itemCode = value;
    18. }
    19. }
    20. private void Awake()
    21. {
    22. spriteRenderer = GetComponentInChildren();
    23. }
    24. private void Start()
    25. {
    26. if(ItemCode != 0)
    27. {
    28. Init(ItemCode);
    29. }
    30. }
    31. public void Init(int itemCodeParams)
    32. {
    33. if(itemCodeParams != 0)
    34. {
    35. ItemCode = itemCodeParams;
    36. ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(ItemCode);
    37. spriteRenderer.sprite = itemDetails.itemSprite;
    38. if(itemDetails.itemType == ItemType.Reapable_scenery) //如果该物品是风景类型的话
    39. {
    40. gameObject.AddComponent();
    41. }
    42. }
    43. }
    44. }

    接下来来实现如何做到拾取物品

      我们给玩家添加一个新脚本就叫ItemPickUp,因为每一个Item身上都有一个触发器

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ItemPickUp : MonoBehaviour
    5. {
    6. private void OnTriggerEnter2D(Collider2D collision)
    7. {
    8. if(collision.TryGetComponent(out Item item))
    9. {
    10. ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(item.ItemCode);
    11. if (itemDetails.canBePickUp)
    12. {
    13. }
    14. }
    15. }
    16. }

     我们还需要一个背包系统InventoryManager,它和SO_ItemList的区别在于,它是一个存储你所能收集到的Item,而SO_ItemList则更像是一个登记所有物品信息的。

    我们要写一个新枚举,我们将使用二维数组来控制使用的是哪一个背包。

    1. public enum InventoryLocation
    2. {
    3. player,
    4. chest,
    5. count,
    6. }
    7. public enum ItemType
    8. {
    9. Seed,
    10. Commodity,
    11. Watering_tool,
    12. Hoeing_tool,
    13. Chopping_tool,
    14. Breaking_tool,
    15. Reping_tool,
    16. Collecting_tool,
    17. Reapable_scenery,
    18. Furinture,
    19. None,
    20. Count,
    21. }

     接着创建一个结构来管理背包中的Item

    1. [System.Serializable]
    2. public struct InventoryItem
    3. {
    4. public int itemCode;
    5. public int itemQuantity;
    6. }

    还需要一个单例模式

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class Singleton<T> : MonoBehaviour where T : Component
    5. {
    6. private static T instance;
    7. public static T Instance
    8. {
    9. get
    10. {
    11. return instance;
    12. }
    13. }
    14. protected virtual void Awake()
    15. {
    16. if(instance == null)
    17. {
    18. instance = this as T;
    19. }
    20. else
    21. {
    22. Destroy(gameObject);
    23. }
    24. }
    25. }

    w我们需要一个事件处理程序来完成它

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. public static class EventHandler
    5. {
    6. public static event Action> InventoryUpdateEvent;
    7. public static void CallInventoryUpdateEvent(InventoryLocation inventoryLocation,List inventoryLists)
    8. {
    9. if(InventoryUpdateEvent != null)
    10. {
    11. InventoryUpdateEvent(inventoryLocation, inventoryLists);
    12. }
    13. }
    14. }

    准备工作就绪,我们就可以继续写InventoryManager了

    首先要考虑它的添加和移除物品,以及初始化的时候要创建的数组

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. public class InventoryManager : Singleton<InventoryManager>
    6. {
    7. private Dictionary<int, ItemDetails> itemDetailsDictionary; //通过itemDetails上的itemCode(int类型)的值寻找对应的itemDetails
    8. public List[] inventoryLists; //一个存放所有InventoryItem的链表的数组
    9. [HideInInspector] public int[] inventoryListInCapcityInArray; //背包容量大小默认是12
    10. [SerializeField] private SO_ItemList itemLists; //SO_ItemList登记过所有Item
    11. protected override void Awake()
    12. {
    13. base.Awake();
    14. CreateInventoryLists();
    15. CreateItemDetailsDictionary();
    16. }
    17. private void CreateInventoryLists()
    18. {
    19. inventoryLists = new List[(int)InventoryLocation.count];//我们有0号背包系统的链表和1号背包系统的链表
    20. for (int i = 0; i < (int)InventoryLocation.count; i++)
    21. {
    22. inventoryLists[i] = new List();
    23. }
    24. //出事背包容量列表
    25. inventoryListInCapcityInArray = new int[(int)InventoryLocation.count];
    26. //初始化玩家背包(0号背包)容量
    27. inventoryListInCapcityInArray[(int)InventoryLocation.player] = Settings.playerIntialInventoryCapcity;
    28. }
    29. public void AddItem(InventoryLocation inventoryLocation, Item item,GameObject gameObjectToDelete)
    30. {
    31. AddItem(inventoryLocation, item);
    32. Destroy(gameObjectToDelete);
    33. }
    34. ///
    35. /// 添加物品
    36. ///
    37. ///
    38. ///
    39. public void AddItem(InventoryLocation inventoryLocation,Item item)
    40. {
    41. int itemCode = item.ItemCode;
    42. List inventoryList = inventoryLists[(int)inventoryLocation];
    43. int itemPositon = FindItemInventory(inventoryLocation, itemCode);
    44. if(itemPositon != -1)
    45. {
    46. AddItemAtPosition(inventoryList, itemCode, itemPositon);//如果找到物品就数量++
    47. }
    48. else
    49. {
    50. AddItemAtPosition(inventoryList, itemCode); //找不到就在空白处新添加物品
    51. }
    52. EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
    53. }
    54. ///
    55. /// 交换物品顺序
    56. ///
    57. ///
    58. ///
    59. ///
    60. public void SwapInventoryItems(InventoryLocation inventoryLocation, int fromSlotNumber, int toSlotNumber)
    61. {
    62. if(fromSlotNumber < inventoryLists[(int)inventoryLocation].Count && toSlotNumber < inventoryLists[(int)inventoryLocation].Count &&
    63. fromSlotNumber != toSlotNumber && toSlotNumber >= 0)
    64. {
    65. InventoryItem fromSlotItem = inventoryLists[(int)inventoryLocation][fromSlotNumber];
    66. InventoryItem toSlotItem = inventoryLists[(int)inventoryLocation][toSlotNumber];
    67. inventoryLists[(int)inventoryLocation][toSlotNumber] = fromSlotItem;
    68. inventoryLists[(int)inventoryLocation][fromSlotNumber] = toSlotItem;
    69. EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
    70. }
    71. }
    72. ///
    73. /// 将物品添加到背包的位置
    74. ///
    75. ///
    76. ///
    77. private void AddItemAtPosition(List inventoryList, int itemCode)
    78. {
    79. InventoryItem inventoryItem = new InventoryItem();
    80. inventoryItem.itemCode = itemCode;
    81. inventoryItem.itemQuantity = 1;
    82. inventoryList.Add(inventoryItem);
    83. }
    84. ///
    85. /// 对于背包中已经有的物品就加数量
    86. ///
    87. ///
    88. ///
    89. ///
    90. private void AddItemAtPosition(List inventoryList, int itemCode, int positon)
    91. {
    92. InventoryItem inventoryItem = new InventoryItem();
    93. int quantity = inventoryList[positon].itemQuantity + 1;
    94. inventoryItem.itemCode = itemCode;
    95. inventoryItem.itemQuantity = quantity;
    96. inventoryList[positon] = inventoryItem;
    97. Debug.ClearDeveloperConsole();
    98. }
    99. internal void RemoveItem(InventoryLocation inventoryLocation, int itemCode)
    100. {
    101. List inventoryList = inventoryLists[(int)inventoryLocation];
    102. int itemPosition = FindItemInventory(inventoryLocation, itemCode);
    103. if (itemPosition != -1)
    104. {
    105. RemoveItemAtPosition(inventoryList,itemCode,itemPosition);
    106. }
    107. EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
    108. }
    109. private void RemoveItemAtPosition(List inventoryList, int itemCode, int position)
    110. {
    111. InventoryItem inventoryItem = new InventoryItem();
    112. int quantity = inventoryList[position].itemQuantity - 1;
    113. if(quantity > 0)
    114. {
    115. inventoryItem.itemQuantity = quantity;
    116. inventoryItem.itemCode = itemCode;
    117. inventoryList[position] = inventoryItem;
    118. }
    119. else
    120. {
    121. inventoryList.RemoveAt(position);
    122. }
    123. }
    124. ///
    125. /// 判断物品是否在背包里面,找不到就return -1,找到的话就返回发现物品的位置
    126. ///
    127. ///
    128. ///
    129. ///
    130. private int FindItemInventory(InventoryLocation inventoryLocation,int itemCode)
    131. {
    132. List inventoryList = inventoryLists[(int)inventoryLocation];
    133. for (int i = 0; i < inventoryList.Count; i++)
    134. {
    135. if(inventoryList[i].itemCode == itemCode)
    136. {
    137. return i;
    138. }
    139. }
    140. return -1;
    141. }
    142. ///
    143. /// 初始化创建的字典
    144. ///
    145. private void CreateItemDetailsDictionary()
    146. {
    147. itemDetailsDictionary = new Dictionary<int, ItemDetails>();
    148. foreach (var itemDetails in itemLists.itemDetails)
    149. {
    150. itemDetailsDictionary.Add(itemDetails.itemCode,itemDetails);
    151. }
    152. }
    153. ///
    154. /// 在背包中找ItemDetails
    155. ///
    156. ///
    157. ///
    158. public ItemDetails GetItemDetails(int itemCode)
    159. {
    160. ItemDetails itemDetails;
    161. if(itemDetailsDictionary.TryGetValue(itemCode,out itemDetails))
    162. {
    163. return itemDetails;
    164. }
    165. else
    166. {
    167. return null;
    168. }
    169. }
    170. ///
    171. /// 吃到物品用的调试
    172. ///
    173. ///
    174. private void DebugPrintInventoryList(List inventoryLists)
    175. {
    176. foreach (var inventoyItem in inventoryLists)
    177. {
    178. Debug.Log("Item Description:" + InventoryManager.Instance.GetItemDetails(inventoyItem.itemCode).itemDescription +
    179. " Item Quantity:" + inventoyItem.itemQuantity);
    180. }
    181. }
    182. }

    回到PickUp脚本,我们就可以直接添加了

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ItemPickUp : MonoBehaviour
    5. {
    6. private void OnTriggerEnter2D(Collider2D collision)
    7. {
    8. if(collision.TryGetComponent(out Item item))
    9. {
    10. ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(item.ItemCode);
    11. if (itemDetails.canBePickUp)
    12. {
    13. InventoryManager.Instance.AddItem(InventoryLocation.player, item, collision.gameObject);
    14. }
    15. }
    16. }
    17. }

     


    UI:

      搞完后就可以制作UI了,直接我们采用熟悉的设计UI模式

     

     

     

    一定要注意这个Slot的两个子对象的Raycast Target要取消勾选,不然会导致后面拖拽的时候出现问题。 

     

     

     

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. public class UIInventoryBar : MonoBehaviour
    6. {
    7. [SerializeField] private PlayerController2D playerController2D;
    8. [SerializeField] private Sprite blank16Sprite = null;
    9. [SerializeField] private UIInventorySlot[] inventorySlots = null;
    10. public GameObject inventoryBarDragItem;
    11. private RectTransform rectTransform;
    12. private bool isInventoryBarPositionBottom = true; //判断背包条是否在视角下方
    13. public bool IsInventoryBarPositionBottom
    14. {
    15. get
    16. {
    17. return isInventoryBarPositionBottom;
    18. }
    19. set
    20. {
    21. isInventoryBarPositionBottom = value;
    22. }
    23. }
    24. private void Awake()
    25. {
    26. rectTransform = GetComponent();
    27. }
    28. private void Update()
    29. {
    30. SwitchInventoryBarPosition();
    31. }
    32. private void OnEnable()
    33. {
    34. EventHandler.InventoryUpdateEvent += InventoryUpdate;
    35. }
    36. private void OnDisable()
    37. {
    38. EventHandler.InventoryUpdateEvent -= InventoryUpdate;
    39. }
    40. private void InventoryUpdate(InventoryLocation inventoryLocation, List inventoryList)
    41. {
    42. if(inventoryLocation == InventoryLocation.player)
    43. {
    44. CLearInventorySlots(); //先清空InventroyBar里面所有的InvnetorSlot
    45. if(inventoryList.Count >0 && inventorySlots.Length > 0) //再遍历一遍inventoryList和inventorySlots重新添加回bar里
    46. {
    47. for (int i = 0; i < inventorySlots.Length; i++)
    48. {
    49. if (i < inventoryList.Count)
    50. {
    51. int itemCode = inventoryList[i].itemCode;
    52. ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);
    53. if (itemDetails != null)
    54. {
    55. inventorySlots[i].inventorySlotImage.sprite = itemDetails.itemSprite;
    56. inventorySlots[i].textMeshProUGUI.text = inventoryList[i].itemQuantity.ToString();
    57. inventorySlots[i].itemDetails = itemDetails;
    58. inventorySlots[i].itemQuantity = inventoryList[i].itemQuantity;
    59. }
    60. }
    61. else
    62. {
    63. break;
    64. }
    65. }
    66. }
    67. }
    68. }
    69. private void CLearInventorySlots()
    70. {
    71. if(inventorySlots.Length > 0)
    72. {
    73. for (int i = 0; i < inventorySlots.Length; i++)
    74. {
    75. inventorySlots[i].inventorySlotImage.sprite = blank16Sprite;
    76. inventorySlots[i].textMeshProUGUI.text = "";
    77. inventorySlots[i].itemDetails = null;
    78. inventorySlots[i].itemQuantity = 0;
    79. }
    80. }
    81. }
    82. ///
    83. /// 切换InventoryBar
    84. ///
    85. private void SwitchInventoryBarPosition()
    86. {
    87. Vector3 playerViewportPosition = playerController2D.GetPlayerVierportPosition();
    88. if (playerViewportPosition.y > 0.3f && !IsInventoryBarPositionBottom)
    89. {
    90. rectTransform.pivot = new Vector2(0.5f, 0f);
    91. rectTransform.anchorMin = new Vector2(0.5f, 0f);
    92. rectTransform.anchorMax = new Vector2(0.5f, 0f);
    93. rectTransform.anchoredPosition = new Vector2(0f, 2.5f);
    94. isInventoryBarPositionBottom = true;
    95. }
    96. else if(playerViewportPosition.y <= 0.3f && IsInventoryBarPositionBottom)
    97. {
    98. rectTransform.pivot = new Vector2(0.5f, 1f);
    99. rectTransform.anchorMin = new Vector2(0.5f, 1f);
    100. rectTransform.anchorMax = new Vector2(0.5f, 1f);
    101. rectTransform.anchoredPosition = new Vector2(0f, -2.5f);
    102. isInventoryBarPositionBottom = false;
    103. }
    104. }
    105. }

    InventorySlot的脚本如下

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5. using TMPro;
    6. using UnityEngine.UI;
    7. using System;
    8. public class UIInventorySlot : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler
    9. {
    10. private Camera mainCamera;
    11. private GameObject draggedItem;
    12. private Transform itemParent;
    13. public Image inventorySlotHighlight;
    14. public Image inventorySlotImage;
    15. public TextMeshProUGUI textMeshProUGUI;
    16. [SerializeField] private UIInventoryBar inventoryBar = null;
    17. [HideInInspector] public ItemDetails itemDetails;
    18. [SerializeField] private GameObject itemPrefab = null;
    19. [HideInInspector] public int itemQuantity;
    20. [SerializeField] private int slotNumber = 0;
    21. private void Start()
    22. {
    23. mainCamera = Camera.main;
    24. itemParent = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;
    25. }
    26. public void OnBeginDrag(PointerEventData eventData)
    27. {
    28. if(itemDetails != null)
    29. {
    30. //PlayerController.Instance.DisablePlayerInputAndResetMovement();
    31. draggedItem = Instantiate(inventoryBar.inventoryBarDragItem, inventoryBar.transform);
    32. Image draggedItemImage = draggedItem.GetComponentInChildren();
    33. draggedItemImage.sprite = inventorySlotImage.sprite;
    34. Debug.Log("draggedItemImage.sprite" + draggedItemImage.sprite);
    35. }
    36. }
    37. public void OnDrag(PointerEventData eventData)
    38. {
    39. if(draggedItem != null)
    40. {
    41. draggedItem.transform.position = Input.mousePosition;
    42. }
    43. }
    44. public void OnEndDrag(PointerEventData eventData)
    45. {
    46. if(draggedItem != null)
    47. {
    48. Destroy(draggedItem);
    49. if(eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent() != null)
    50. {
    51. int toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent().slotNumber;
    52. InventoryManager.Instance.SwapInventoryItems(InventoryLocation.player,slotNumber,toSlotNumber);
    53. }
    54. else
    55. {
    56. if (itemDetails.canBeDropped)
    57. {
    58. DropSelectedItemMousePosition();
    59. }
    60. }
    61. //PlayerController.Instance.EnablePlayerInput();
    62. }
    63. }
    64. private void DropSelectedItemMousePosition()
    65. {
    66. if(itemDetails != null)
    67. {
    68. Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));
    69. GameObject itemGameobject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, itemParent);
    70. Item item = itemGameobject.GetComponent();
    71. item.ItemCode = itemDetails.itemCode;
    72. InventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);
    73. }
    74. }
    75. }

    依次添加 

    对应的SlotNumer 

     

    .....

     

     

    我们需要创建一个名字叫UIInventoryDraggedItem的预制体,用来处理拖拽出来的图像显示

     

     

     完成以后我们可以把物品放到场景中试玩一下,可以

    我们来做最后的交换位置,在我们之前写的InventoryManager加上这个函数即可

    1. ///
    2. /// 交换物品顺序
    3. ///
    4. ///
    5. ///
    6. ///
    7. public void SwapInventoryItems(InventoryLocation inventoryLocation, int fromSlotNumber, int toSlotNumber)
    8. {
    9. if(fromSlotNumber < inventoryLists[(int)inventoryLocation].Count && toSlotNumber < inventoryLists[(int)inventoryLocation].Count &&
    10. fromSlotNumber != toSlotNumber && toSlotNumber >= 0)
    11. {
    12. InventoryItem fromSlotItem = inventoryLists[(int)inventoryLocation][fromSlotNumber];
    13. InventoryItem toSlotItem = inventoryLists[(int)inventoryLocation][toSlotNumber];
    14. inventoryLists[(int)inventoryLocation][toSlotNumber] = fromSlotItem;
    15. inventoryLists[(int)inventoryLocation][fromSlotNumber] = toSlotItem;
    16. EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
    17. }
    18. }

    回到OnEndDrag的函数

    1. public void OnEndDrag(PointerEventData eventData)
    2. {
    3. if(draggedItem != null)
    4. {
    5. Destroy(draggedItem);
    6. if(eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent() != null)
    7. {
    8. int toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent().slotNumber;
    9. InventoryManager.Instance.SwapInventoryItems(InventoryLocation.player,slotNumber,toSlotNumber);
    10. }
    11. else
    12. {
    13. if (itemDetails.canBeDropped)
    14. {
    15. DropSelectedItemMousePosition();
    16. }
    17. }
    18. //PlayerController.Instance.EnablePlayerInput();
    19. }
    20. }

     

    学习产出:

    1

    由于时间有限(要去恰午饭了)展示就不展示了,总之我们先做到通过这些来实现玩家有关物体UI的效果,下次更新可能是比赛完以后,我先走啦,拜拜。

  • 相关阅读:
    软件需求说明书(GB856T-88)
    【零基础入门Mybatis系列】第十篇——查询专题
    内网穿透的应用-通过内网穿透技术实现PLSQL远程访问Oracle数据库
    MySQL索引原理之索引与约束
    作为产品经理,在工作中常逛哪些网站?
    【Codeforces】 CF79D Password
    [含lw+源码等]微信小程序家政预约系统+后台管理系统[包运行成功]
    腾讯云2023年双11云服务器优惠价格表
    71、Spring Data JPA 的 样本查询--参数作为样本去查询数据库的数据,也可以定义查询的匹配规则
    Flask 实战笔记02 - SQLAlchemy实现mysql的应用
  • 原文地址:https://blog.csdn.net/dangoxiba/article/details/126802595