背包系统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父类(是所有面板的父类),子类不同的面板有不同的功能,就可以单独设置
 
