• Unity3D学习(二):使用JSON进行对象数据的存储读取


    前言

    前段时间完成了自己的小游戏Konster的制作,今天重新又看了下代码。原先对关卡解锁数据的存储时用了Unity自带的PlayerPref(字典式存储数据)。

    读取关卡数据的代码:

    void Awake ()
        {
            foreach(Transform Child in transform) //Update Level Lists
            {
                //Get Level's Name
                string levelname = Child.name.Substring(0,1); 
    
                //Load level data ,check if it is unlocked
                if(PlayerPrefs.GetInt(levelname) == 0)  //Set Lock
                {
                    Child.GetChild(1).gameObject.SetActive(true);
                }
                else
                {
                    Child.GetChild(1).gameObject.SetActive(false);
                }
            }
        }

    某一关通关时,解锁下一关:

    public void LevelCompleted()                   // level completed
        {
            int temp = thisLevel + 1; //next level
            string nextLevel = temp.ToString();
            if (PlayerPrefs.GetInt(nextLevel) == 0) // check if next level is locked
                PlayerPrefs.SetInt(nextLevel, 1);
            PlayerPrefs.Save();
            StartCoroutine(GameSuccess());  //Show the GameSuccess UI
        }

    个人觉得虽然使用起来方便,但是如果我想存取一些对象结构数据的话很麻烦,于是自己试着写了个简单的对象数据存储。

    使用JSON存储数据

    什么是Json

    JSON是一种轻量级的数据交换格式。它的本质其实就是一种对象以键值为基础保存的字符串。

    比如有一个Person类:

    public class Person
    {
       public string name;
       public int age;
       public Person(string _name,int _age)
      {
          // constructor
             name = _name;
             age = _age;
      }
    }
    
    //
    static void Main()
    {
         Person kk = new Person("KK",20);
    }

    然后实例化一个叫KK的人,那么它的JSON字符串格式为:{"name": "KK","age":"19"}。

    应用:利用JSON对关卡数据进行存储

    这里使用的是JsonFx,一个基于.NET的JSON操作库。下载地址1(CSDN) 下载地址2(GitHub)

     首先我们需要确定下数据的存储路径,这里定义了一个FilePath类来存放所有路径(全局变量)。

    public class FilePath{
        public static readonly string GAMEDATA_PATH = Application.dataPath + "/GameData"; //游戏数据的文件夹
        public static readonly string LEVEL_PATH = GAMEDATA_PATH + "/leveldata.txt"; //关卡解锁数据存储路径
    //之后有新的数据需要存储路径时可以拓展
    }

    然后将JSON操作和文件IO操作简单封装一下:

    using JsonFx.Json;
    using System.IO;
    public class DataManager{
       //保存对象数据
        public static void SaveClass<T>(T saveClass,string path)
        {
           //如果游戏数据文件夹不存在,创建一个文件夹
            if(!Directory.Exists(FilePath.GAMEDATA_PATH))
                Directory.CreateDirectory(FilePath.GAMEDATA_PATH);
            File.WriteAllText(path, JsonWriter.Serialize(saveClass));
            
        }
    
       //读取对象数据
        public static T LoadClass<T>(string path)
        {
            string list = File.ReadAllText(path);
            return JsonReader.Deserialize<T>(list);
        }
    }

    然后定义一个Level类保存单个关卡数据,需要特别注意的是,JSON序列化的对象必须显式地指明默认构造函数,不然JsonFx会报错。

    public class Level{
        //关卡编号
        public int LevelNumber { get; set; }
       //关卡是否解锁
        public bool IsUnlocked { get; set; }
        public Level(){}  //必须提供默认构造函数
        public Level(int _levelnumber,bool _IsUnlocked)
        {
            LevelNumber = _levelnumber;
            IsUnlocked = _IsUnlocked;
        }
    }

    再定义一个LevelList来存储所有的关卡。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class LevelLists{
    
        public List<Level> lists;
    
        //默认构造函数,用于json序列化与反序列化
        public LevelLists() { }
    
        /// <summary>
        ///  赋值构造函数,用于游戏第一次启动的初始化
        /// </summary>
        public LevelLists(int levels)
        {
            lists = new List<Level>();
            lists.Add(new Level(1, true));  //关卡1肯定必须提前解锁的
            for(int i = 2; i <= levels;i++ )
            {
               lists.Add(new Level(i, false)); //其余关卡锁定
            }
        }
    }

    然后为了在游戏第一次启动时初始化数据,我们需要一个脚本FirstLaunch来检测游戏是否是第一次启动。在场景里创建一个空物体挂上这个脚本即可。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class FirstLaunch : MonoBehaviour {
        public int InitLevelNum; //初始化的关卡数,在编辑器的Inspector里填写好,我填写的是4关
        void Awake()
        {
            Check();
        }
    
        void Check() //检查游戏是否是第一次启动
        {
            int IsFirstLaunch = PlayerPrefs.GetInt("First");  //获取这个键对应的值,不存在默认为0
            if(IsFirstLaunch == 0) 
            {
                //如果不存在,创建并把值设置为1
                PlayerPrefs.SetInt("First", 1);
                PlayerPrefs.Save();
                InitializeLevelLists();
            }
        }
    
        void InitializeLevelLists() //在本地初始化关卡列表的数据
        {
           
            LevelLists mylist = new LevelLists(InitLevelNum);
            DataManager.SaveClass<LevelLists>(mylist, FilePath.LEVEL_PATH);
        }
    }

    然后对于关卡列表,我们写一个脚本来管理它的状态

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class LevelsControll : MonoBehaviour{
        public GameObject[] Levels;  //关卡选择
        public GameObject[] Locks;   //关卡锁
        // Use this for initialization
    
    
        void Start()
        {
            Initialize();
        }
        
        void Initialize()   //关卡列表初始化
        {
            //读取关卡数据
            LevelLists data = DataManager.LoadClass<LevelLists>(FilePath.LEVEL_PATH);
            for(int i = 0; i < Levels.Length;i++)
            {
                //初始化点击事件监听
               ClickListener.Get(Levels[i]).onClick += SelectLevels;
                //初始化关卡解锁状态
               Locks[i].SetActive(!data.lists[i].IsUnlocked);
            }
        }
    
        public void SelectLevels(GameObject go)
        {
            int i = 0;
            for(;i < Levels.Length;i++)
            {
                if (go == Levels[i])
                    break;
            }
            //Load Level 这里为了测试直接打印了
            Debug.Log("Load level " + (i + 1));
        }
    }

    由于关卡选择直接用的UGUI的Image组件,它没有UI点击事件处理,但我们可以自己写一个脚本实现Unity的点击事件接口IPointerClickHandler来监听点击事件。

    using UnityEngine;
    using UnityEngine.EventSystems;
    
    public class ClickListener : MonoBehaviour, IPointerClickHandler //注意这是Unity提供的点击接口
    {
        public delegate void VoidDelegate(GameObject go); //事件委托
        public VoidDelegate onClick;
    
        public static ClickListener Get(GameObject go) //获取对应GameObject的Listener脚本,没有就新增一个
        {
            ClickListener listener = go.GetComponent<ClickListener>();
            if (listener == null)
                listener = go.AddComponent<ClickListener>();
            return listener;
        }
    
        public void OnPointerClick(PointerEventData eventData)
        {
            if (onClick != null)
                onClick(gameObject);
        }
    }

    OK,然后在场景中弄好UI,挂上脚本,点击运行。

    可以发现创建了一个叫GameData的文件夹,然后该文件夹下新建了一个leveldata.txt文件,里面存储了关卡的数据。

    停止运行,然后这里我再在文本文件里把第四关的IsUnlocked属性修改为true,再运行,发现第四关解锁了。

         

    由此,一个简单的关卡数据存储也就完成了。

    当然这样没有什么安全性,玩家只需要在对应文件夹里修改这个文本文件就能解锁了,因此有些时候我们需要对数据进行加密。关于数据加密又是另一个话题了( ̄▽ ̄)。

    参考资料

    《Unity5.X开发指南》   作者:罗盛誊

     UGUI研究院之控件以及按钮的监听事件系统(五)  作者:雨凇MOMO

  • 相关阅读:
    apache安全—用户访问控制
    hdu 3232 Crossing Rivers 过河(数学期望)
    HDU 5418 Victor and World (可重复走的TSP问题,状压dp)
    UVA 11020 Efficient Solutions (BST,Splay树)
    UVA 11922 Permutation Transformer (Splay树)
    HYSBZ 1208 宠物收养所 (Splay树)
    HYSBZ 1503 郁闷的出纳员 (Splay树)
    HDU 5416 CRB and Tree (技巧)
    HDU 5414 CRB and String (字符串,模拟)
    HDU 5410 CRB and His Birthday (01背包,完全背包,混合)
  • 原文地址:https://www.cnblogs.com/0kk470/p/7577120.html
Copyright © 2020-2023  润新知