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

  • Post author:
  • Post category:其他


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



版权声明:本文为qq_62947569原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。