【Unity随笔】Unity之ugui简单背包实现与Json读取物品信息
程序员文章站
2024-01-20 13:39:58
...
今天记录一个自己制作背包的过程,不带插件,使用原生的UI系统,物品的信息统一使用Json存储与提取。一般来说,相比于txt和xml,Json更适合用在移动设备上,下文会对Unity封装好的Json工具做详细介绍。
整体的结构可能不是非常合理,主要是提供一个思路。该背包的使用建立在一个UI框架之上,之后我会找时间写一个自己设计的UI框架,就当抛砖引玉,希望网友有更好的想法,能够提出来,大家互相学习学习。在背包上的更改建议,我会直接在本文进行修改与试验。
(1)背包框架以及说明
这里我使用了一个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
}
]
}
可能有一些东西没说好,如果有不明白的地方,可以直接提问,我看到了会回复。但有些不懂的地方也还请体谅,我也是菜鸟~