0


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

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

学习目标:

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


学习内容:

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

public enum ItemType
{
    Seed,
    Commodity,
    Watering_tool,
    Hoeing_tool,
    Chopping_tool,
    Breaking_tool,
    Reping_tool,
    Collecting_tool,
    Reapable_scenery,
    Furinture,
    None,
    Count,

}

只有枚举没有类名,

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

using UnityEngine;

[System.Serializable]
public class ItemDetails
{
    public int itemCode;
    public ItemType itemType;
    public string itemDescription;
    public Sprite itemSprite;
    public string itemLongDescription;
    public short itemUseGridRadius;
    public float itemUseRadius;
    public bool isStartingItem;
    public bool canBePickUp;
    public bool canBeDropped;
    public bool canBeEaten;
    public bool canBeCarried;
}

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

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Item : MonoBehaviour
{
    [ItemCodeDescription][SerializeField] private int _itemCode;
    private SpriteRenderer spriteRenderer;

    public int ItemCode
    {
        get
        {
            return _itemCode;
        }
        set
        {
            _itemCode = value;
        }
    }

    private void Awake()
    {
        spriteRenderer = GetComponentInChildren<SpriteRenderer>();
    }

    private void Start()
    {
        if(ItemCode != 0)
        {
            Init(ItemCode);
        }
    }

    public void Init(int itemCodeParams)
    {
        
    }
}

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

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName  = "so_Itemlist", menuName = "Scriptable Objects/Item list")]
public class SO_ItemList : ScriptableObject
{
    public List<ItemDetails> itemDetails;
}

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

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

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

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

using UnityEngine;

public class ItemCodeDescriptionAttribute : PropertyAttribute
{
    
}

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


using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(ItemCodeDescriptionAttribute))] //告诉系统自定义特性名叫ItemCodeDescriptionAttribute
public class ItemCodeDescriptionDrawer : PropertyDrawer
{
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUI.GetPropertyHeight(property) * 2;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        if(property.propertyType == SerializedPropertyType.Integer)
        {
            EditorGUI.BeginChangeCheck();

            var newValue = EditorGUI.IntField(new Rect(position.x, position.y, position.width, position.height / 2),label,property.intValue);

            EditorGUI.LabelField(new Rect(position.x, position.y + position.height /2 , position.width, position.height / 2), "Item Description",
                GetItemDescription(property.intValue));

            //如果Item的ItemCode的值发生改变,则面板上的值也要发生变化

            if (EditorGUI.EndChangeCheck())
            {
                property.intValue = newValue;
            }
        }

        EditorGUI.EndProperty();
    }

    private string GetItemDescription(int itemCode)
    {
        SO_ItemList so_itemList;

        so_itemList = AssetDatabase.LoadAssetAtPath("Assets/Scriptable Object Assets/Items/so_Itemlist.asset", typeof(SO_ItemList)) as SO_ItemList;

        List<ItemDetails> itemDetailLists = so_itemList.itemDetails;

        ItemDetails itemDetail = itemDetailLists.Find(x => x.itemCode == itemCode);

        if(itemDetail!= null)
        {
            return itemDetail.itemDescription;
        }
        else
        {
            return "";
        }
    }
}

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

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

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

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

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

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

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

写个新脚本用协程来实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemNudge : MonoBehaviour
{
    [SerializeField] private float pauseTime = 0.04f;
    private WaitForSeconds pause;
    private bool isAnimating = false;

    private void Awake()
    {
        pause = new WaitForSeconds(pauseTime);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (!isAnimating)
        {
            if(gameObject.transform.position.x < collision.transform.position.x) //如果在左边
            {
                StartCoroutine(nameof(RotateAndClock));
            }
            else
            {
                StartCoroutine(nameof(RotateClock));
            }
        }
    }

    private IEnumerator RotateAndClock()
    {
        isAnimating = true;

        for (int i = 0; i < 4; i++)
        {
            gameObject.transform.GetChild(0).Rotate(0f, 0f, 2f);
            yield return pause;
        }

        for (int i = 0; i < 5; i++)
        {
            gameObject.transform.GetChild(0).Rotate(0f, 0f, -2f);
            yield return pause;
        }

        gameObject.transform.GetChild(0).Rotate(0f, 0f, 2f);

        yield return pause;

        isAnimating = false;
    }

    private IEnumerator RotateClock()
    {
        isAnimating = true;

        for (int i = 0; i < 4; i++)
        {
            gameObject.transform.GetChild(0).Rotate(0f, 0f, -2f);
            yield return pause;
        }

        for (int i = 0; i < 5; i++)
        {
            gameObject.transform.GetChild(0).Rotate(0f, 0f, 2f);
            yield return pause;
        }

        gameObject.transform.GetChild(0).Rotate(0f, 0f, -2f);

        yield return pause;

        isAnimating = false;
    }
}

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

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Item : MonoBehaviour
{
    [ItemCodeDescription][SerializeField] private int _itemCode;
    private SpriteRenderer spriteRenderer;

    public int ItemCode
    {
        get
        {
            return _itemCode;
        }
        set
        {
            _itemCode = value;
        }
    }

    private void Awake()
    {
        spriteRenderer = GetComponentInChildren<SpriteRenderer>();
    }

    private void Start()
    {
        if(ItemCode != 0)
        {
            Init(ItemCode);
        }
    }

    public void Init(int itemCodeParams)
    {
        if(itemCodeParams != 0)
        {
            ItemCode = itemCodeParams;

            ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(ItemCode);

            spriteRenderer.sprite = itemDetails.itemSprite;

            if(itemDetails.itemType == ItemType.Reapable_scenery) //如果该物品是风景类型的话
            {
                gameObject.AddComponent<ItemNudge>();
            }
        }
    }
}

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

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemPickUp : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.TryGetComponent<Item>(out Item item))
        {
            ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(item.ItemCode);

            if (itemDetails.canBePickUp)
            {
               
            }
        }
    }
}

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

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


public enum InventoryLocation
{
    player,
    chest,
    count,
}

public enum ItemType
{
    Seed,
    Commodity,
    Watering_tool,
    Hoeing_tool,
    Chopping_tool,
    Breaking_tool,
    Reping_tool,
    Collecting_tool,
    Reapable_scenery,
    Furinture,
    None,
    Count,

}

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

[System.Serializable]
public struct InventoryItem 
{
    public int itemCode;
    public int itemQuantity;
}

还需要一个单例模式

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : Component
{
    private static T instance;

    public static T Instance
    {
        get
        {
            return instance;
        }
    }

    protected virtual void Awake()
    {
        if(instance == null)
        {
            instance = this as T;
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

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

using System;
using System.Collections;
using System.Collections.Generic;

public static class EventHandler
{
    public static event Action<InventoryLocation,List<InventoryItem>> InventoryUpdateEvent;

    public static void CallInventoryUpdateEvent(InventoryLocation inventoryLocation,List<InventoryItem> inventoryLists)
    {
        if(InventoryUpdateEvent != null)
        {
            InventoryUpdateEvent(inventoryLocation, inventoryLists);
        }
    }

    
}

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

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

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : Singleton<InventoryManager>
{
    private Dictionary<int, ItemDetails> itemDetailsDictionary; //通过itemDetails上的itemCode(int类型)的值寻找对应的itemDetails
    public List<InventoryItem>[] inventoryLists; //一个存放所有InventoryItem的链表的数组
    [HideInInspector] public int[] inventoryListInCapcityInArray; //背包容量大小默认是12

    [SerializeField] private SO_ItemList itemLists; //SO_ItemList登记过所有Item

    protected override void Awake()
    {
        base.Awake();

        CreateInventoryLists();

        CreateItemDetailsDictionary();
    }

    private void CreateInventoryLists()
    {
        inventoryLists = new List<InventoryItem>[(int)InventoryLocation.count];//我们有0号背包系统的链表和1号背包系统的链表

        for (int i = 0; i < (int)InventoryLocation.count; i++)
        {
            inventoryLists[i] = new List<InventoryItem>();
        }

        //出事背包容量列表
        inventoryListInCapcityInArray = new int[(int)InventoryLocation.count];

        //初始化玩家背包(0号背包)容量
        inventoryListInCapcityInArray[(int)InventoryLocation.player] = Settings.playerIntialInventoryCapcity;
    }
    public void AddItem(InventoryLocation inventoryLocation, Item item,GameObject gameObjectToDelete) 
    {
        AddItem(inventoryLocation, item);

        Destroy(gameObjectToDelete);
    }
    /// <summary>
    /// 添加物品
    /// </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];

        int itemPositon = FindItemInventory(inventoryLocation, itemCode);

        if(itemPositon != -1)
        {
            AddItemAtPosition(inventoryList, itemCode, itemPositon);//如果找到物品就数量++
        }
        else
        {
            AddItemAtPosition(inventoryList, itemCode); //找不到就在空白处新添加物品
        }

        EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
    }
    /// <summary>
    /// 交换物品顺序
    /// </summary>
    /// <param name="inventoryLocation"></param>
    /// <param name="fromSlotNumber"></param>
    /// <param name="toSlotNumber"></param>
    public void SwapInventoryItems(InventoryLocation inventoryLocation, int fromSlotNumber, int toSlotNumber)
    {
        if(fromSlotNumber < inventoryLists[(int)inventoryLocation].Count && toSlotNumber < inventoryLists[(int)inventoryLocation].Count &&
            fromSlotNumber != toSlotNumber  && toSlotNumber >= 0)
        {
            InventoryItem fromSlotItem = inventoryLists[(int)inventoryLocation][fromSlotNumber];
            InventoryItem toSlotItem = inventoryLists[(int)inventoryLocation][toSlotNumber];

            inventoryLists[(int)inventoryLocation][toSlotNumber] = fromSlotItem;
            inventoryLists[(int)inventoryLocation][fromSlotNumber] = toSlotItem;

            EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);

        }
    }

    /// <summary>
    /// 将物品添加到背包的位置
    /// </summary>
    /// <param name="inventoryList"></param>
    /// <param name="itemCode"></param>

    private void AddItemAtPosition(List<InventoryItem> inventoryList, int itemCode)
    {
        InventoryItem inventoryItem = new InventoryItem();

        inventoryItem.itemCode = itemCode;
        inventoryItem.itemQuantity = 1;

        inventoryList.Add(inventoryItem);
    }

    /// <summary>
    /// 对于背包中已经有的物品就加数量
    /// </summary>
    /// <param name="inventoryList"></param>
    /// <param name="itemCode"></param>
    /// <param name="itemPositon"></param>

    private void AddItemAtPosition(List<InventoryItem> inventoryList, int itemCode, int positon)
    {
        InventoryItem inventoryItem = new InventoryItem();

        int quantity = inventoryList[positon].itemQuantity + 1;
        inventoryItem.itemCode = itemCode;
        inventoryItem.itemQuantity = quantity;
        inventoryList[positon] = inventoryItem;

        Debug.ClearDeveloperConsole();
    }

    internal void RemoveItem(InventoryLocation inventoryLocation, int itemCode)
    {
        List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];
        int itemPosition = FindItemInventory(inventoryLocation, itemCode);

        if (itemPosition != -1)
        {
            RemoveItemAtPosition(inventoryList,itemCode,itemPosition);
        }
        EventHandler.CallInventoryUpdateEvent(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>
    /// 判断物品是否在背包里面,找不到就return -1,找到的话就返回发现物品的位置
    /// </summary>
    /// <param name="inventoryLocation"></param>
    /// <param name="itemCode"></param>
    /// <returns></returns>
    private int FindItemInventory(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>
    /// 初始化创建的字典
    /// </summary>

    private void CreateItemDetailsDictionary()
    {
        itemDetailsDictionary = new Dictionary<int, ItemDetails>();

        foreach (var itemDetails in itemLists.itemDetails)
        {
            itemDetailsDictionary.Add(itemDetails.itemCode,itemDetails);
        }
    }

    /// <summary>
    /// 在背包中找ItemDetails
    /// </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;
        }
    }

    /// <summary>
    /// 吃到物品用的调试
    /// </summary>
    /// <param name="inventoryLists"></param>
    private void DebugPrintInventoryList(List<InventoryItem> inventoryLists)
    {
        foreach (var inventoyItem in inventoryLists)
        {
            Debug.Log("Item Description:" + InventoryManager.Instance.GetItemDetails(inventoyItem.itemCode).itemDescription +
                "       Item Quantity:" + inventoyItem.itemQuantity);
        }
    }
}

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemPickUp : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.TryGetComponent<Item>(out Item item))
        {
            ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(item.ItemCode);

            if (itemDetails.canBePickUp)
            {
                InventoryManager.Instance.AddItem(InventoryLocation.player, item, collision.gameObject);
            }
        }
    }
}

UI:

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

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

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIInventoryBar : MonoBehaviour
{
    [SerializeField] private PlayerController2D playerController2D;
    [SerializeField] private Sprite blank16Sprite = null;
    [SerializeField] private UIInventorySlot[] inventorySlots = null;
    public GameObject inventoryBarDragItem;

    private RectTransform rectTransform;

    private bool isInventoryBarPositionBottom = true;  //判断背包条是否在视角下方

    public bool IsInventoryBarPositionBottom
    {
        get
        {
            return isInventoryBarPositionBottom;
        }
        set
        {
            isInventoryBarPositionBottom = value;
        }
    }

    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void Update()
    {
        SwitchInventoryBarPosition();
    }

    private void OnEnable()
    {
        EventHandler.InventoryUpdateEvent += InventoryUpdate;
    }

    private void OnDisable()
    {
        EventHandler.InventoryUpdateEvent -= InventoryUpdate;
    }

    private void InventoryUpdate(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList)
    {
        if(inventoryLocation == InventoryLocation.player)
        {
            CLearInventorySlots(); //先清空InventroyBar里面所有的InvnetorSlot
            if(inventoryList.Count >0 && inventorySlots.Length > 0) //再遍历一遍inventoryList和inventorySlots重新添加回bar里
            {
                for (int i = 0; i < inventorySlots.Length; i++)
                {
                    if (i < inventoryList.Count)
                    {
                        int itemCode = inventoryList[i].itemCode;

                        ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);

                        if (itemDetails != null)
                        {
                            inventorySlots[i].inventorySlotImage.sprite = itemDetails.itemSprite;
                            inventorySlots[i].textMeshProUGUI.text = inventoryList[i].itemQuantity.ToString();
                            inventorySlots[i].itemDetails = itemDetails;
                            inventorySlots[i].itemQuantity = inventoryList[i].itemQuantity;                            
                        }
                    }
                    else
                    {
                        break;
                    }

                }
            }
        }
    }

    private void CLearInventorySlots()
    {
        if(inventorySlots.Length > 0)
        {
            for (int i = 0; i < inventorySlots.Length; i++)
            {
                inventorySlots[i].inventorySlotImage.sprite = blank16Sprite;
                inventorySlots[i].textMeshProUGUI.text = "";
                inventorySlots[i].itemDetails = null;
                inventorySlots[i].itemQuantity = 0;
            }
        }
    }

    /// <summary>
    /// 切换InventoryBar
    /// </summary>
    private void SwitchInventoryBarPosition()
    {
        Vector3 playerViewportPosition = playerController2D.GetPlayerVierportPosition();

        if (playerViewportPosition.y > 0.3f && !IsInventoryBarPositionBottom)
        {
            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)
        {

            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;            
        }
    }
}

InventorySlot的脚本如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using TMPro;
using UnityEngine.UI;
using System;

public class UIInventorySlot : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler
{
    private Camera mainCamera;
    private GameObject draggedItem;
    private Transform itemParent;

    public Image inventorySlotHighlight;
    public Image inventorySlotImage;
    public TextMeshProUGUI textMeshProUGUI;

    [SerializeField] private UIInventoryBar inventoryBar = null;
    [HideInInspector] public ItemDetails itemDetails;
    [SerializeField] private GameObject itemPrefab = null;
    [HideInInspector] public int itemQuantity;
    [SerializeField] private int slotNumber = 0;

    private void Start()
    {
        mainCamera = Camera.main;
        itemParent = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if(itemDetails != null)
        {
            //PlayerController.Instance.DisablePlayerInputAndResetMovement();

            draggedItem = Instantiate(inventoryBar.inventoryBarDragItem, inventoryBar.transform);

            Image draggedItemImage = draggedItem.GetComponentInChildren<Image>();
            draggedItemImage.sprite = inventorySlotImage.sprite;
            Debug.Log("draggedItemImage.sprite" + draggedItemImage.sprite);
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        if(draggedItem != null)
        {
            draggedItem.transform.position = Input.mousePosition;
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        if(draggedItem != null)
        {
            Destroy(draggedItem);

            if(eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null)
            {
                int toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;

                InventoryManager.Instance.SwapInventoryItems(InventoryLocation.player,slotNumber,toSlotNumber);
            }
            else
            {
                if (itemDetails.canBeDropped)
                {
                    DropSelectedItemMousePosition();
                }
            }

            //PlayerController.Instance.EnablePlayerInput();
        }
    }

    private void DropSelectedItemMousePosition()
    {
        if(itemDetails != null)
        {
            Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));

            GameObject itemGameobject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, itemParent);
            Item item = itemGameobject.GetComponent<Item>();
            item.ItemCode = itemDetails.itemCode;

            InventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);
        }
    }
}

依次添加

对应的SlotNumer

.....

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

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

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

    /// <summary>
    /// 交换物品顺序
    /// </summary>
    /// <param name="inventoryLocation"></param>
    /// <param name="fromSlotNumber"></param>
    /// <param name="toSlotNumber"></param>
    public void SwapInventoryItems(InventoryLocation inventoryLocation, int fromSlotNumber, int toSlotNumber)
    {
        if(fromSlotNumber < inventoryLists[(int)inventoryLocation].Count && toSlotNumber < inventoryLists[(int)inventoryLocation].Count &&
            fromSlotNumber != toSlotNumber  && toSlotNumber >= 0)
        {
            InventoryItem fromSlotItem = inventoryLists[(int)inventoryLocation][fromSlotNumber];
            InventoryItem toSlotItem = inventoryLists[(int)inventoryLocation][toSlotNumber];

            inventoryLists[(int)inventoryLocation][toSlotNumber] = fromSlotItem;
            inventoryLists[(int)inventoryLocation][fromSlotNumber] = toSlotItem;

            EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);

        }
    }

回到OnEndDrag的函数

public void OnEndDrag(PointerEventData eventData)
    {
        if(draggedItem != null)
        {
            Destroy(draggedItem);

            if(eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null)
            {
                int toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;

                InventoryManager.Instance.SwapInventoryItems(InventoryLocation.player,slotNumber,toSlotNumber);
            }
            else
            {
                if (itemDetails.canBeDropped)
                {
                    DropSelectedItemMousePosition();
                }
            }

            //PlayerController.Instance.EnablePlayerInput();
        }
    }

学习产出:

1

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

标签: unity 游戏引擎 c#

本文转载自: https://blog.csdn.net/dangoxiba/article/details/126802595
版权归原作者 dangoxiba 所有, 如有侵权,请联系我们删除。

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

还没有评论