Unity背包系统

发表于2018-03-20
评论1 4.9k浏览
在前面的文章中给大家介绍了背包系统的Json生成与解析背包UI设计。在本文中将和大家介绍整个背包制作流程,由于背包系统制作涉及挺多内容,大家如果想了解透,可以下载本文工程源码。

【开发物品类及其子类】

物品类(KnapsackGood)是一切物品的基类
其中物品分为消耗物品(Consumable)、装备物品(Equitment)、武器物品(Weapon)、材料物品(Material)都继承自物品类(KnapsackGood)

KnapsackGood.cs
using UnityEngine;  
using System.Collections;  
/// <summary>  
/// 物品共有属性类  
/// </summary>  
public class BaseProperty  
{  
    public int ID { get; set; }  
    public string name { get; set; }  
    public string description { get; set; }  
    public int capacity { get; set; }  
    public int buyPrice { get; set; }  
    public int sellPrice { get; set; }  
    public string sprite { get; set; }  
    public KnapsackGood.GoodType goodType { get; set; }  
    public KnapsackGood.GoodQuality goodQuality { get; set; }  
    public BaseProperty()  
    {}  
    public BaseProperty(int ID, string name, string description, int capacity, int buyPrice, int sellPrice, string sprite, KnapsackGood.GoodType goodType, KnapsackGood.GoodQuality goodQuality)  
    {  
        this.ID = ID;  
        this.name = name;  
        this.description = description;  
        this.capacity=capacity;  
        this.buyPrice = buyPrice;  
        this.sellPrice = sellPrice;  
        this.sprite = sprite;  
        this.goodType = goodType;  
        this.goodQuality = goodQuality;  
    }  
}  
/// <summary>  
/// 物品基类  
/// </summary>  
public class KnapsackGood   
{  
    public BaseProperty goodProperty { get; set; }  
    public KnapsackGood()  
    {   
    }  
    public KnapsackGood(BaseProperty goodProperty)  
    {  
        this.goodProperty = goodProperty;  
    }  
    /// <summary>  
    /// 物品类型  
    /// </summary>  
    public enum GoodType  
    {  
        Consumable, //消耗品  
        Equipment,  //装备  
        Weapon,     //武器  
        Meterial    //材料  
    }  
    /// <summary>  
    /// 物品品质  
    /// </summary>  
    public enum GoodQuality  
    {  
        Common,     //普通  
        Uncommon,   //不普通  
        Rare,       //稀有  
        Epic,       //史诗  
        Legendary   //传奇  
    }  
    public virtual string GetDescribe()  
    {  
        string color = "";  
        switch (goodProperty.goodQuality)  
        {  
            case KnapsackGood.GoodQuality.Common:  
                color = "white";  
                break;  
            case KnapsackGood.GoodQuality.Epic:  
                color = "lime";  
                break;  
            case KnapsackGood.GoodQuality.Legendary:  
                color = "navy";  
                break;  
            case KnapsackGood.GoodQuality.Rare:  
                color = "magenta";  
                break;  
            case KnapsackGood.GoodQuality.Uncommon:  
                color = "orange";  
                break;  
            default:  
                break;  
        }  
        string describe = string.Format("<color={0}>{1}</color>\n<size=10><color=green>购买价格:{2} 出售价格:{3}</color></size>\n<color=yellow><size=10>{4}</size></color>", color, goodProperty.name, goodProperty.buyPrice, goodProperty.sellPrice, goodProperty.description);  
        return describe;  
    }  
}  

其他子类是在父类的基础上增加特点属性,因为篇幅在这里我就不贴出其子类的代码,大家可下载源码自行学习,代码不难理解

【创建KnapsackManager(面板与物品之间的媒介)】

KnapsackManager这个类管理面板背包面板与物品,相当于面板与物品之间的媒介,方便管理
using UnityEngine;  
using System.Collections;  
using UnityEditor;  
using LitJson;  
using System.Collections.Generic;  
using System.IO;  
using System.Text;  
using System;  
public class KnapsackManager : MonoBehaviour  
{  
    private static KnapsackManager _instance;  
    public static KnapsackManager GetInstance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                _instance = GameObject.Find("Camera").GetComponent<KnapsackManager>();  
            }  
            return _instance;  
        }  
    }  
    private bool isShowToolTilePanel = false;  
    public GameObject pickGood;  
    public GoodUI GetPickGoodUI  
    {  
        get  
        {  
            return pickGood.GetComponent<GoodUI>();  
        }  
    }  
    public Vector3 GetPickGoodLocalPos  
    {  
        get  
        {  
            return pickGood.transform.localPosition;  
        }  
    }  
    public bool isPickGood = false;  
    /// <summary>  
    /// 存储多个物品对象(从json解析过来)  
    /// </summary>  
    private List<KnapsackGood> goodList;  
    void Awake()  
    {  
        ParseGoodsJson();  
    }  
    private GameObject toolTilePanel;  
    private GameObject canvase;  
    // Use this for initialization  
    void Start ()   
    {  
        toolTilePanel = GameObject.Find("ToolTilePanel");  
        canvase = GameObject.Find("Canvas");  
        pickGood = GameObject.Find("PickGoodUI");  
    }  
    // Update is called once per frame  
    void Update ()   
    {  
        if (isPickGood)  
        {  
            Vector2 point = Vector2.zero;  
            //该函数是将屏幕坐标转化以第一个参数对象的子节点坐标  
            //参数一:需要转换的坐标以该对象作为父节点  
            //参数二:鼠标点  
            //参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null)  
            //参数四:返回一个需要转换的目标点  
            RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent<Canvas>().transform as RectTransform, Input.mousePosition, null, out point);  
            pickGood.GetComponent<GoodUI>().SetLocalPosition(point);  
        }  
        if (isShowToolTilePanel)  
        {  
            Vector2 point=Vector2.zero;  
            //该函数是将屏幕坐标转化以第一个参数对象的子节点坐标  
            //参数一:需要转换的坐标以该对象作为父节点  
            //参数二:鼠标点  
            //参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null)  
            //参数四:返回一个需要转换的目标点  
            RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent<Canvas>().transform as RectTransform,Input.mousePosition,null, out point);  
            toolTilePanel.GetComponent<ToolTilePanel>().SetLocalPosition(point + new Vector2(5,-15));  
        }  
        //                 画布的EventSystem事件上    判断点击鼠标左键位置上是否有UI对象  
        //                                  |                        |  
        //UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject(-1))  
        if(isPickGood&&Input.GetMouseButtonDown(0)&&UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject(-1)==false)  
        {  
            isPickGood = false;  
            GetPickGoodUI.SetLocalPosition(new Vector3(-420, 2, 0));  
            HideToolTilePanel();  
        }  
    }  
    void ParseGoodsJson()  
    {  
        goodList = new List<KnapsackGood>();  
        string path = Application.streamingAssetsPath + "/GoodJson.json";  
        #region 文件流方式读文件  
        /* 
        byte[] bt=new byte[1024]; 
        char[] data=new char[1024]; 
        FileStream fileStream = File.Open(path,FileMode.Open); 
        fileStream.Seek(0,SeekOrigin.Begin); 
        fileStream.Read(bt,0,1024); 
        System.Text.Decoder coder = System.Text.Encoding.UTF8.GetDecoder(); 
        coder.GetChars(bt,0,bt.Length,data,0); 
        Debug.Log(new string(data)); 
        fileStream.Flush(); 
        fileStream.Close(); 
        List<Consumable> goods = JsonMapper.ToObject<List<Consumable>>(new string(data)); 
        foreach (Consumable good in goods) 
        { 
            Debug.Log(good.ToString()); 
        } 
        */  
        #endregion  
        StreamReader sr =new StreamReader(path);  
        string json=sr.ReadToEnd();  
        sr.Close();  
        JsonData data = JsonMapper.ToObject(json);  
        int i = 0;  
        for (i = 0; i <= data.Count - 1; i++)  
        {  
            BaseProperty gp = JsonMapper.ToObject<BaseProperty>(data[i]["goodProperty"].ToJson());  
            KnapsackGood kg = null;  
            switch (gp.goodType)  
            {  
                case KnapsackGood.GoodType.Consumable:  
                    int HP = int.Parse(data[i]["HP"].ToString());  
                    int MP = int.Parse(data[i]["MP"].ToString());  
                    kg = new Consumable(HP,MP,gp);  
                    break;  
                case KnapsackGood.GoodType.Equipment:  
                    int strength = int.Parse(data[i]["strength"].ToString());  
                    int intellect = int.Parse(data[i]["intellect"].ToString());  
                    int agility = int.Parse(data[i]["agility"].ToString());  
                    int stamina = int.Parse(data[i]["stamina"].ToString());  
                    Equipment.EquitmentType equitmentType = (Equipment.EquitmentType)System.Enum.Parse(typeof(Equipment.EquitmentType), data[i]["equitmentType"].ToString());  
                    kg = new Equipment(strength,intellect,agility,stamina,equitmentType,gp);  
                    break;  
                case KnapsackGood.GoodType.Meterial:  
                    break;  
                case KnapsackGood.GoodType.Weapon:  
                    int damage = int.Parse(data[i]["damage"].ToString());  
                    Weapon.WeaponType weaponType = (Weapon.WeaponType)System.Enum.Parse(typeof(Weapon.WeaponType), data[i]["weaponType"].ToString());  
                    kg = new Weapon(damage, weaponType, gp);  
                    break;  
                default:  
                    break;  
            }  
            goodList.Add(kg);  
        }  
    }  
    public KnapsackGood GetGoodWithID(int ID)  
    {  
        foreach (KnapsackGood good in goodList)  
        {  
            if (ID == good.goodProperty.ID)  
            {  
                return good;  
            }  
        }  
        return null;  
    }  
    public void ShowToolTilePanel(string str=" ")  
    {  
        isShowToolTilePanel = true;  
        toolTilePanel.GetComponent<ToolTilePanel>().ShowPanel(str);  
    }  
    public void HideToolTilePanel()  
    {  
        isShowToolTilePanel = false;  
        toolTilePanel.GetComponent<ToolTilePanel>().HidePanel();  
    }  
}  

【创建面板类Inventory】

面板类管理面板中各个物品槽,当存储物品是,寻找合适的物品槽并且管理将物品存储到该物品槽内,并且控制面板的显示和隐藏
Inventory.cs
using UnityEngine;  
using System.Collections;  
public class Inventory : MonoBehaviour  
{  
    public Slot[] slotList;  
    /// <summary>  
    /// 控制面板的透明度从而控制显示或隐藏  
    /// </summary>  
    private float targetAlpha=1;  
    private CanvasGroup cg;  
    private float lerpSpeed=2.0f;  
    protected virtual void Start()  
    {  
        slotList = this.GetComponentsInChildren<Slot>();  
        cg = this.GetComponent<CanvasGroup>();  
    }  
    void Update()  
    {  
        if (targetAlpha != this.cg.alpha)  
        {  
            this.cg.alpha = Mathf.Lerp(this.cg.alpha, targetAlpha, lerpSpeed * Time.deltaTime);  
            if (Mathf.Abs(this.cg.alpha - targetAlpha) <= 0.01f)  
            {  
                this.cg.alpha = targetAlpha;  
            }  
        }  
    }  
    public bool StoreGood(int ID)  
    {  
        KnapsackGood good = KnapsackManager.GetInstance.GetGoodWithID(ID);  
        return StoreGood(good);  
    }  
    public bool StoreGood(KnapsackGood good)  
    {  
        if (good == null)  
        {  
            Debug.Log("需要存储的物品为空");  
            return false;  
        }  
        if (good.goodProperty.capacity == 1)//如果该物品在物品槽中只能存放一个  
        {//直接找到一个还没有使用的物品槽  
            Slot slot = FindEmptySlot();  
            if (slot != null)//如果找到了空的物品槽,那么将该物品发进去  
            {  
                slot.StoreGood(good);  
            }  
            else//如果没有找到,则将说明背包以满  
            {  
                Debug.Log("没有找到空的物品槽,可能是背包以满");  
                return false;  
            }  
        }  
        else  
        {//先寻找是否已经有存放了该物品的物品槽  
            Slot slot = FindFreeSlot(good.goodProperty.ID);  
            if (slot != null)  
            {  
                slot.StoreGood(good);  
            }  
            else  
            {  
                Slot s = FindEmptySlot();  
                if (s != null)  
                {  
                    s.StoreGood(good);  
                }  
                else  
                {  
                    Debug.Log("没有找到空的物品槽,可能是背包以满");  
                    return false;  
                }  
            }  
        }  
        return true; ;  
    }  
    /// <summary>  
    /// 根据ID寻找存储了该物品的物品槽  
    /// </summary>  
    /// <returns></returns>  
    private Slot FindFreeSlot(int ID)  
    {  
        foreach (Slot s in slotList)  
        {  
            if (s.transform.childCount > 0 && s.GetGoodID() == ID&&s.IsFill()==false)  
            {  
                return s;  
            }  
        }  
        return null;  
    }  
    private Slot FindEmptySlot()  
    {  
        foreach (Slot s in slotList)  
        {  
            //还没有存放物品的物品槽中也没有添加GoodUI,根据该物品槽中是否有子节点这个线索来判断是否存放物品  
            if (s.transform.childCount == 0||s.transform.gameObject.activeInHierarchy==false)  
            {  
                return s;  
            }  
        }  
        //没有找到空的物品槽,说明背包以满  
        return null;  
    }  
    public void ShowPanel()  
    {  
        targetAlpha = 1;  
        this.cg.blocksRaycasts = true;  
    }  
    public void HidePanel()  
    {  
        targetAlpha = 0;  
        this.cg.blocksRaycasts = false;  
    }  
}  

创建一个KnapsackPanel.cs挂载到面板上,继承自Inventory
using UnityEngine;  
using System.Collections;  
public class KnapsackPanel : Inventory  
{  
    private static KnapsackPanel _instance;  
    public static KnapsackPanel GetInstance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                _instance = GameObject.Find("KnapsackPanel").GetComponent<KnapsackPanel>();  
            }  
            return _instance;  
        }  
    }  
}  

【创建物品槽类】

物品槽类控制显示物品UI,提示面板的显示和隐藏
该类是挂载到物品槽上
using UnityEngine;  
using System.Collections;  
using UnityEngine.EventSystems;  
using System;  
public class Slot : MonoBehaviour ,IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler  
{  
    // Use this for initialization  
    void Start ()  
    {  
    }  
    // Update is called once per frame  
    void Update ()  
    {  
    }  
    public int GetGoodID()  
    {  
        if (this.transform.childCount == 0)  
        {  
            return -1;  
        }  
        GoodUI gu = this.transform.GetChild(0).GetComponent<GoodUI>();  
        return gu.good.goodProperty.ID;  
    }  
    public bool IsFill()  
    {  
        GoodUI gu = this.transform.GetChild(0).GetComponent<GoodUI>();  
        return gu.good.goodProperty.capacity <= gu.amount;//物品的容量小于物品当前的数量  
    }  
    public GameObject goodPrefab;  
    public void StoreGood(KnapsackGood good,int amount=1)  
    {  
        if (this.transform.childCount == 0)//如果该物品槽为空,则在这物品槽里新建一个GoodUI  
        {  
            GameObject obj = GameObject.Instantiate(goodPrefab);  
            obj.transform.SetParent(this.transform);  
            obj.transform.localPosition = Vector3.zero;  
            obj.transform.localScale = Vector3.one;  
            obj.GetComponent<GoodUI>().SetGood(good,amount);  
        }  
        else  
        {  
            if (this.transform.GetChild(0).gameObject.activeInHierarchy == false)  
            {  
                this.transform.GetChild(0).GetComponent<GoodUI>().Show();  
                this.transform.GetChild(0).GetComponent<GoodUI>().SetGood(good,amount);  
            }  
            else  
            {  
                this.transform.GetChild(0).GetComponent<GoodUI>().AddGood();  
            }  
        }  
    }  
    public  void OnPointerEnter(PointerEventData eventData)  
    {  
        Debug.Log("鼠标进入物品槽");  
        if (this.transform.childCount > 0)  
        {  
            //获得描述文本  
            string text = this.transform.GetChild(0).GetComponent<GoodUI>().good.GetDescribe();  
            KnapsackManager.GetInstance.ShowToolTilePanel(text);  
        }  
    }  
    public void OnPointerExit(PointerEventData eventData)  
    {  
        if (this.transform.childCount > 0)  
        {  
            KnapsackManager.GetInstance.HideToolTilePanel();  
        }  
    }  
    /// <summary>  
    /// 鼠标抬下  
    ///   
    /// **************************方法说明*************************************  
    /// *//物品槽里没有物品                                                   *  
    /// *   //1.IsPickGood=true     将PickGood里的物品放置到该物品槽内         *  
    /// *   //2.IsPickGood=false    不做任何处理                            *  
    /// *//物品槽里存在物品                                                   *                   
    /// *   //1.IsPickGood=true     将该物品槽的物品替换PickGood里的物品        *   
    /// *   //2.IsPickGood=false    将该物品槽的物品放置在PickGood里          *  
    /// ***********************************************************************  
    /// </summary>  
    /// <param name="eventData"></param>  
    public virtual void OnPointerDown(PointerEventData eventData)  
    {  
        if (this.transform.childCount == 0 || this.transform.GetChild(0).gameObject.activeInHierarchy == false)  
        {  
            if (KnapsackManager.GetInstance.isPickGood)  
            {  
                Debug.Log(KnapsackManager.GetInstance.GetPickGoodUI.amount);  
                StoreGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount);  
                KnapsackManager.GetInstance.GetPickGoodUI.SetLocalPosition(new Vector3(-420,2,0));//将pickGoodUI位置归零  
                KnapsackManager.GetInstance.isPickGood = false;  
            }  
            else  
            {  
                //无需做处理  
            }  
        }  
        else  
        {  
            if (KnapsackManager.GetInstance.isPickGood)  
            {  
                StorageSlot.slot.StoreGood(this.transform.GetChild(0).GetComponent<GoodUI>().good, this.transform.GetChild(0).GetComponent<GoodUI>().amount);  
                //this.transform.GetChild(0).GetComponent<GoodUI>().Exchange(Slot.GetInstance.transform.GetChild(0).GetComponent<GoodUI>());  
                //StoreGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount);  
                this.transform.GetChild(0).GetComponent<GoodUI>().SetGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount);  
                KnapsackManager.GetInstance.GetPickGoodUI.SetLocalPosition(new Vector3(-420, 2, 0));//将pickGoodUI位置归零  
                KnapsackManager.GetInstance.isPickGood = false;  
            }  
            else  
            {  
                StorageSlot.slot = this;  
                KnapsackManager.GetInstance.GetPickGoodUI.ReplaceGoodUI(transform.GetChild(0).GetComponent<GoodUI>());  
                KnapsackManager.GetInstance.isPickGood = true;  
                //将自身物品槽的UI销毁  
                Destroy(this.transform.GetChild(0).gameObject);  
            }  
        }  
    }  
}  

【创建GoodUI】

该类挂载在物品槽的子对象上
控制物品现在在物品槽内
using UnityEngine;  
using System.Collections;  
using UnityEngine.UI;  
public class GoodUI : MonoBehaviour  
{  
    public KnapsackGood good { get; private set; }//物品槽中存放的物品  
    public int amount { get; private set; }//物品槽存放物品的数量  
    private Image GoodUIImage  
    {  
        get  
        {  
            return this.transform.GetComponent<Image>();  
        }  
    }  
    private Text GoodUIText  
    {  
        get  
        {  
            return this.transform.GetComponentInChildren<Text>();  
        }  
    }  
    public void SetGood(KnapsackGood good, int amount = 1)  
    {  
        this.good = good;  
        this.amount = amount;  
        GoodUIImage.sprite = Resources.Load<Sprite>(good.goodProperty.sprite);  
        if (good.goodProperty.capacity > 1)  
        {  
            GoodUIText.text = this.amount.ToString();  
        }  
        else  
        {  
            GoodUIText.text = "";  
        }  
    }  
    public void AddGood(int amount = 1)  
    {  
        this.amount += amount;  
        if (good.goodProperty.capacity > 1)  
        {  
            GoodUIText.text = this.amount.ToString();  
        }  
        else  
        {  
            GoodUIText.text = "";  
        }  
    }  
    public void ReplaceGoodUI(GoodUI gu)  
    {  
        this.good = gu.good;  
        this.amount = gu.amount;  
        Debug.Log(gu.amount);  
        SetGood(this.good, this.amount);  
    }  
    public void Exchange(GoodUI gu)  
    {  
        KnapsackGood tempGood = gu.good;  
        int tempAmount = gu.amount;  
        gu.SetGood(good, amount);  
        this.SetGood(tempGood, tempAmount);  
    }  
    public void SetLocalPosition(Vector3 point)  
    {  
        this.transform.localPosition = point;  
    }  
    public void Show()  
    {  
        this.gameObject.SetActive(true);  
    }  
    public void Hide()  
    {  
        this.gameObject.SetActive(false);  
    }  
}  

【测试】

创建一个UserPlaying.cs来测试代码
using UnityEngine;  
using System.Collections;  
public class UserPlaying : MonoBehaviour  
{  
    // Update is called once per frame  
    void Update ()  
    {  
        if (Input.GetKeyUp(KeyCode.G))  
        {  
            int rand = Random.Range(1,8);  
            KnapsackPanel.GetInstance.StoreGood(rand);  
        }  
}  
}  

按下G键,发现随机生成了物品并显示在物品槽中

建议大家下载我的源码学习,这样有助于理解,源码中也写了角色背包,角色面板的角色物品槽处理和背包不尽相同,大体差不多

好了,背包系统介绍到这里。

总结:

在这里我着重介绍两个知识点:
一、RectTransformUtility.ScreenPointToLocalPointInRectangle函数的使用
//该函数是将屏幕坐标转化以第一个参数对象的子节点坐标  
            //参数一:需要转换的坐标以该对象作为父节点  
            //参数二:鼠标点  
            //参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null)  
            //参数四:返回一个需要转换的目标点  
            RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent<Canvas>().transform as RectTransform,Input.mousePosition,null, out point);  

二、IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler接口的使用
以上接口的实现函数分别在鼠标移入该对象、移除该对象、点击该对象时触发调用
其中会传进一个参数UnityEngine.EventSystems.PointerEventData eventData
可以根据这个参数,解析你需要的事件
比如:
if (eventData.button != PointerEventData.InputButton.Left)  
        {  
            return;  
        }  
背包系统源码:https://download.csdn.net/download/qq_33747722/9819495
来自:http://blog.csdn.net/qq_33747722/article/details/70244303

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论