UI搭建:
————————————————————————————————
代码:

装备共有属性类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum ArticleType//装备种类
{
Weapon,
Drag,
Collect,
Pick
}

public class Article //装备属性类(装备共有参数)
{
public string name;//装备名称
public string spritPath;//装备UI图路径
public ArticleType articleType;//装备类型
public int count;//装备数量

//构造函数
public Article(string name,string spritPath,ArticleType articleType,int count)
{
    this.name = name;
    this.spritPath = spritPath;
    this.articleType = articleType;
    this.count = count;
}

}
——————————————————————————————
装备“物品”类
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ArticleItem : MonoBehaviour
{
private Image articleSprit;
private Text count;
public Article article;
private UIDrag UIDrag;//实现拖拽的类
private Canvas canvas;//物体上的Canvas组件,用于物体显示在上层
//private CanvasGroup canvasGroup;//拖拽此物体是取消其射线检测,代替事件渗透
private int defortSort;
//private Transform Origentparent;

private Vector3 currentLocalPosition;
private float moveTimer;
public float moveTime = 0.2f;

private bool isMoving = false;

private Action onMoveEnd;

// Start is called before the first frame update
void Awake()
{
    articleSprit = transform.GetComponent<Image>();
    count = transform.Find("ItemCount").GetComponent<Text>();
    //将对应拖拽状态时要掉用的方法注册到拖拽类的委托链
    UIDrag = transform.GetComponent<UIDrag>();
    UIDrag.onStartDrag += OnStartDrag;
    UIDrag.onDrag += OnDrag;
    UIDrag.onEndDrag += OnEndDrag;
    
    canvas = transform.GetComponent<Canvas>();
    defortSort = canvas.sortingOrder;
    //canvasGroup = transform.GetComponent<CanvasGroup>();
    
    //currentLocalPosition = transform.localPosition;
}
private void Start()
{
    transform.localPosition = Vector3.zero;
    currentLocalPosition = transform.localPosition;
}
private void Update()
{
    MoveBack();
}
//Lerpg固定时间内完成执行
private void MoveBack()
{
    if (isMoving)
    {
        moveTimer += Time.deltaTime * (1 / moveTime);//0.2秒后为1
        transform.localPosition = Vector3.Lerp(currentLocalPosition, Vector3.zero, moveTimer);
        if (moveTimer >= 1)
        {
            isMoving = false;
            //MoveToOrigin方法导致的事件完成后执行自身委托
            onMoveEnd?.Invoke();
        }
    }
}
//执行Lerp
public void MoveToOrigin(Action onMoveEnd)
{
    isMoving = true;
    moveTimer = 0;
    currentLocalPosition = transform.localPosition;
    this.onMoveEnd = onMoveEnd;//将之后要执行的方法方到自身委托链上
}
//设置装备属性(UI图标,数量等)
public void SetArticle(Article article)
{
    this.article = article;

    //显示数据
    articleSprit.sprite = Resources.Load<Sprite>(this.article.spritPath);
    count.text = this.article.count.ToString();
}
//开始拖拽时执行
public void OnStartDrag()
{
    canvas.sortingOrder = defortSort + 1;//显示在同级上层
    //transform.SetParent(transform.parent.parent);
    BagPanel.instence.curretnAricle = this;//正在拖拽的为此物体
}
public void OnDrag()
{

}
//拖拽结束时执行
public void OnEndDrag()
{
    //结束拖拽时所选格子不为空时,物品进入格子,
    if (BagPanel.instence.currtenHoverGrid != null && !BagPanel.instence.currtenHoverGrid.gameObject.Equals(transform.parent.gameObject))
    {
        BagPanel.instence.currtenHoverGrid.DragIntoGride(BagPanel.instence.curretnAricle);//格子的方法
        canvas.sortingOrder = defortSort;//恢复层级
    }
    else//为空则返回
    {
        //transform.SetParent(Origentparent);
        //使用委托实现结束后执行
        MoveToOrigin(() => { canvas.sortingOrder = defortSort; });//表达式内为方法结束后要执行的语句
    }
    BagPanel.instence.curretnAricle = null;//结束拖拽时正在拖拽的物体为空
}    

}
——————————————————————————————
实现拖拽的类:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour,IBeginDragHandler,ICanvasRaycastFilter ,IDragHandler,IEndDragHandler
{
private Vector3 mousePosition;
private RectTransform rect;

public Action onDrag;
public Action onStartDrag;
public Action onEndDrag;

private bool isDraging = false;

private void Awake()
{
    rect = GetComponent<RectTransform>();
    if (rect ==null)
    {
        throw new System.Exception("只能拖拽UI物体");
    }
}
//使用事件渗透监听不到拖拽事件时可用下面代码
/*private void Update()
{
    if (isDraging)
    {
        *//*rect.anchoredPosition += (Vector2)(Input.mousePosition - mousePosition);
        mousePosition = Input.mousePosition;
        //transform.position = eventData.position; //中心随光标移动
        //锚点的位置加上鼠标移动
        //rect.anchoredPosition += eventData.delta;
        //if (onDrag != null) onDrag();
        onDrag?.Invoke();*//*
    }
    if(Input .GetMouseButtonUp(0)&&isDraging==true)
    {
        onEndDrag?.Invoke();
        isDraging = false;
    }
}*/

public void OnBeginDrag(PointerEventData eventData)
{
    mousePosition = Input.mousePosition;
    onStartDrag?.Invoke();
    isDraging = true;
}
//事件渗透接口:渗透过当前事件向下进行事件检测(背包系统渗透装备UI图标向下检测所在物品栏)
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)//事件渗透接口:返回值TRUE时:检测,返回值为FALES:渗透事件。
{
    return !isDraging;//拖拽时事件渗透当前拖拽的物体渗透当前事件继续检测
}
//事件渗透监听不到此接口事件(实际监听的到),但要保留接口实现拖拽
public void OnDrag(PointerEventData eventData)//参数为相机与点击物体的射线检测点
{
    rect.anchoredPosition += (Vector2)(Input.mousePosition - mousePosition);
    mousePosition = Input.mousePosition;
    //transform.position = eventData.position; //中心随光标移动
    //锚点的位置加上鼠标移动
    //rect.anchoredPosition += eventData.delta;
    //if (onDrag != null) onDrag();
    onDrag?.Invoke();
}
//事件渗透监听不到此接口事件(实际监听的到)
public void OnEndDrag(PointerEventData eventData)
{
    onEndDrag?.Invoke();
    isDraging = false;
}

}
——————————————————————————————
格子类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class BagGrde : MonoBehaviour , IPointerEnterHandler,IPointerExitHandler
{
private ArticleItem articleItem;

private Image bgImage;
private Color defortColor;

public ArticleItem ArticleItem
{
    get { return articleItem; }
}

private void Awake()
{
    bgImage = transform.GetComponent<Image>();
    defortColor = bgImage.color;
}
//设置格子内的装备物品方法
public void SetArticleItem(ArticleItem articleItem)
{
    this.articleItem = articleItem;//此格子装着的物品
    this.articleItem.gameObject.SetActive(true);
    //将此物品栏设置父物体
    this.articleItem.transform.SetParent(transform);
    //刷新背包时直接设置位置,取消物品UI缓动效果
    articleItem.transform.localPosition = Vector3.zero;

    //防止发生变化
    //设置位置
    //this.articleItem.transform.localPosition = Vector3.zero;
    this.articleItem.MoveToOrigin(() =>//用物品类里面的方法实现拖拽到格子时的缓动效果
   {//设置5px的边界值
       this.articleItem.GetComponent<RectTransform>().offsetMin = new Vector2(5, 5);
       this.articleItem.GetComponent<RectTransform>().offsetMax = new Vector2(-5, -5);
   });
    //设置大小
    this.articleItem.transform.localScale = Vector3.one;
    //设置
    //this.articleItem.GetComponent<RectTransform>().offsetMin = new Vector2(5, 5);
    //this.articleItem.GetComponent<RectTransform>().offsetMax = new Vector2(-5, -5);
}
//清空格子
public void ClearGride()
{
    articleItem.gameObject.SetActive(false);
    articleItem.transform.SetParent(null);
    articleItem = null;
}

//物品填入格子
public void DragIntoGride(ArticleItem articleItem)
{
    //物品原来所在的格子
    BagGrde lastGride = articleItem.transform.parent.GetComponent<BagGrde>();
    if (this.articleItem==null)//如果这个格子中没有物品
    {
        //清空之前的格子
        lastGride.ClearGride();

        //设置拖拽到的此格子
        SetArticleItem(articleItem);
        //lastGride.ClearGride();
    }
    else//此格子不为空时将物品进行交换
    {
        //当前格子内物品设置到之前的格子
        lastGride.SetArticleItem(this.articleItem);
        //设置拖拽过来的新物品到的此格子
        SetArticleItem(articleItem);
    }
}
//鼠标进入格子时接口调用的函数
public void OnPointerEnter(PointerEventData eventData)
{
    if(BagPanel.instence .curretnAricle!=null)//拖拽的物品不为空时
    {
        BagPanel.instence.currtenHoverGrid = this;//光标进入此格子则选中此格子
        bgImage.color = Color.white;//该变选中格子的颜色
    }
    //此格子布为空时显示此格子物品的信息界面
    if (this.articleItem != null)
    {
        UIManager.Instence.articleInformation.gameObject.SetActive(true);
        UIManager.Instence.articleInformation.panel.transform.position = transform.position;
        UIManager.Instence.articleInformation.info.text = this.articleItem.article.name + "\n" + this.articleItem.article.count.ToString ();
    }
}
//光标退出时
public void OnPointerExit(PointerEventData eventData)
{
    BagPanel.instence.currtenHoverGrid = null;
    bgImage.color = defortColor;
    //隐藏信息界面
    UIManager.Instence.articleInformation.gameObject.SetActive(false);
    UIManager.Instence.articleInformation.panel.transform.position = Vector3.zero;
    UIManager.Instence.articleInformation.info.text = " ";
}

}
———————————————————————————————————
背包模块类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BagPanel : MonoBehaviour
{

private List<Article> articlesList = new List<Article>();//背包中所有物品的属性

private List<GameObject> articleObjectPool = new List<GameObject>();//储存物品预制件的对象池

public GameObject articleItemPrafab;//物品UI预制件
private BagGrde[] bagGrdes;//物品栏

public BagGrde currtenHoverGrid;//当前鼠标所悬浮所在格子
public ArticleItem curretnAricle;//当前拖拽物品
//单例
public static BagPanel instence;//单例,在外部访问公开属性

//private Button btn_Back;
// Start is called before the first frame update
void Awake()
{
    /*if (instence !=null)
    {
        Destroy(gameObject);
        return;
    }*/
    instence = this;
    //DontDestroyOnLoad(gameObject);

    //btn_Back = transform.Find("btn_Back").GetComponent<Button>();
    //btn_Back.onClick.AddListener(UIManager.Instence.ExtBagPanel);
    InitArticleDate();
    bagGrdes = transform.GetComponentsInChildren<BagGrde>();//获取所有格子
    LoadDate();//初始加载全部数据
}

/* private void Start()
{
LoadDate();
}*/
//加载全部数据
public void LoadDate()
{
ClearGrideAndHideItems();
for (int i = 0; i < articlesList.Count; i++)
{
//将列表中所有物品的属性生成对应物品装入空着的格子
GetBagGrid().SetArticleItem(LoadArticleItem(articlesList[i]));
}
}
//按类加载的重载方法
public void LoadDate(ArticleType articleType)
{
ClearGrideAndHideItems();
for (int i = 0; i < articlesList.Count; i++)
{
if (articlesList[i].articleType == articleType)
{
//将列表中某类物品生成对应物品装入空着的格子
GetBagGrid().SetArticleItem(LoadArticleItem(articlesList[i]));
}
}
}
//获取空着的格子
public BagGrde GetBagGrid()
{
for (int i = 0; i < bagGrdes.Length; i++)
{
if (bagGrdes[i].ArticleItem == null)
return bagGrdes[i];
}
return null;
}
//加载ArticleItem物品:根据属性创建对应装备物品
public ArticleItem LoadArticleItem(Article article)
{
GameObject go = GetGameObject();
ArticleItem articleItem = go.GetComponent();
articleItem.SetArticle(article);
return articleItem;
}
//获取GameObject:获取对象池中空闲物品预制件
public GameObject GetGameObject()
{
for (int i = 0; i < articleObjectPool.Count; i++)
{
if (articleObjectPool[i].activeSelf==false)
{
articleObjectPool[i].SetActive(true);
return articleObjectPool[i];
}
}
GameObject go = Instantiate(articleItemPrafab);
articleObjectPool.Add(go);
return go;

}
//重新加载背包时物品栏清空
public void ClearGrideAndHideItems()
{
    for (int i = 0; i < bagGrdes.Length; i++)
    {
        if (bagGrdes[i].ArticleItem!=null)
        {
            bagGrdes[i].ClearGride();
        }
    }
}

//初始化物品数据:加载拥有的制备数据应从服务器获取,此处实验性加载部分装备
public void InitArticleDate()
{
    //武器
    articlesList.Add(new Article("刀刀","at0", ArticleType.Weapon, 2));
    articlesList.Add(new Article("剑剑", "at1", ArticleType.Weapon, 1));
    articlesList.Add(new Article("春药", "at2", ArticleType.Drag, 7));
    articlesList.Add(new Article("夏药", "at3", ArticleType.Drag, 6));
    articlesList.Add(new Article("秋药", "at4", ArticleType.Drag, 5));
    articlesList.Add(new Article("冬药", "at5", ArticleType.Drag, 4));
    articlesList.Add(new Article("典藏种子", "at6", ArticleType.Collect, 1001));
    articlesList.Add(new Article("玻璃碎片", "at7", ArticleType.Pick, 12));
}


#region 点击标签事件(所有标签都在同一个标签组-只有一个标签可选。勾选标签是执行)
public void OnAllToggleValueChange(bool v)//v为标签是否被勾选
{
    if (v) LoadDate();
}
public void OnWepanToggleValueChange(bool v)
{
    if (v) LoadDate(ArticleType.Weapon);
}
public void OnDragToggleValueChange(bool v)
{
    if (v) LoadDate(ArticleType.Drag);
}
public void OnClctorToggleValueChange(bool v)
{
    if (v) LoadDate(ArticleType.Collect);
}
public void OnPickToggleValueChange(bool v)
{
    if (v) LoadDate(ArticleType.Pick);
}
#endregion

}