Unity--背包系统(含锻造和装备系统)

背包系统Package包git地址:https://github.com/PigerYoung/InventorySystem.git

背包系统离不开物品,因此在设计背包系统时需要将物品(Item)的类图设置好,附上下发UML类图

 首先,根据类图可以编写出Item这个父类,因为所有的装备都是继承自Item类的

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

public enum ItemType//物品种类
{
    Consumable,//消耗品
    Equipment,//装备
    Weapon,//武器
    Material//材料
}
public enum Quality//物品品质
{
    Common,
    Uncommon,
    Rare,
    Epic,
    Legendary,
    Artifact
}

public class Item //物品基类
{
    public int ID { get; set; }
    public string Name { get; set; }
    public ItemType ItemType { get; set; }
    public Quality Quality { get; set; }
    public string Description { get; set; }
    public int Capicity { get; set; }
    public int BuyPrice { get; set; } 
    public int SellPrice { get;set; }
    public string Sprite { get; set; }//存放物品的图片路径,通过Resources加载
    public Item(int iD, string name, ItemType itemType, Quality quality, string description, int capicity, int buyPrice, int sellPrice)
    {
        ID = iD;
        Name = name;
        ItemType = itemType;
        Quality = quality;
        Description = description;
        Capicity = capicity;
        BuyPrice = buyPrice;
        SellPrice = sellPrice;
        Sprite = sprite;
    }
public Item() { }//无参构造函数,防止子类在没中没有显式定义构造函数,则会默认调用父类无参数构造
//函数。
}

再依次创建Item的子类,消耗品,装备,武器,材料类 

using UnityEngine;

/// <summary>
/// 消耗品类
/// </summary>
public class Consumable : Item
{
    public int HP { get; set; }//消耗品增加的血量
    public int Mp { get; set; }//消耗品增加的蓝量
    public Consumable(int hP, int mp, int iD, string name, ItemType itemType, Quality quality, string description, int capicity, int buyPrice, int sellPrice,string sprite) :base(iD,name,itemType,quality,description,capicity,buyPrice,sellPrice, sprite)
    {
        HP = hP;
        Mp = mp;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum EquipType//装备类型枚举
{
    Head,//头部
    Neck,//脖子
    Ring,//戒指
    Leg,//腿
    Bracer,//护腕
    Boots,//靴子
    Shoulder,//肩膀
    Belt,//腰带
    OffHand//副手
}
/// <summary>
/// 装备类
/// </summary>
public class Equipment : Item
{
    public int Strength { get; set; }//力量
    public int Intellect { get; set; }//智力
    public int Agility { get; set; }//敏捷
    public int Stamina { get; set; }//体力
    public EquipType equipType { get; set; }//装备类型 
    public Equipment(int strength, int intellect, int agility, int stamina, EquipType equipType, int iD, string name, ItemType itemType, Quality quality, string description, int capicity, int buyPrice, int sellPrice,string sprite): base(iD, name, itemType, quality, description, capicity, buyPrice, sellPrice, sprite)
    {
        Strength = strength;
        Intellect = intellect;
        Agility = agility;
        Stamina = stamina;
        this.equipType = equipType;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Playables;
using UnityEngine;

/// <summary>
/// 武器类
/// </summary>
public enum WeaponType//武器类型
{
    offHand,//副手
    mianHand//主手
}

public class Weapon : Item
{
    public int Damage { get; set; }
    public WeaponType weaponType { get; set;}

    public Weapon(int damage ,WeaponType weaponType,int iD, string name, ItemType itemType, Quality quality, string description, int capicity, int buyPrice, int sellPrice,string sprite) : base(iD, name, itemType, quality, description, capicity, buyPrice, sellPrice, sprite)
    {
       Damage = damage;
       this.weaponType = weaponType;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 材料类
/// </summary>
public class Material : Item
{
public Material(int iD, string name, ItemType itemType, Quality quality, string description, int capicity, int buyPrice, int sellPrice, string sprite) : base(iD, name, itemType, quality, description, capicity, buyPrice, sellPrice, sprite)
    {

    }
    public Material()
    { }
}

编写好物品的类后,用json的格式来创建出各种物品,然后再在代码中解析json,就可以达到解析出的json会把物品的各种信息赋值好,(简单方式就是将各种类的脚本挂载在物品上,制作成预制件,直接使用预制件(因为预制件保存了游戏对象的各种信息,这里json 的作用就是保存装备的各种信息),但是这种方法只适合装备数量不多的情况)(Litjson在解析文件时会区分大小写!!,因此json文件的字段名称应于类中的名称内容和大小写都应该保持一致)

(!!Listjson在在解析枚举类型时,不能"ItemType": "Consumable"解析这种类型的枚举,会自动解析成字符串,因此有两种解决方案:1.使用ISerializationCallbackReceiver接口,序列化完成后再将字符串转为枚举类型,这中需要类中即包含枚举类型有包含字符串类型(这两个要对应)具体参考参考链接。2.将json格式用int类型来表示,比如Consumable对应的枚举类型int就是0,因此这样写参考链接

这是部分json文件内容,用来做测试用

[
  {
    "Id": 1,
    "Name": "血瓶",
    "ItemType": 0,
    "Quality": 1,
    "Description": "这是用来加血的",
    "Capicity": 10,
    "BuyPrice": 10,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/hp",
    "Hp": 100,
    "Mp": 0
  }
]

有了json文件后就可以创建InventoryManager类来 解析jison文件和管理,在解析json时,因为只用itemList集合(list中存放的是Item这个父类)来接收各种装备(子类),因此就需要用到动态解析json的方法,也就是用JsonData来接收(它可以把所有信息接收下来),随后再通过json中特定的标识符进行区分后再new出对应的子类对象放进itemList中

为了理清思路,先附上开发到该阶段时,InventoryManager类该有的东西(还未开发完,后续还会添加内容)

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using LitJson;
using System;

public class InventoryManager : MonoBehaviour
{
    #region 单例模式
    private static InventoryManager _instance;
    public static InventoryManager Instance
    {
        get 
        {
            if( _instance == null )
            {
                _instance = GameObject.Find("InventoryManager").GetComponent<InventoryManager>();//这里是在静态方法中,因此不能直接用this或者gameobject来获取组件
            }
            return _instance;
        }
    }
    #endregion

    #region 解析json
    //这里设计的大概思想就像,首先json文件是一个写好的装备信息,改json文件是一个[{},{}],数组里面的类是继承自Item的,因此设定一个itemList集合
    //将json文件解析到itemList(解析json到数组需要用到Litjson,unity内置的jsonutility不能解析成数组),如果使用itemList = JsonMapper.ToObject<List<Item>>(itemsjson);
    //会出现一个问题,就是虽然能够成功解析到内容,但是因为是json数组里面存放的是Item的子类对象,子类有特定的一些属性就不能成功解析到itemList 集合的对象中(因为itemList存放的是Item对象,是父类对象),因此也就访问不到
    //因此就需要动态解析json,也就是把所有的json信息都要解析出来,不能只将json解析为Item对象,因此就可以使用JsonData类,来接受json的解析,并且通过判断每一个对象的itemtype来newItem的子对象,并add到itemLis中(子类向上转型);如果要使用itemList对象时,就需要向下转型成对应的子类对象
    private List<Item> itemList = new List<Item>();

    private void Start()
    {
        ParseItemJson();
    }
    //解析物品信息
    void ParseItemJson()
    { 
        TextAsset itemText = Resources.Load<TextAsset>("Items");
        string itemsjson = itemText.text;//物品信息的json格式
        //itemList=JsonUtility.FromJson<List<Item>>(itemsjson);//jsonuti不能解析成数组
        JsonData jsondata = JsonMapper.ToObject(itemsjson);
        //itemList = JsonMapper.ToObject<List<Item>>(itemsjson);//将json解析到itemList中,用一个父类List集合保存所有子类对象(我觉得这里有设计缺陷)
        Item itemtemp = null;                                    
        for (int i=0; i<jsondata.Count;i++)//用物品类型来区分
        {
            int id = (int)jsondata[i]["Id"];
            string name = jsondata[i]["Name"].ToString();
            ItemType itemType = (ItemType)((int)jsondata[i]["ItemType"]);
            Quality quality = (Quality)((int)jsondata[i]["Quality"]);
            string description = jsondata[i]["Description"].ToString();
            int capicity = (int)jsondata[i]["Capicity"];
            int buyprice = (int)jsondata[i]["BuyPrice"];
            int sellprice = (int)jsondata[i]["SellPrice"];
            string sprite = jsondata[i]["Sprite"].ToString();
            switch (itemType)
            {
                case ItemType.Consumable:
                    int hp = (int)jsondata[i]["Hp"];
                    int mp = (int)jsondata[i]["Mp"];
                    itemtemp = new Consumable(hp, mp, id, name, itemType, quality, description, capicity, buyprice, sellprice, sprite);
                    break;
                case ItemType.Equipment:
                    break;
                case ItemType.Weapon:
                    break;
                case ItemType.Material:
                    break;
                default:
                    break;
            }
            itemList.Add(itemtemp);
        }
        Consumable test = (Consumable)itemList[0];
        Debug.Log(test.HP + "+" + test.Mp);
    }
    #endregion
}

 能够成功读取到json后,就可以进行UI的设计和搭建了

Unity搭建UI(这里就不详细讲解如何搭建,都学背包系统了还不会搭UI的!!!回去多搭几个)

设计UI需要注意的点:Slot和Item分别做成一个预制件,也就是格子和Item物品分开制作,方便后续添加隐藏(添加的思路就是将item设为格子的子物体,随后把局部坐标赋为0;隐藏同理)

目前搭建效果

UI搭建好后就可以根据UI来设计脚本,以背包面板举例,就会涉及四个脚本,1.Inventory,2.Knapsack,3.Slot,4.ItemUI (具体功能在脚本中)

1.Inventory类,是面板类的父类,面板类也就是背包面板,箱子面板等,这里以背包面板举例,其中就会有些面板类共有的方法属性,如List物品槽,调用存放物品函数,寻找空槽,寻找于item相同的槽等方法

2.Knapsack类,背包面板特有的方法属性(挂载到背包面板上)

3.Slot类,控制格子的脚本,如将Item存放进格子函数(Inventory类调用),获取slot下的item类型,判断当前槽是否装满某装备(挂载到Slot预制件上)

4.ItemUI类,控制Slot中的Item的UI脚本,其中包括Item和数量,设置Item等(挂载带Item预制件上)

下面附上开发到这个阶段的这个四个脚本的源码

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

/// <summary>
/// 所有可以装物品面板的父类,用来装物品
/// </summary>
public class Inventory : MonoBehaviour
{
    //所有装物品的面板都有Slot,因此可以用list集合来存放当前面板的所有slot
    private List<Slot> slotList = new List<Slot>();
   public virtual void Start()//因为还有背包,箱子等面板可能会用到Start函数,所以把它设为虚函数
    {
        slotList.AddRange(GetComponentsInChildren<Slot>());
    }

/// <summary>
/// 存储物品函数
/// </summary>
/// <param name="id">存储id对应的Item</param>
/// <returns>是否存放成功</returns>
    public bool StoreItem(int id)
    {
        Item item =InventoryManager.Instance.GetItemById(id);
        if (item == null)
        {
            Debug.LogWarning("要储存的物品id不存在");
            return false;
        }
        //如果要存储的物品id存在
        if(item.Capicity==1)//如果容量为1
        {
            //放在一个新格子里面
            Slot slot=FindEmptySlot();
            if (slot==null)
            {
                Debug.LogWarning("没有空的物品槽");
                return false;
            }
            else
            {
                slot.StoreItem(item);//把物品存储到空物品槽里面
            }
        }
        else//如果容量不为1
        {
              //找一个和item的id一样的格子进行存放
            Slot slot = FindSameIdSlot(item);
            if (slot != null)
            {
                slot.StoreItem(item);
            }
            else
            {
                Slot emptySlot= FindEmptySlot();
                if(emptySlot!=null)
                {
                    emptySlot.StoreItem(item);
                }
                else
                {
                    Debug.LogWarning("没有空的物品槽");
                    return false;
                }
            }
        }
        return true;
    }

    private Slot FindEmptySlot()//找空的物品槽Slot
    {
        foreach (Slot slot in slotList)
        {
            if(slot.transform.childCount==0)
            {
                return slot;
            }
        }
        return null;
    }
   private Slot FindSameIdSlot(Item item)//找物品槽存放的item与参数itemID相同的slot
    {
        foreach (Slot slot in slotList)
        {
            if(slot.transform.childCount>=1&&slot.GetItemId()==item.Id&&slot.IsFilled()==false)
            {
                return slot;
            }
        }
        return null;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Knapsack : Inventory
{
    #region 单例模式
    private static Knapsack _instance;
    public static Knapsack Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = GameObject.Find("KnapsackPanel").GetComponent<Knapsack>();//这里是在静态方法中,因此不能直接用this或者gameobject来获取组件
            }
            return _instance;
        }
    }
    #endregion
}

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


/// <summary>
/// 这是存储物品的格子脚本
/// </summary>
public class Slot : MonoBehaviour
{
    public GameObject itemPrefab;//这里需要用预制件进行赋值
   public void StoreItem(Item item)//将Item存储到slot格子中(把item放在自身下面),1.如果有item了就,amount++。2.如果没有item,就实例化item放在下面
    {
        if(transform.childCount==0)//如果格子为空时
        {
            GameObject itemGameObject=Instantiate(itemPrefab);
            itemGameObject.transform.SetParent(transform);
            itemGameObject.transform.localPosition = Vector3.zero;
            itemGameObject.GetComponent<ItemUI>().SetItem(item);//!!!这里就是将外部的item和slot中的item同步
        }
        else
        {
            transform.GetChild(0).GetComponent<ItemUI>().AddAmount();
        }
    }

   public int GetItemId()//获取到slot下的Item的Id
    {
        return transform.GetChild(0).GetComponent<ItemUI>().Item.Id;
    }

    public bool IsFilled()//判断当前槽是否装满该装备
    {
        ItemUI itemUI = transform.GetChild(0).GetComponent<ItemUI>();
        return itemUI.Amount >= itemUI.Item.Capicity;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ItemUI : MonoBehaviour
{
   public Item Item { get; private set; }
   public int Amount { get; private set; }

    private Image itemImage;//item中图片和数量
    private Text amountText;

    private void Awake()
    {
        itemImage = GetComponent<Image>();
        amountText=GetComponentInChildren<Text>();
    }

    public void SetItem(Item item,int amount=1)//用参数item设置itemUI中的item
    {
        this.Item = item;
        this.Amount= amount;
        //更新UI
        itemImage.sprite = Resources.Load<Sprite>(item.Sprite);//根据item中的sprite加载Resources文件中的图片并赋值给当前slot中的item
         if(item.Capicity>1)//容量大于1才显示
        amountText.text=Amount.ToString();
        else 
            amountText.text="";
    }
    public void AddAmount(int amount=1)
    {
        this.Amount += amount;
        //更新UI
        amountText.text = Amount.ToString();
    }


}

其中还有个Player脚本用来模拟装备的生成等操作

using UnityEngine;

public class Player : MonoBehaviour
{
    void Update()
    {
        //G 按下G键,随机得到一个物品到背包中
        if (Input.GetKeyDown(KeyCode.G))
        {
            int id = Random.Range(1, 2);//通过生成装备id来表示生成装备
            Knapsack.Instance.StoreItem(id);
        }
    }
}

开发到该阶段,能实现的效果是,按下G键就能生成物品 

开发完这个功能就可以开发ToolTip提示功能,UI就做个自适应的文本框即可(这里采用两个Text的采用代码同步的方式)

ToolTip脚本功能比较简单,就负责信息面板的显示,隐藏,和更改信息面板的显示内容,(需要在InventoryManager中调用)

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

public class ToolTip : MonoBehaviour
{
    private Text toolTipText;
    private Text contentText;
    private CanvasGroup canvasGroup;

    private float targetAlpha = 0;
    private float smoothing = 6f;
    private void Start()
    {
        toolTipText = GetComponent<Text>();
        contentText=transform.Find("Content").GetComponent<Text>();
        canvasGroup = GetComponent<CanvasGroup>();
    }
    private void Update()
    {
        if(canvasGroup.alpha!=targetAlpha)
        {
            canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, targetAlpha, smoothing * Time.deltaTime);
            if(Mathf.Abs(canvasGroup.alpha-targetAlpha)<0.01f)
            {
                canvasGroup.alpha=targetAlpha;
            }
        }
    }
    public void Show(string text)//显示提示信息
    {
        targetAlpha = 1;
        toolTipText.text = text;
        contentText.text = text;
    }
    public void Hide()//隐藏提示信息
    {
        targetAlpha = 0;
    }
}

有了提示面板后,就需要触发提示面板的显示和隐藏,也就是当鼠标移入和移出Slot时判断(因此Slot脚本还需要实现两个鼠标移入移出事件的接口IPointerEnterHandler,IPointerExitHandler,这里还在Item脚本中添加了获取提示面板内容的函数)

Slot脚本添加的内容是: (需实现IPointerEnterHandler,IPointerExitHandler接口)

public void OnPointerExit(PointerEventData eventData)//鼠标移出事件
    {
        if (transform.childCount > 0)
            InventoryManager.Instance.HideToolTip();
    }

    public void OnPointerEnter(PointerEventData eventData)//鼠标移事件
    {
        if(transform.childCount>0)//要格子有item才能显示
        {
            string toolTipText=transform.GetChild(0).GetComponent<ItemUI>().Item.GetToolTipText();
            InventoryManager.Instance.ShowToolTip(toolTipText);
        }
        
    }

InventoryManager脚本需添加的内容:

 private ToolTip toolTip;//信息面板

 private void Start()
    {
        ParseItemJson();
        toolTip=GameObject.Find("ToolTip").GetComponent<ToolTip>();
    }

 public void ShowToolTip(string content)//调用显示信息面板,并赋值内容
    {
        isToolTipsShow = true;
        toolTip.Show(content);
    }
    public void HideToolTip()
    {
        isToolTipsShow = false;
        toolTip.Hide();
    }

Item中添加的脚本:

 /// <summary>
    /// 得到提示面板应该显示的内容(还没有完善,仅做测试)
    /// </summary>
    /// <returns></returns>
    public virtual string GetToolTipText()
    {
        return Name;
    }

 做到这一步能够实现的效果

 可以看见提示面板的显示和隐藏,接下来就是实现提示面板跟随鼠标的效果

就在ToolTip脚本中添加一个设置面板位置的函数,随后在InventoryManager调用即可(这里需要屏幕坐标和rect坐标的转换)

ToolTip脚本添加:

public void SetClocalPosition(Vector3 position)//设置信息面板位置
    {
        transform.localPosition = position;
    }

 InventoryManager脚本添加:

    private bool isToolTipsShow = false;//提示面板是否显示的标志位
    private Canvas canvas;
    private Vector2 toolTipPositionOffset = new Vector2(10, -10);//因为中心轴设置的位置的原因,因此要有个偏差才能达到合适的效果

  private void Update()//在update中坐标转换并调用设置面板位置
    {
        if(isToolTipsShow)
        {
            Vector2 position;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), Input.mousePosition,null, out position);
            toolTip.SetClocalPosition(position + toolTipPositionOffset);
        }
    }

效果:

 做到这里,就可以把前面部分json物品信息的部分进行完善一下了,和完善提示面板信息的内容

这是完善后json文件的装备配置信息

[
  {
    "Id": 1,
    "Name": "血瓶",
    "ItemType": 0,
    "Quality": 1,
    "Description": "这是用来加血的",
    "Capicity": 10,
    "BuyPrice": 10,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/hp",
    "Hp": 100,
    "Mp": 0
  },
  {
    "Id": 2,
    "Name": "蓝瓶",
    "ItemType": 0,
    "Quality": 1,
    "Description": "这是用来加蓝的",
    "Capicity": 10,
    "BuyPrice": 10,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/mp",
    "Hp": 0,
    "Mp": 10
  },
  {
    "Id": 3,
    "Name": "胸甲",
    "ItemType": 1,
    "Quality": 2,
    "Description": "这是一个胸甲",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/armor",
    "Strength": 10,
    "Intellect": 4,
    "Agility": 9,
    "Stamina": 1,
    "equipType": 2
  },
  {
    "Id": 4,
    "Name": "皮腰带",
    "ItemType": 1,
    "Quality": 3,
    "Description": "这皮腰带可以加速",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/belts",
    "Strength": 1,
    "Intellect": 6,
    "Agility": 10,
    "Stamina": 10,
    "equipType": 8
  },
  {
    "Id": 5,
    "Name": "靴子",
    "ItemType": 1,
    "Quality": 4,
    "Description": "这靴子带可以加速",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 10,
    "Sprite": "Sprites/Items/boots",
    "Strength": 10,
    "Intellect": 5,
    "Agility": 0,
    "Stamina": 10,
    "equipType": 6
  },
  {
    "Id": 6,
    "Name": "护腕",
    "ItemType": 1,
    "Quality": 2,
    "Description": "这个护腕可以增加防御",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 10,
    "Sprite": "Sprites/Items/bracers",
    "Strength": 1,
    "Intellect": 2,
    "Agility": 3,
    "Stamina": 4,
    "equipType": 5
  },
  {
    "Id": 6,
    "Name": "护腕",
    "ItemType": 1,
    "Quality": 2,
    "Description": "这个护腕可以增加防御",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 10,
    "Sprite": "Sprites/Items/bracers",
    "Strength": 1,
    "Intellect": 2,
    "Agility": 3,
    "Stamina": 4,
    "equipType": 5
  },
  {
    "Id": 7,
    "Name": "神奇手套",
    "ItemType": 1,
    "Quality": 0,
    "Description": "这是暴击拳套",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/gloves",
    "Strength": 1,
    "Intellect": 2,
    "Agility": 3,
    "Stamina": 4,
    "equipType": 9
  },
  {
    "Id": 8,
    "Name": "头盔",
    "ItemType": 1,
    "Quality": 5,
    "Description": "这是头盔",
    "Capicity": 1,
    "BuyPrice": 10,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/helmets",
    "Strength": 1,
    "Intellect": 2,
    "Agility": 3,
    "Stamina": 4,
    "equipType": 0
  },
  {
    "Id": 9,
    "Name": "项链",
    "ItemType": 1,
    "Quality": 5,
    "Description": "这是很厉害的项链",
    "Capicity": 1,
    "BuyPrice": 10,
    "SellPrice": 5,
    "Sprite": "Sprites/Items/necklace",
    "Strength": 1,
    "Intellect": 2,
    "Agility": 3,
    "Stamina": 4,
    "equipType": 1
  },

  {
    "Id": 10,
    "Name": "戒指",
    "ItemType": 1,
    "Quality": 0,
    "Description": "这是很厉害的戒指",
    "Capicity": 1,
    "BuyPrice": 20,
    "SellPrice": 10,
    "Sprite": "Sprites/Items/rings",
    "Strength": 10,
    "Intellect": 2,
    "Agility": 3,
    "Stamina": 4,
    "equipType": 3
  },
  {
    "Id": 11,
    "Name": "裤子",
    "ItemType": 1,
    "Quality": 1,
    "Description": "这是很厉害的裤子",
    "Capicity": 1,
    "BuyPrice": 40,
    "SellPrice": 20,
    "Sprite": "Sprites/Items/pants",
    "Strength": 20,
    "Intellect": 20,
    "Agility": 20,
    "Stamina": 40,
    "equipType": 4
  },
  {
    "Id": 12,
    "Name": "护肩",
    "ItemType": 1,
    "Quality": 4,
    "Description": "这是很厉害的护肩",
    "Capicity": 1,
    "BuyPrice": 100,
    "SellPrice": 20,
    "Sprite": "Sprites/Items/shoulders",
    "Strength": 20,
    "Intellect": 30,
    "Agility": 40,
    "Stamina": 40,
    "equipType": 7
  },
  {
    "Id": 13,
    "Name": "黑色切割者",
    "ItemType": 2,
    "Quality": 2,
    "Description": "黑切适合半肉战士",
    "Capicity": 1,
    "BuyPrice": 50,
    "SellPrice": 20,
    "Sprite": "Sprites/Items/axe",
    "Damage": 100,
    "weaponType": 1
  },
  {
    "Id": 14,
    "Name": "暴风大剑",
    "ItemType": 2,
    "Quality": 3,
    "Description": "用来合成无尽之刃",
    "Capicity": 1,
    "BuyPrice": 100,
    "SellPrice": 50,
    "Sprite": "Sprites/Items/sword",
    "Damage": 50,
    "weaponType": 0
  },
  {
    "Id": 15,
    "Name": "黑切的合成秘籍",
    "ItemType": 3,
    "Quality": 5,
    "Description": "用来锻造黑切",
    "Capicity": 2,
    "BuyPrice": 100,
    "SellPrice": 99,
    "Sprite": "Sprites/Items/book"
  },
  {
    "Id": 16,
    "Name": "头盔的合成秘籍",
    "ItemType": 3,
    "Quality": 0,
    "Description": "用来锻造头盔",
    "Capicity": 2,
    "BuyPrice": 50,
    "SellPrice": 10,
    "Sprite": "Sprites/Items/scroll"
  },
  {
    "Id": 17,
    "Name": "铁块",
    "ItemType": 3,
    "Quality": 0,
    "Description": "用来锻造其他东西的材料",
    "Capicity": 20,
    "BuyPrice": 5,
    "SellPrice": 4,
    "Sprite": "Sprites/Items/ingots"
  }
]

 完善Item脚本中的GetToolTipText()方法,写为虚方法是为了指向不同种类的物品会显示不同的内容

 /// <summary>
    /// 得到提示面板应该显示的内容
    /// </summary>
    /// <returns></returns>
    public virtual string GetToolTipText()
    {
        string color = "";
        switch (Quality)
        {
            case Quality.Common:
                color = "white";
                break;
            case Quality.Uncommon:
                color = "lime";
                break;
            case Quality.Rare:
                color = "navy";
                break;
            case Quality.Epic:
                color = "magenta";
                break;
            case Quality.Legendary:
                color = "orange";
                break;
            case Quality.Artifact:
                color = "red";
                break;
        }

        string text = string.Format("<color={4}>{0}</color>\n购买价格:{1}出售价格:{2}\n<color=yellow>{3}</color>", Name, BuyPrice, SellPrice, Description,color);
        return text;
    }

 随后再各个Item子脚本中重写GetToolTipText()方法,到达不同的效果

如消耗品Consumable脚本中,重写GetToolTipText()方法

 public override string GetToolTipText()
    {
        string text= base.GetToolTipText();
        string newText=string.Format("{0}\n\n<color=bulue>加血:{1}\n加蓝:{2}</color>",text, HP,Mp);
        return newText;
    }

 后续的如装备,材料等信息的展示内容同理,具体重写的方法就不一一展示(可以参考最终版代码中的内容)

 从这里开始由于是背包系统的附加功能,不展示阶段性的功能代码任何实现的,如有需要去看最终源码

 做到这里,后续就是需要做装备的点击于拖动了,装备的拖动

这里的大概设计思想就是,创建一个Item的对象来跟随鼠标移动,并且用它来表示拾取到的物品,如果拾取到物品pickedItem就会变成Slot中的Item,那么对应的Slot中的物品就应该销毁或者减少,重点就在于Slot中鼠标按下时对当前点击的Slot的Item和已经跟随着鼠标的Item进行比较判断(还有丢弃物品功能)

 箱子面板,面板的显示和隐藏,目前能到达的效果,由于代码耦合度较高,比较复杂,因此就不附上功能阶段性代码(因为该部分也不算是背包系统的重点),该部分重点在于介绍思想,如果想看源码,在文章最后有最终开发完全的源码

角色面板,功能包括对应槽只能穿戴对应装备,点击鼠标右键一键穿戴装备,一键卸载,装备替换等

 开发角色属性数据,通过遍历角色面板中所有的Slot中的Item属性,定义一个更新函数,函数内容是遍历角色面板中的所有Slot中的Item属性,并相加(因此就不用做是穿戴装备还是脱去装备的区分)

 接下来就是做商店功能,功能有买,卖商品,全部卖出和部分卖出的功能

锻造系统,锻造系统的设计需要一个配方类,配方类需要有材料的id数量还有合成结果的id,随后用json格式生成配置文件,json文件的内容就是一个存放配方类的数组;锻造功能的核心在于点击锻造时,利用配方数组进行匹配(算法关键在于两个List的匹配,自身手中物品的List和秘籍需要的物品的List)

 保存加载功能,该部分功能主要原理就是将面板中的所有格子中的item读取成字符串的形式储存(每一个格子的内容都需要存储),随后读取时用Split进行分格,根据不同的字符串内容最后进行不同的操作

 到这里整个背包系统就完结了,从背部系统的附加功能都没有附上代码,可以去参考源码!!(源码里面有详细的注释,推荐看看源码)

梳理一下思路,背包系统核心功能的设计

1.首先就是Item类的设计,item类是一个父类,所有的武器类,装备类,补给品类等都是继承自Item(区分物品的唯一标识就是ID),并且武器类,装备类等又有各自的特有属性

2.在设计完各个物品类后,就需要将物品的配置信息等读取到游戏中,将物品的信息进行配置成json文件,随后解析到游戏中;因为json配置的文件都是Item的各个子类,想要用一个集合把所有的Item子类都保存下来,那么就需要创建一个itemList<Item>这个List集合,接收的对象为Item类(item的子类也可以放进去,这里参考向上转型),这样就用一个父类List存放了所有子类装备(具体实现参考上方文章内容)

3.解析完所有的装备信息后,就是需要设计物品槽和物品的搭建,实现面板和物品之间的交互比如移动物品,获得物品等,是将物品槽和物品都做成单独的预制件,在实现交互等功能(比如将物品储存在槽中,逻辑就是将Item设为槽的子物体,随后将局部坐标变为0就能实现),这里有需要创建多个面板的话(比如背包,商店等面板),就需要有个Inventory父类(是所有面板的父类),子类不同的面板有不同的功能,就可以单独设置

  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值