Unity使用JSON存储实现背包功能
前言
在Unity有五种常用的存储数据的方法,可以用来存储我们游戏的数据。
一、PlayerPrefs
这是Unity自带的一种用于本地持久化保存与读取的一个类,采用以键值对的形式将数据保存在文件中。
1 2 3 4 5 6 int IntValue;float FloatValue;String StringValue; PlayerPrefs.SetFloat("FloatKey" ,FloatValue); PlayerPrefs.SetInt("IntKey" ,IntValue); PlayerPrefs.SetString("StringKey" ,StringValue);
二、读取普通文本:TextAsset
1 2 TextAsset text=(TextAsset)Resources.Load("Text" ); Debug.Log(text.text);
在Project窗口的根目录创建Resources 文件夹,然后把名字为Text.txt的文件夹的文件放在Resources文件夹下就可以读取到。
三、JSON
本篇使用的方法,后续详细讲解。
四、XML储存
本篇不讲解。
五、Splite
本篇不讲解。
具体实现方法
一、背包UI界面创建
本教程使用的UI资源来自Unity Assets Store中的免费资源SIMPLE FANTASY GUI 和Fantasy Wooden GUI : Free 。
1、创建Canvas画布
创建一个Canvas(UI画布),然后在Canvas下创建一个Image,命名为Bag,放入图片,作为背包背景,将其缩放到合适大小,放置到合适位置。
在创建一个DragCanvas,并将sort order 值设置为1,使其内的UI始终高于Canvas的UI显示,后面需要用到。
2、添加组件
然后添加Grid Layout Group 组件,用来控制其子物体的排序格式。
CellSize :每个子物体的大小。
Spacing :每个子物体之间的间隔。
Start Corner :子物体的起始角落。
Start Axis :子物体起始轴线。
3、创建物品格
在Bag下创建一个Image,命名为Slot,如上添加图片,然后复制,达到想要的背包效果。
4、创建Item
在Slot下创建一个Image,命名为Item,用来显示物品的图片,然后在Item下创建一个Text,用来显示物品的数字。将Item制作为预设体,方便后期使用。
5、最终层级关系
二、脚本编写
1、Inventory脚本
首先定义一个物品类,用来存放每个物品信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class InventoryInfo { public string Parent; public string Name; public int Num; public Sprite icon; public enum Type { Weapon, Food } public Type myType; }
在定义一个类来存储物品信息。
1 2 3 4 public class InvenInfoList { public List<InventoryInfo> inventoryInfo = new List<InventoryInfo>(); }
定义脚本所需要的变量。
1 2 3 4 5 6 7 public static Inventory instance; private string filePath = Application.streamingAssetsPath +"/GameData/saveData.json" ;private InvenInfoList list = new InvenInfoList();public GameObject itemPrefab; public GameObject dragCanvas;
我们需要引入命名空间using System.IO
,让我们能往计算机硬盘中写入数据。
接下来需要就需要用到Unity关于Json文件数据的存储。
JsonUtility支持的数据类型。
·支持数字数据类型:int、float、double、decimal、long,包括 uint、float2x4、double2 等数据类型
·支持字符数据类型:char、string
·【特别】支持 Vector 数据类型,包括 Vetor2、Vector3、Vector2x2 等数据类型
·【特别】支持 Quateration 四元数数据类型
·【特别】支持 public 访问类型的类、字段
·【特别】支持 SerializeField 特性指引的类、字段
·JsonUtility.toJson(object target, bool prettyPrint)
object
:对象转化为Json文本。
prettyPrint
:决定最终的 Json 数据文本是否是一个格式化后的数据文本,即是否使用 Json 文本的 Format 化。
·FromJson(string text)
1、将 Json 数据文本转存至类中 public 或 附有 SerializeField 特性的字段上赋值。
2、使用时无需管理值具体分配。其将基于字段命名自行匹配并赋值。
·FromJsonOverwrite(string text, object objectToOverwrite)
编写CreateSave
函数,用于数据存储。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private void CreateSave () { for (int i = 0 ; i < transform.childCount; i++) { InventoryInfo a = new InventoryInfo(); GameObject slot = transform.GetChild(i).gameObject; if (slot.transform.childCount != 0 ) { Item tmp =slot.transform.GetChild(0 ).GetComponent<Item>(); a.Parent = slot.name; a.Name = tmp.Info.Name; a.Num = tmp.Info.Num; a.icon = tmp.Info.icon; a.myType = tmp.Info.myType; list.inventoryInfo.Add(a); } } }
接下来编写SaveByJson
函数,用于向硬盘内写入数据。
1 2 3 4 5 6 7 8 9 10 11 12 private void SaveByJson (){ list.inventoryInfo.Clear(); CreateSave(); string json = JsonUtility.ToJson(list,true ); StreamWriter sw = new StreamWriter(filePath); sw.Write(json); sw.Close(); }
存储数据的内容就写完了,接下来需要编写读取Json数据的函数。编写LoadByJson
函数,并且在Awake
函数中调用它。
1 2 3 4 5 6 7 8 9 10 11 12 private void LoadByJson (){ string json; StreamReader sr = new StreamReader(filePath); json = sr.ReadToEnd(); sr.Close(); list = JsonUtility.FromJson<InvenInfoList>(json); SetGame(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private void SetGame (){ for (int i = 0 ; i < list.inventoryInfo.Count; i++) { InventoryInfo a = new InventoryInfo(); for (int j = 0 ; j < transform.childCount; j++) { GameObject slot = transform.GetChild(j).gameObject; if (slot.name == list.inventoryInfo[i].Parent) { GameObject it = Instantiate(itemPrefab, slot.transform, true ); Item tmp =it.GetComponent<Item>(); RectTransform rt = it.GetComponent<RectTransform>(); rt.offsetMax = new Vector2(-5f , -5f ); rt.offsetMin = new Vector2(5f , 5f ); tmp.Info.Name = list.inventoryInfo[i].Name; tmp.Info.Num = list.inventoryInfo[i].Num; tmp.Info.icon = list.inventoryInfo[i].icon; Image icon = tmp.GetComponent<Image>(); icon.sprite = tmp.Info.icon; tmp.Info.myType = list.inventoryInfo[i].myType; } } } }
2、Item脚本
用于物品信息实时更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Item : MonoBehaviour { public InventoryInfo Info; private TMP_Text a; private void Start () { a = transform.GetChild(0 ).GetComponent<TMP_Text>(); } private void Update () { a.text = Info.Num.ToString(); } }
3、DragItem脚本
首先要实现拖移UI功能,需要先引入三个接口,IBeginDragHandler,IDragHandler,IEndDragHandler
。
然后先创建3个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void OnBeginDrag (PointerEventData eventData ) { } public void OnDrag (PointerEventData eventData ) { } public void OnEndDrag (PointerEventData eventData ) { }
需要实现物品拖移,首先我们需要记录下物品原始的位置,用于拖拽到非法位置之后可以回归到原来的位置,然后需要区分左右键,左键代表正常拖移,右键代表平分物体拖移 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public Transform originalParent; public void OnBeginDrag (PointerEventData eventData ){ originalParent = transform.parent; Item iItem = gameObject.GetComponent<Item>(); if (Average.instance.isLeft || iItem.Info.Num==1 ) { transform.SetParent(Inventory.instance.dragCanvas.transform,true ); } else { GameObject a = Instantiate(gameObject, transform.parent, true ); int num = iItem.Info.Num / 2 ; iItem.Info.Num -= num; Item aItem = a.GetComponent<Item>(); aItem.Info.Num = num; transform.SetParent(Inventory.instance.dragCanvas.transform,true ); } }
正在拖移至需要实时更新物品位置即可。
1 2 3 4 5 public void OnDrag (PointerEventData eventData ){ transform.position = eventData.position; }
左后结束拖移需要判断物品当前位置是否合法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public void OnEndDrag (PointerEventData eventData ){ if (EventSystem.current.IsPointerOverGameObject()) { if (Inventory.instance.CheckInInventoryUI(eventData.position)) { Vector2 a = eventData.pointerEnter.transform.position; GameObject item = null ; double minDistance=1000000000f ; for (int i = 0 ; i < Inventory.instance.transform.childCount; i++) { Vector2 b = Inventory.instance.transform.GetChild(i).transform.position; double dis = Vector2.Distance(a, b); if (dis < minDistance) { minDistance = dis; item = Inventory.instance.transform.GetChild(i).gameObject; } } if (item != null ) { if (Swap(item)) { Item it1 = item.transform.GetChild(0 ).GetComponent<Item>(); Item it2 = transform.GetComponent<Item>(); if (it1.Info.Name == it2.Info.Name) { it1.Info.Num += it2.Info.Num; Destroy(gameObject); } else { item.transform.GetChild(0 ).SetParent(originalParent,true ); transform.SetParent(item.transform, true ); SetRectTransform(gameObject); SetRectTransform(originalParent.GetChild(0 ).gameObject); } } else { transform.SetParent(item.transform, true ); SetRectTransform(gameObject); } } } else { transform.SetParent(originalParent,true ); SetRectTransform(gameObject); } } }
这边需要在Inventory脚本中添加一个CheckInInventoryUI
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 public bool CheckInInventoryUI (Vector3 position ){ for (int i = 0 ; i < transform.childCount; i++) { RectTransform t = transform.GetChild(i).transform as RectTransform; if (RectTransformUtility.RectangleContainsScreenPoint(t, position)) { return true ; } } return false ; }
最后编写swap
函数和SetRectTransform
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private bool Swap (GameObject tmp ){ if (tmp.transform.childCount == 0 ) { return false ; } else { return true ; } } private void SetRectTransform (GameObject tmp ){ RectTransform rt = tmp.transform.GetComponent<RectTransform>(); rt.offsetMax = new Vector2(-5f , -5f ); rt.offsetMin = new Vector2(5f , 5f ); }
4、Average脚本
最后是在DragItem中用到的Average脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static Average instance;public bool isLeft;private void Awake (){ instance = gameObject.GetComponent<Average>(); } void Update (){ if (Input.GetKeyDown(KeyCode.Mouse0)) { isLeft = true ; } else if (Input.GetKeyDown(KeyCode.Mouse1)) { isLeft = false ; } }
效果
完整代码
Inventory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 using System;using System.Collections.Generic;using UnityEngine;using System.IO;using UnityEngine.Serialization;using UnityEngine.UI;[System.Serializable ] public class InventoryInfo { public string Parent; public string Name; public int Num; public Sprite icon; public enum Type { Weapon, Food } public Type myType; } public class InvenInfoList { public List<InventoryInfo> inventoryInfo = new List<InventoryInfo>(); } public class Inventory : MonoBehaviour { public static Inventory instance; private string filePath = Application.streamingAssetsPath +"/GameData/saveData.json" ; private InvenInfoList list = new InvenInfoList(); public GameObject itemPrefab; public GameObject dragCanvas; private void Awake () { dragCanvas = GameObject.Find("DragCanvas" ); instance = GetComponent<Inventory>(); if (File.Exists(filePath)) { LoadByJson(); } } private void Update () { if (Input.GetKeyDown(KeyCode.M)) { SaveByJson(); } } private void CreateSave () { for (int i = 0 ; i < transform.childCount; i++) { InventoryInfo a = new InventoryInfo(); GameObject slot = transform.GetChild(i).gameObject; if (slot.transform.childCount != 0 ) { Item tmp =slot.transform.GetChild(0 ).GetComponent<Item>(); a.Parent = slot.name; a.Name = tmp.Info.Name; a.Num = tmp.Info.Num; a.icon = tmp.Info.icon; a.myType = tmp.Info.myType; list.inventoryInfo.Add(a); } } } private void SaveByJson () { list.inventoryInfo.Clear(); CreateSave(); string json = JsonUtility.ToJson(list,true ); StreamWriter sw = new StreamWriter(filePath); sw.Write(json); sw.Close(); } private void LoadByJson () { string json; StreamReader sr = new StreamReader(filePath); json = sr.ReadToEnd(); sr.Close(); list = JsonUtility.FromJson<InvenInfoList>(json); SetGame(); } private void SetGame () { for (int i = 0 ; i < list.inventoryInfo.Count; i++) { InventoryInfo a = new InventoryInfo(); for (int j = 0 ; j < transform.childCount; j++) { GameObject slot = transform.GetChild(j).gameObject; if (slot.name == list.inventoryInfo[i].Parent) { GameObject it = Instantiate(itemPrefab, slot.transform, true ); Item tmp =it.GetComponent<Item>(); RectTransform rt = it.GetComponent<RectTransform>(); rt.offsetMax = new Vector2(-5f , -5f ); rt.offsetMin = new Vector2(5f , 5f ); tmp.Info.Name = list.inventoryInfo[i].Name; tmp.Info.Num = list.inventoryInfo[i].Num; tmp.Info.icon = list.inventoryInfo[i].icon; Image icon = tmp.GetComponent<Image>(); icon.sprite = tmp.Info.icon; tmp.Info.myType = list.inventoryInfo[i].myType; } } } } public bool CheckInInventoryUI (Vector3 position ) { for (int i = 0 ; i < transform.childCount; i++) { RectTransform t = transform.GetChild(i).transform as RectTransform; if (RectTransformUtility.RectangleContainsScreenPoint(t, position)) { return true ; } } return false ; } }
Item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;using TMPro;public class Item : MonoBehaviour { public InventoryInfo Info; private TMP_Text a; private void Start () { a = transform.GetChild(0 ).GetComponent<TMP_Text>(); } private void Update () { a.text = Info.Num.ToString(); } }
DragItem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 using System;using System.Collections;using System.Collections.Generic;using Unity.VisualScripting;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;public class DragItem : MonoBehaviour ,IBeginDragHandler ,IDragHandler ,IEndDragHandler { public Transform originalParent; public void OnBeginDrag (PointerEventData eventData ) { originalParent = transform.parent; Item iItem = gameObject.GetComponent<Item>(); if (Average.instance.isLeft || iItem.Info.Num==1 ) { transform.SetParent(Inventory.instance.dragCanvas.transform,true ); } else { GameObject a = Instantiate(gameObject, transform.parent, true ); int num = iItem.Info.Num / 2 ; iItem.Info.Num -= num; Item aItem = a.GetComponent<Item>(); aItem.Info.Num = num; transform.SetParent(Inventory.instance.dragCanvas.transform,true ); } } public void OnDrag (PointerEventData eventData ) { transform.position = eventData.position; } public void OnEndDrag (PointerEventData eventData ) { if (EventSystem.current.IsPointerOverGameObject()) { if (Inventory.instance.CheckInInventoryUI(eventData.position)) { Vector2 a = eventData.pointerEnter.transform.position; GameObject item = null ; double minDistance=1000000000f ; for (int i = 0 ; i < Inventory.instance.transform.childCount; i++) { Vector2 b = Inventory.instance.transform.GetChild(i).transform.position; double dis = Vector2.Distance(a, b); if (dis < minDistance) { minDistance = dis; item = Inventory.instance.transform.GetChild(i).gameObject; } } if (item != null ) { if (Swap(item)) { Item it1 = item.transform.GetChild(0 ).GetComponent<Item>(); Item it2 = transform.GetComponent<Item>(); if (it1.Info.Name == it2.Info.Name) { it1.Info.Num += it2.Info.Num; Destroy(gameObject); } else { item.transform.GetChild(0 ).SetParent(originalParent,true ); transform.SetParent(item.transform, true ); SetRectTransform(gameObject); SetRectTransform(originalParent.GetChild(0 ).gameObject); } } else { transform.SetParent(item.transform, true ); SetRectTransform(gameObject); } } } else { transform.SetParent(originalParent,true ); SetRectTransform(gameObject); } } } private bool Swap (GameObject tmp ) { if (tmp.transform.childCount == 0 ) { return false ; } else { return true ; } } private void SetRectTransform (GameObject tmp ) { RectTransform rt = tmp.transform.GetComponent<RectTransform>(); rt.offsetMax = new Vector2(-5f , -5f ); rt.offsetMin = new Vector2(5f , 5f ); } }
Average
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;public class Average : MonoBehaviour { public static Average instance; public bool isLeft; private void Awake () { instance = gameObject.GetComponent<Average>(); } void Update () { if (Input.GetKeyDown(KeyCode.Mouse0)) { isLeft = true ; } else if (Input.GetKeyDown(KeyCode.Mouse1)) { isLeft = false ; } } }
Unity包资源 提取码: a53r