欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Unity随笔】Unity之ugui简单背包实现与Json读取物品信息

程序员文章站 2024-01-20 13:39:58
...

  今天记录一个自己制作背包的过程,不带插件,使用原生的UI系统,物品的信息统一使用Json存储与提取。一般来说,相比于txt和xml,Json更适合用在移动设备上,下文会对Unity封装好的Json工具做详细介绍。
  整体的结构可能不是非常合理,主要是提供一个思路。该背包的使用建立在一个UI框架之上,之后我会找时间写一个自己设计的UI框架,就当抛砖引玉,希望网友有更好的想法,能够提出来,大家互相学习学习。在背包上的更改建议,我会直接在本文进行修改与试验。


(1)背包框架以及说明

【Unity随笔】Unity之ugui简单背包实现与Json读取物品信息

这里我使用了一个UI框架,这个不用管,UI框架的作用只是为了方便管理,和使得整个结构清晰明了。窗体封装了打开与关闭的公有方法,并无涉及到窗体具体的特有功能,所以这里不讲框架并不会造成做不出背包的问题。

(2)脚本以及说明

UI_PacksackForm.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyUI;
using System;
using UnityEngine.EventSystems;

public class UI_PacksackForm : BaseUIForm,UnityEngine.EventSystems.IBeginDragHandler,
    UnityEngine.EventSystems.IDragHandler, UnityEngine.EventSystems.IEndDragHandler{



    /*属性字段*/
    public List<UI_PackCell> DrugCells;              //这个用来存储药品类所有小格子的列表
    public List<UI_PackCell> EquipCells;
    public List<UI_PackCell> SkillCells;
    public List<UI_PackCell> TaskCells;
    private Vector3 _offsetPointerToPack;            //这个鼠标指针点击到背包窗体的中心点的偏移
    private UnityEngine.UI.Button _Btn_ClearUp;      //这个是整理按钮
    private UnityEngine.UI.Button _Btn_Close;        //关闭按钮
    private UnityEngine.UI.ScrollRect _ScrollView;   //这个是ScrollView
    private UnityEngine.UI.Dropdown _DpDn_BackSelect;//背包类型的下拉选择菜单
    public RectTransform DrugContent;                //这是4个不同种类的背包
    public RectTransform EquipContent;
    public RectTransform SkillContent;
    public RectTransform TaskContent;

    private ItemsType _itemsType = ItemsType.Drug;   //背包当前选择的物品种类

    #region 拖拽
    ///开始拖拽
    void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
    {
        Debug.Log("开始拖拽背包");
        Vector3 worldPosition;
        if (RectTransformUtility.ScreenPointToWorldPointInRectangle(transform.GetComponent<RectTransform>(),
            eventData.position, eventData.pressEventCamera, out worldPosition))
        {
            _offsetPointerToPack = worldPosition - transform.GetComponent<RectTransform>().position;
        }

    }
    ///拖拽中
    void IDragHandler.OnDrag(PointerEventData eventData)
    {
        Vector3 worldPosition;
        if (RectTransformUtility.ScreenPointToWorldPointInRectangle(transform.GetComponent<RectTransform>(),
            eventData.position, eventData.pressEventCamera, out worldPosition))
        {
            transform.GetComponent<RectTransform>().position = worldPosition - _offsetPointerToPack;
        }
    }
    ///拖拽结束
    void IEndDragHandler.OnEndDrag(PointerEventData eventData)
    {
        Debug.Log("结束背包拖拽");
    }
    #endregion


    /*公有方法*/
    /// <summary>
    /// 用以注册下拉选项的事件
    /// </summary>
    /// <param name="num"></param>
    public void onValueChange(Int32 num)
    {
        switch(num)
        {
            case 0:
                {
                    DrugContent.gameObject.SetActive(true);
                    EquipContent.gameObject.SetActive(false);
                    SkillContent.gameObject.SetActive(false);
                    TaskContent.gameObject.SetActive(false);
                    _ScrollView.content = DrugContent;
                    _itemsType = ItemsType.Drug;
                }
                break;
            case 1:
                {
                    DrugContent.gameObject.SetActive(false);
                    EquipContent.gameObject.SetActive(true);
                    SkillContent.gameObject.SetActive(false);
                    TaskContent.gameObject.SetActive(false);
                    _ScrollView.content = EquipContent;
                    _itemsType = ItemsType.Equip;
                }
                break;
            case 2:
                {
                    DrugContent.gameObject.SetActive(false);
                    EquipContent.gameObject.SetActive(false);
                    SkillContent.gameObject.SetActive(true);
                    TaskContent.gameObject.SetActive(false);
                    _ScrollView.content = SkillContent;
                    _itemsType = ItemsType.Skil;
                }
                break;
            case 3:
                {
                    DrugContent.gameObject.SetActive(false);
                    EquipContent.gameObject.SetActive(false);
                    SkillContent.gameObject.SetActive(false);
                    TaskContent.gameObject.SetActive(true);
                    _ScrollView.content = TaskContent;
                    _itemsType = ItemsType.Other;
                }
                break;
        }

    }
    /// <summary>
    /// 用以注册整理按钮的事件
    /// </summary>
    public void CleanUp()
    {
        List<UI_PackCell> temp;
        switch (_itemsType)
        {
            case ItemsType.Drug:
                {
                    temp = DrugCells;
                }
                break;
            case ItemsType.Equip:
                {
                    temp = EquipCells;
                }
                break;
            case ItemsType.Skil:
                {
                    temp = SkillCells;
                }
                break;
            case ItemsType.Other:
                {
                    temp = TaskCells;
                }
                break;
            default:
                temp = new List<UI_PackCell>();
                break;
        }
        if (temp.Count > 0)
            for (int i = 0; i < temp.Count; ++i)
            {
                for(int j = i+1; j < temp.Count; ++j)
                {
                    if(temp[i].getGoodsId()<temp[j].getGoodsId()&& temp[j].getGoodsId()>0)
                    {
                        temp[i].Exchange(temp[j]);
                    }
                }
            }

    }

    // Use this for initialization
    void Awake () {
        _DpDn_BackSelect = transform.FindChild("DpDn_PackSelect").GetComponent<UnityEngine.UI.Dropdown>();
        _ScrollView = transform.FindChild("ScrollView").GetComponent<UnityEngine.UI.ScrollRect>();
        _offsetPointerToPack = new Vector3();
        _Btn_ClearUp = transform.FindChild("Btn_ClearUp").GetComponent<UnityEngine.UI.Button>();
        _Btn_Close = transform.FindChild("Btn_Close").GetComponent<UnityEngine.UI.Button>();
        //为整理按钮注册事件
        _Btn_ClearUp.onClick.AddListener(new UnityEngine.Events.UnityAction(CleanUp));
        //为关闭按钮注册事件
        _Btn_Close.onClick.AddListener(new UnityEngine.Events.UnityAction(CloseUIForm));
        //下拉选项的事件注册
        _DpDn_BackSelect.onValueChanged.AddListener(new UnityEngine.Events.UnityAction<int>(onValueChange));
    }
}

UI_PackCell.cs

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

public class UI_PackCell : MonoBehaviour, UnityEngine.EventSystems.IBeginDragHandler,
    UnityEngine.EventSystems.IDragHandler, UnityEngine.EventSystems.IEndDragHandler{

    /*字段属性*/
    public UnityEngine.UI.RawImage _cellRawImage;
    private UnityEngine.UI.Text _Count;
    public byte _goodsId = 0;
    public bool isOccupy = false;   //是否被占用
    private bool isDragging =false; //是否被拖拽


    /*公有方法*/
    /// <summary>
    /// 获取物品的id,id为0则为空
    /// </summary>
    /// <returns></returns>
    public byte getGoodsId()
    {
        return _goodsId;
    }
    /// <summary>
    /// 设置物品id
    /// </summary>
    /// <param name="id"></param>
    public void setGoodsId(byte id)
    {
        _goodsId = id;
    }
    /// <summary>
    /// 获取该物品数量
    /// </summary>
    /// <returns></returns>
    public int getCount()
    {
        return int.Parse(_Count.text);
    }
    /// <summary>
    /// 设置该物品数量
    /// </summary>
    /// <param name="_count"></param>
    public void setCount(int _count)
    {
        _Count.text = _count.ToString();
    }

    #region 拖拽实现
    ///开始拖拽
    void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
    {
        if (isOccupy)
        {
            _cellRawImage.rectTransform.SetParent(transform.parent);
            _cellRawImage.rectTransform.SetAsLastSibling();
            isDragging = true;
        }
    }
    ///拖拽中
    void IDragHandler.OnDrag(PointerEventData eventData)
    {
        if (isDragging)
        {
            Vector3 target;
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(transform.GetComponent<RectTransform>(),
                eventData.position, eventData.pressEventCamera, out target))
            {
                _cellRawImage.transform.position = target;
            }
        }
    }
    ///拖拽结束
    void IEndDragHandler.OnEndDrag(PointerEventData eventData)
    {
        if (isDragging)
        {
            _cellRawImage.rectTransform.SetParent(transform);
            _cellRawImage.rectTransform.localPosition = Vector3.zero;
            if (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.tag == "PackCell")
            {
                _exChanged(eventData.pointerCurrentRaycast.gameObject);
            }

            isDragging = false;
        }
    }
    #endregion

    //私有方法
    /// <summary>
    /// 拖拽交换逻辑
    /// </summary>
    /// <param name="go"></param>
    private void _exChanged(GameObject go)
    {
        UI_PackCell targetCell = go.GetComponent<UI_PackCell>();

       if( targetCell.isOccupy)//目标位被占用
        {
            byte targetId = targetCell.getGoodsId();
            int targetCount = targetCell.getCount();
            //更新目标位置上的参数
            targetCell._cellRawImage.texture = Resources.Load<Texture>(ItemsInfo.getInstance().getSpritePath(getGoodsId()));
            targetCell._cellRawImage.gameObject.SetActive(true);
            targetCell.isOccupy = true;
            targetCell.setGoodsId(getGoodsId());
            targetCell.setCount(getCount());
            //跟新本地位置的参数
            _cellRawImage.texture = Resources.Load<Texture>(ItemsInfo.getInstance().getSpritePath(targetId));
            setGoodsId(targetId);
            setCount(targetCount);
        }
        else//目标位为空
        {
            byte targetId = targetCell.getGoodsId();
            int targetCount = targetCell.getCount();
            //更新目标位置上的参数
            targetCell._cellRawImage.texture = Resources.Load<Texture>(ItemsInfo.getInstance().getSpritePath(getGoodsId()));
            targetCell._cellRawImage.gameObject.SetActive(true);
            targetCell.isOccupy = true;
            targetCell.setGoodsId(getGoodsId());
            targetCell.setCount(getCount());
            targetCell._Count.gameObject.SetActive(true);
            //跟新本地位置的参数
            _cellRawImage.texture = null;
            _cellRawImage.gameObject.SetActive(false);
            isOccupy = false;
            setGoodsId(targetId);
            setCount(0);
            _Count.gameObject.SetActive(false);
        }
    }


    //公有方法
    /// <summary>
    /// 交换图片
    /// </summary>
    /// <param name="targetCell"></param>
    public void Exchange( UI_PackCell targetCell)
    {
        if (isOccupy)
        {
            byte targetId = targetCell.getGoodsId();
            int targetCount = targetCell.getCount();
            //更新目标位置上的参数
            targetCell._cellRawImage.texture = Resources.Load<Texture>(ItemsInfo.getInstance().getSpritePath(getGoodsId()));
            targetCell._cellRawImage.gameObject.SetActive(true);
            targetCell.isOccupy = true;
            targetCell.setGoodsId(getGoodsId());
            targetCell.setCount(getCount());
            //跟新本地位置的参数
            _cellRawImage.texture = Resources.Load<Texture>(ItemsInfo.getInstance().getSpritePath(targetId));
            setGoodsId(targetId);
            setCount(targetCount);
        }
        else
        {
            byte targetId = getGoodsId();
            int targetCount = getCount();
            //更新本地位置的参数
            _cellRawImage.texture = Resources.Load<Texture>(ItemsInfo.getInstance().getSpritePath(targetCell.getGoodsId()));
            _cellRawImage.gameObject.SetActive(true);
            isOccupy = true;
            setGoodsId(targetCell.getGoodsId());
            setCount(targetCell.getCount());
            _Count.gameObject.SetActive(true);
            //更新目标位置上的参数
            targetCell._cellRawImage.texture = null;
            targetCell._cellRawImage.gameObject.SetActive(false);
            targetCell.isOccupy = false;
            targetCell.setGoodsId(targetId);
            targetCell.setCount(0);
            targetCell._Count.gameObject.SetActive(false);
        }
    }


    // Use this for initialization
    void Awake () {
        _cellRawImage = transform.FindChild("Image").GetComponent<UnityEngine.UI.RawImage>();
        _Count = transform.FindChild("Count").GetComponent<UnityEngine.UI.Text>();
    }
}

这里的ItemsInfo先别管,这是用来读取物品信息的类;在后面会有详细说明;

(3)Json文件以及读取方法

ItemsInfo .cs

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

[System.Serializable]
public enum ItemsType
{
    Drug=1,//药品
    Equip,//装备
    Skil,//技能
    Other,//普通物品(收集、任务)
}

/*药品数据类*/
[System.Serializable]
public class DrugData
{
    [System.Serializable]
    public class Drug
    {
        public byte id;//1—50为药品编号
        public string drugName;
        public string sprite_Path;//图片路径(显示于UI背包中)
        public string prefeb_Path;//预制体路径(显示于世界坐标中)
        public string introduce;//物品详细介绍
        public int bloodAdd;//加血量
        public int magicAdd;//加血量
    }
    public string dataType;
    public Drug[] Drugs;
}

/*普通物品数据类*/
[System.Serializable]
public class OtherData
{
    [System.Serializable]
    public class Other
    {
        public byte id;//51—100之间编号
        public string otherName;
        public string sprite_Path;//图片路径
        public string prefeb_Path;//预制体路径
        public string introduce;//物品介绍
    }
    public string dataType;
    public Other[] Others;
}

/*装备物品类*/
[System.Serializable]
public class EquipData
{
    [System.Serializable]
    public class Equip
    {
        public byte id;//101—150之间编号(101-130为武器)
        public string equipName;
        public string sprite_Path;//图片路径
        public string prefeb_Path;//预制体路径
        public string introduce;//物品详细介绍
        public ushort STR;//力量增加值
        public ushort INT;//智力增加值
        public ushort DEX;//敏捷增加值
        public ushort LUC;//幸运增加值
        public ushort HLTH;//体力增加值
    }
    public string dataType;
    public Equip[] Equips;
}

/*技能物品类*/
[System.Serializable]
public class SkillData
{
    [System.Serializable]
    public class SkillField
    {
        public enum Type
        {
            Type_0=0,
            Type_1=1,
            Type_2,
            Type_3,
            Type_4,
            Type_5
        }
        private Type _type;
        public byte[,] SkillType
        {
            get
            {
                switch(_type)
                {
                    case Type.Type_1:
                        return Type_1;
                    case Type.Type_2:
                        return Type_2;
                    case Type.Type_3:
                        return Type_3;
                    case Type.Type_4:
                        return Type_4;
                    case Type.Type_5:
                        return Type_5;
                    default:
                        return null;
                }
            }
        }

        //         1
        //         1
        //         1
        //         1 
        public static byte[,] Type_1 = new byte[9, 9] 
        { 
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 }
        };

        //          1
        //          1
        //   1  1  1  1  1
        //          1
        //          1
        public static byte[,] Type_2 = new byte[9, 9]
        {
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,1,1,1,1,1,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 }
        };

        //       1    1
        //       1    1
        //       1 1 1
        //       1    1
        //       1    1
        public static byte[,] Type_3 = new byte[9, 9]
        {
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,1,0,1,0,0,0 },
            {0,0,0,1,0,1,0,0,0 },
            {0,0,0,1,1,1,0,0,0 },
            {0,0,0,1,0,1,0,0,0 },
            {0,0,0,1,0,1,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 }
        };

        //   1  1  1  1  1
        //          1
        //          1 
        public static byte[,] Type_4 = new byte[9, 9]
        {
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,1,1,1,1,1,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 }
        };

        //           1
        //           1
        //           1
        //           1
        //1  1  1  1  1  1  1
        public static byte[,] Type_5 = new byte[9, 9]
        {
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,0,0,0,1,0,0,0,0 },
            {0,1,1,1,1,1,1,1,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 },
            {0,0,0,0,0,0,0,0,0 }
        };
    }

    [System.Serializable]
    public class Skill
    {
        public byte id;//151_200之间的编号
        public string skillName;
        public string sprite_Path;
        public string prefeb_Path;//这个为技能特效,世界坐标中用于表示技能的物品统一用宝箱代替
        public string introduce;
        public SkillField skillField; 
    }

    public string dataType;
    public Skill[] Skills;

}


public class LoadJson<T> : MonoBehaviour
{
    /// <summary>
    /// 从Json文件中读取数据
    /// </summary>
    /// <param name="_FileName">文件名(文件必须是.json文件)</param>
    /// <returns></returns>
    public static T LoadJsonFromFile(string _FileName)
    {
        if (!File.Exists(Application.dataPath + "/Resources/Config/" + _FileName+".json"))
        {
            return default(T);
        }

        StreamReader sr = new StreamReader(Application.dataPath + "/Resources/Config/" + _FileName + ".json");

        if (sr == null)
        {
            return default(T);
        }
        string json = sr.ReadToEnd();

        if (json.Length > 0)
        {
            return JsonUtility.FromJson<T>(json);
        }

        return default(T);
    }
}


public class ItemsInfo : MonoBehaviour {

    /*属性字段*/
    DrugData Drug;
    SkillData Skill;

    #region debug
    private void ReadTest_Drug(DrugData drug)
    {
        Debug.Log("( ItemsInfo脚本 ) 测试Json读取:" + drug.dataType);
        foreach (var data in drug.Drugs)
            Debug.Log(data.id + " " + data.drugName + " " + data.introduce);
        Debug.Log("\n");
    }
    private void ReadTest_Skill(SkillData skill)
    {
        Debug.Log("( ItemsInfo脚本 ) 测试Json读取:" + skill.dataType);
        foreach (var data in skill.Skills)
            Debug.Log(data.id + " " + data.skillName + " " + data.introduce);
        Debug.Log("\n");
    }
    #endregion

    /*公有方法*/

    public string getSpritePath(byte id)
    {
        if (id < 50 && id > 0)
        {
            foreach (var data in Drug.Drugs)
                if (data.id == id)
                {
                    return data.sprite_Path;
                }
        }

        return "none";
    }


    #region 单例
    private static ItemsInfo _instance;
    public static ItemsInfo getInstance()
    {
        return _instance;
    }
    #endregion


    // Use this for initialization
    void Awake () {
        _instance = this;

        //药品信息的读取
        Drug = LoadJson<DrugData>.LoadJsonFromFile("DrugData");
        //ReadTest_Drug(Drug);

        //技能信息的读取
        Skill = LoadJson<SkillData>.LoadJsonFromFile("SkillData");
        //ReadTest_Skill(Skill);

    }//end Start

}

前面的药品数据类DrugData,普通物品数据类OtherData,装备物品类EquipData,技能物品类SkillData,是为了存储Json文件和读取Json文件的结构类,这4个,只需要看DrugData就行,其他3个不用管。
LoadJson这个类是读取Json文件所封装的类,能够读取Json文件。
那我们怎么存储文件呢?Json是有一定格式的,请往下看Json文件的例子。

DrugData.json

{
"dataType":"Drug",
"Drugs":
    [
     {
         "id": 1, 
         "drugName":"小瓶红药水",
         "sprite_Path": "Atlas/UI2/icon-potion1",
         "prefeb_Path":"None",
         "introduce":"补充少量的HP值",
         "bloodAdd":50,
         "magicAdd":0
     },
     {
         "id": 2, 
         "drugName":"大瓶红药水",
         "sprite_Path": "Atlas/UI2/icon-potion2",
         "prefeb_Path":"None",
         "introduce":"补充大量的HP值",
         "bloodAdd":100,
         "magicAdd":0
     },
     {
         "id": 3,
         "drugName":"小瓶蓝药水",
         "sprite_Path": "Atlas/UI2/icon-potion3",
         "prefeb_Path":"None",
         "introduce":"ֵ补充少量的MP值",
         "bloodAdd":0,
         "magicAdd":50
     }
    ] 
}

可能有一些东西没说好,如果有不明白的地方,可以直接提问,我看到了会回复。但有些不懂的地方也还请体谅,我也是菜鸟~

相关标签: unity json ui