UGUI实现背包系统

一、项目需求:

1、可以生成物品

2、提示物品相关信息

3、能拖拽背包中的物品进行交换、丢弃操作

 

二、实现步骤:

1,背包中的物品包括各种种类,如武器、消耗品、防具等,它们具备一些相同的属性,如ID、名字、描述、购买价格、出售价格、图标等,同时需要一个字段用于标志这个物品到底是什么类型的(让子类进行修改,最好的类型是枚举,这里我使用的是字符串),于是很容易想到建立一个基类Item:

public class Item 
{
    public int ID { get; private set; }
    public int Num { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public int BuyPrice { get; private set; }
    public int SellPrice { get; private set; }
    public string Icon { get; private set; }
    public string ItemType { get; protected set; }

    public Item(int id, int num, string name, string description,
        int buyPrice, int sellPrice, string icon)
    {
        this.ID = id; this.Num = num; this.Name = name; this.Description = description;
        this.BuyPrice = buyPrice; this.SellPrice = sellPrice; this.Icon = icon;
    }
}

同时衍生出其他所需要的物品类型,武器类需要攻击力的字段、消耗品类需要回复Hp、Mp数值的字段、防具需要力量、防御和敏捷的字段:

武器类:

public class Weapon : Item
{
    public int Damage {get;private set;}
    public Weapon(int id, int num, string name, string description,
        int buyPrice, int sellPrice, string icon, int damage)
        : base(id, num, name, description, buyPrice, sellPrice, icon)
    {
        this.Damage = damage;
        base.ItemType = "Weapon";
    }
}

消耗品类:

public class Consumable : Item
{
    public int BackHp { get; private set; }
    public int BackMp { get; private set; }
    public Consumable(int id, int num, string name, string description,
        int buyPrice, int sellPrice, string icon, int backHp, int backMp)
        : base(id, num, name, description, buyPrice, sellPrice, icon)
    {
        this.BackHp = backHp;
        this.BackMp = backMp;
        base.ItemType = "Consumable";
    }
}

防具类:

public class Armor : Item
{
    public int Power { get; private set; }
    public int Defend { get; private set; }
    public int Agility { get; private set; }
    public Armor(int id, int num, string name, string description,
        int buyPrice, int sellPrice, string icon, int power, int defend, int agility) :
        base(id, num, name, description, buyPrice, sellPrice, icon)
    {
        this.Power = power;
        this.Defend = defend;
        this.Agility = agility;
        base.ItemType = "Armor";
    }
}

 

2、制作背包系统的UI:

创建格子UI:

在Canvas下创建一个GridPanel作为背包的大背景,在GridPanel下创建格子背景GridBg,背景下放格子Grid,将格子作为预制体,给GridPanel添加一个组件GridLayoutGroup,根据需要修改Padding下的CellSize和Spacing属性,给GridPanel添加一个脚本GridPanelUI,用这个脚本来管理所有的格子,因为当需要生成物品时,我们需要知道背包中的空格子,在这个脚本中获取一个空格子。

 

创建物品UI:创建一个空物体Item,创建子物体包括Image(物品图片)和Text(数量),给Item添加脚本ItemUI,用于动态加载格子的内容,因为生成物品是随机的,会产生不同的物品。

 

3、如何得到物品的数据,于是我们需要另一个类KnapsackManager,其中包含一个字典public Dictionary<int, Item> ItemList;用于保存这些数据,编写一个Load方法,通过1中的类实例化一个个对象,再将对象保存到这个字典中即可,除此之外,还需要一个保存物品的方法,即:生成一个物品,将物品放到一个空格子下,注意:考虑到后来交换物品的操作,我们需要建立起格子和物品的对应关系,所以还需要一个类ItemModel处理这些数据(包括添加、查找、删除)

 public void StoreItem(int itemId)
 {
        if (!ItemList.ContainsKey(itemId))
        {
            return;
        }
        Transform emptyGrid = GridPanelUI.GetEmptyGrid();
        if (emptyGrid == null)
        {
            Debug.LogWarning("背包已满!");
            return;
        }
        Item temp = ItemList[itemId];
        CreateNewItem(temp, emptyGrid);
}

private void CreateNewItem(Item item, Transform parent)
{
        GameObject itemPrefab = Resources.Load<GameObject>("Prefabs/Item");
        ItemUI itemUI = itemPrefab.GetComponent<ItemUI>();
        Sprite s = Resources.Load<Sprite>("Image/" + item.Icon);

        itemUI.UpdateItemImg(s);
        itemUI.UpdateItemNum(item.Num);
        GameObject.Instantiate(itemPrefab, parent);

        ItemModel.StoreItem(parent.name, item);//注意不要忘了每创建一个数据就要建立起格子和物品的对应关系
 }

 

而在ItemModel中(具体代码不加以赘述):

public class ItemModel
{
    private static Dictionary<string, Item> m_GridItem = new Dictionary<string, Item>();
    public static void StoreItem(string name, Item item)
    {
    }
    public static Item GetItem(string name)
    {
    }
    public static void DeleteItem(string name)
    {
    } 
}

 

4、如何产生物品,这里我们采用鼠标右键单击的方式模拟:

创建一个类InputDetector:

 void Update()
 {
        if (Input.GetMouseButtonDown(1))
        {
            int index = Random.Range(0, 10);
            KnapsackManager.Instance.StoreItem(index);
        }
 }

 

5、实现当鼠标移动到物品上时,会弹出一个提示框,制作一个提示框(注意ContentSizeFitter的使用),为提示框添加脚本TooltipUI:主要包括更新文本内容、显示、隐藏、跟随鼠标的功能

为格子创建一个GridUI脚本用于鼠标事件监测(鼠标进入、鼠标移出):

实现IPointerEnterHandler, IPointerExitHandler接口,暴露出对应的事件:

public static Action<Transform> OnEnter;
public static Action OnExit;

让KnapsackManager来监听:

当鼠标进入格子的时候就让TooltipUI显示并更新内容,当移出格子的时候就让TooltipUI隐藏起来

至于提示框的跟随则在Update中进行检测即可,但要注意屏幕坐标和世界坐标的转换

 

6、拖拽功能的实现与提示框的实现大体一致,只是实现的接口是

IBeginDragHandler,IDragHandler,IEndDragHandler,另外值得注意的是这两个事件携带的参数:

public static Action<Transform> OnLeftBeginDrag;
public static Action<Transform, Transform> OnLeftEndDrag;

当开始拖拽时传入的是当前鼠标下面的格子,将格子中的数据传递给DragItemUI,同时删除视图中的游戏物体

Destroy(gridTransform.GetChild(0).gameObject);

让DragItem跟随鼠标移动

而当拖拽结束时传入的两个参数,一个是当前拖拽的格子,一个是鼠标下面的游戏物体

 

当结束拖拽时分为以下几种情况:

当鼠标下方的transform为空,意为拖到了背包外面,想要丢弃物品,那么在ItemModel中删除拖拽的物品

当鼠标下方的物体为格子,又分为两种情况,一,格子下面没有物品,那么在ItemModel中删除拖拽的物品,重新建立格子和物品的对应关系,二,格子下面有物品,交换两个格子的物品,听起来似乎有些复杂,但实际上只需要分别得到两个格子,重新建立关系即可,注意要删除视图中的进入格子的物品

其他情况,则是在背包中,但有没有拖拽到格子上,则将物品返回到原处

 if (enterTransform == null)
 {
            ItemModel.DeleteItem(prevTransform.name);
            Debug.LogWarning("物品已扔");
 }
else if (enterTransform.tag == "Grid")
 {
            if (enterTransform.childCount == 0)
            {
                Item item = ItemModel.GetItem(prevTransform.name);
                if (item == null)
                    return;
                ItemModel.DeleteItem(prevTransform.name);
                CreateNewItem(item, enterTransform);              
            }
            else
            {
                Destroy(enterTransform.GetChild(0).gameObject);
                Item prevGridItem = ItemModel.GetItem(prevTransform.name);
                Item enterGridItem = ItemModel.GetItem(enterTransform.name);
                CreateNewItem(prevGridItem, enterTransform);
                CreateNewItem(enterGridItem, prevTransform);
            }
 }
else
{
            Item item = ItemModel.GetItem(prevTransform.name);          
            CreateNewItem(item, prevTransform);
 }

 

三、重难点及遇到的问题:

1、物品之间的继承关系

2、用两个字典保存数据,一个是id与item对应的字典(模板数据),一个是格子与物品对应的字典(实例数据)

3、结束拖拽时的逻辑

遇到的问题:

当结束拖拽时鼠标下方为格子,而格子下没有游戏物体时具体又分为两种情况:

1、此格子为之前格子所在位置

2、此格子不为之前格子所在位置

看下面这段代码,如果是情况2没有问题,但如果是情况1呢?

if (enterTransform.childCount == 0)
{

   Item item = ItemModel.GetItem(prevTransform.name);
   if (item == null)
                    return;

  CreateNewItem(item, enterTransform);      
   ItemModel.DeleteItem(prevTransform.name);       

}

 

四、实现效果:

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值