• 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

  • 相关阅读:
    postgresql获取随机数
    windows环境中Tomcat实现开机自启动
    让Tomcat支持中文路径名和中文文件名
    CentOS 环境变量编辑、保存、立即生效的方法
    eclipse/intellij idea 远程调试hadoop 2.6.0
    利用Spring的@Async异步处理改善web应用中耗时操作的用户体验
    ssh 免密码设置失败原因总结
    hadoop 2.6伪分布安装
    weblogic.nodemanager.common.ConfigException: Native version is enabled but nodemanager native library could not be loaded 解决办法
    velocity模板引擎学习(3)-异常处理
  • 原文地址:https://www.cnblogs.com/0kk470/p/7577120.html
Copyright © 2020-2023  润新知