Unity使用JSON存储实现背包功能

Unity使用JSON存储实现背包功能

前言

在Unity有五种常用的存储数据的方法,可以用来存储我们游戏的数据。

一、PlayerPrefs

这是Unity自带的一种用于本地持久化保存与读取的一个类,采用以键值对的形式将数据保存在文件中。

PlayerPrefs

int IntValue;
float FloatValue;
String StringValue;
PlayerPrefs.SetFloat("FloatKey",FloatValue);	//存储float类型的值,对应的键为FloatKey
PlayerPrefs.SetInt("IntKey",IntValue);	//存储int类型的值,对应的键为IntKey
PlayerPrefs.SetString("StringKey",StringValue);	////存储string类型的值,对应的键为StringKey

二、读取普通文本:TextAsset

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 GUIFantasy Wooden GUI : Free

1、创建Canvas画布

创建一个Canvas(UI画布),然后在Canvas下创建一个Image,命名为Bag,放入图片,作为背包背景,将其缩放到合适大小,放置到合适位置。

在创建一个DragCanvas,并将sort order值设置为1,使其内的UI始终高于Canvas的UI显示,后面需要用到。

Bag

2、添加组件

然后添加Grid Layout Group组件,用来控制其子物体的排序格式。

Grid

CellSize:每个子物体的大小。

Spacing:每个子物体之间的间隔。

Start Corner:子物体的起始角落。

Start Axis:子物体起始轴线。

3、创建物品格

在Bag下创建一个Image,命名为Slot,如上添加图片,然后复制,达到想要的背包效果。

Slot

4、创建Item

在Slot下创建一个Image,命名为Item,用来显示物品的图片,然后在Item下创建一个Text,用来显示物品的数字。将Item制作为预设体,方便后期使用。

5、最终层级关系

层级关系

二、脚本编写

1、Inventory脚本

首先定义一个物品类,用来存放每个物品信息。

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 static Inventory instance;	//	将该脚本定义为静态变量。

private string filePath = Application.streamingAssetsPath +"/GameData/saveData.json";//存储位置
private InvenInfoList list = new InvenInfoList();
public GameObject itemPrefab;	//获取Item预设体,用于后面生成。

public GameObject dragCanvas;	//拖拽画布

我们需要引入命名空间using System.IO,让我们能往计算机硬盘中写入数据。

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函数,用于数据存储。

private void CreateSave()		//向list中添加需要序列化存储的数据信息
  {
      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函数,用于向硬盘内写入数据。

private void SaveByJson()
{
    list.inventoryInfo.Clear();		//清空list内的内容

    CreateSave();

    string json = JsonUtility.ToJson(list,true);		//转化为Json文本

    StreamWriter sw = new StreamWriter(filePath);		//写入硬盘
    sw.Write(json);
    sw.Close();
}

存储数据的内容就写完了,接下来需要编写读取Json数据的函数。编写LoadByJson函数,并且在Awake函数中调用它。

private void LoadByJson()
{
    string json;

    StreamReader sr = new StreamReader(filePath);		//获取硬盘中的Json
    json = sr.ReadToEnd();
    sr.Close();

     list = JsonUtility.FromJson<InvenInfoList>(json);	//将其重新写入list中

    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;
            }
        }
    }
}
2、Item脚本

用于物品信息实时更新。

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个函数。

public void OnBeginDrag(PointerEventData eventData)	//开始拖拽
{
  
}

public void OnDrag(PointerEventData eventData)	//正在拖拽
{
  
}

public void OnEndDrag(PointerEventData eventData)	//结束拖拽
{
  
}

需要实现物品拖移,首先我们需要记录下物品原始的位置,用于拖拽到非法位置之后可以回归到原来的位置,然后需要区分左右键,左键代表正常拖移,右键代表平分物体拖移

public Transform originalParent;    //初始位置

public void OnBeginDrag(PointerEventData eventData)
{
    //记录初始位置
    originalParent = transform.parent;
    Item iItem = gameObject.GetComponent<Item>();
    
    if (Average.instance.isLeft || iItem.Info.Num==1)
    {
        //将拖动的物品放到DragCanvas下
        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())//是否指向UI组件
    {
        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函数。

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函数。

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脚本。

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

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

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

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)
        {
            //将拖动的物品放到DragCanvas下
            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())//是否指向UI组件
        {
            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

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;
        }
    }
}

资源下载可前往个人博客:Ackow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值