前言

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

读取关卡数据的代码:

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

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

public void LevelCompleted()                   // level completed
{
int temp = thisLevel + ; //next level
string nextLevel = temp.ToString();
if (PlayerPrefs.GetInt(nextLevel) == ) // check if next level is locked
PlayerPrefs.SetInt(nextLevel, );
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",);
}

然后实例化一个叫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(, true)); //关卡1肯定必须提前解锁的
for(int i = ; 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 == )
{
//如果不存在,创建并把值设置为1
PlayerPrefs.SetInt("First", );
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 = ; i < Levels.Length;i++)
{
//初始化点击事件监听
ClickListener.Get(Levels[i]).onClick += SelectLevels;
//初始化关卡解锁状态
Locks[i].SetActive(!data.lists[i].IsUnlocked);
}
} public void SelectLevels(GameObject go)
{
int i = ;
for(;i < Levels.Length;i++)
{
if (go == Levels[i])
break;
}
//Load Level 这里为了测试直接打印了
Debug.Log("Load level " + (i + ));
}
}

由于关卡选择直接用的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

Unity3D学习(二):使用JSON进行对象数据的存储读取的更多相关文章

  1. spring boot: @Entity @Repository一个简单的数据读存储读取

    spring boot: @Entity @Repository一个简单的数据读存储读取 创建了一个实体类. 如何持久化呢?1.使用@Entity进行实体类的持久化操作,当JPA检测到我们的实体类当中 ...

  2. Java编程思想学习(二)----一切都是对象

    2.1用应用操作对象 String s; 这里所创建的只是引用,并不是对象.如果此时向s发送一个消息,就会返回一个运行时错误.这是因为此时s没有和任何事物关联.因此,一种安全的做法是:创建一个引用的同 ...

  3. Android读写JSON格式的数据之JsonWriter和JsonReader

    近期的好几个月都没有搞Android编程了,逐渐的都忘却了一些东西.近期打算找一份Android的工作,要继续拾起曾经的东西.公司月初搬家之后就一直没有网络,直到今日公司才有网络接入,各部门才開始办公 ...

  4. Unity3D学习笔记(二十五):Json

    Json:使用固定的文本格式来存储和表示数据! 优点:简介,清晰,易于人的阅读和编写,易于机器的解析和生成. 类似:XML富文本形式 Json的键值对(类中的变量): Json的键值对是使用冒号来区分 ...

  5. javascript学习-创建json对象数据,遍历

    之前我已经有讲过后台返回json数据到前台,并在前台遍历json数据. 这里讲下直接在JS里创建JSON数据,然后遍历使用~ 创建代码例如以下:(创建的是JSON对象) var YearSelect ...

  6. JavaScript学习总结(二)数组和对象部分

    pt学习总结(二)数组和对象部分 2016-09-16    分类:WEB开发.编程开发.首页精华暂无人评论     来源:trigkit4 分享到:更多1 对象部分 Object类型 Object  ...

  7. Android学习笔记之JSON数据解析

    转载:Android学习笔记44:JSON数据解析 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种 ...

  8. xml学习篇(二) ----JSON 和XML对比

    在比较JSON和XML之前,我们先来上一堂关于数据格式的简要历史(更准确的说,是关于XML的始祖): 早在1970年,IBM开发了一种叫Generalized Markup Language的标记语言 ...

  9. Jquery重新学习之五[操作JSON数据]

    Jquery操作Json格式的数据在我们平时的项目中也经常运用:最近看Jquery权威指南中就有一章节是对这方面的整理总结:最后通过一个Asp.net结合一般处理程序ashx的实例,基本上能满足项目中 ...

随机推荐

  1. AngularJS进阶(二十六)实现分页操作

    JS实现分页操作 前言 项目开发过程中,进行查询操作时有可能会检索出大量的满足条件的查询结果.在一页中显示全部查询结果会降低用户的体验感,故需要实现分页显示效果.受前面"JS实现时间选择插件 ...

  2. FPGA学习笔记(一)Verilog语法基础

    一.变量类型 ①数值 数值表示采用 <二进制位数>'<数值表示的进制><数值>的结构. 其中进制可以为b.o.d.h分别代表二.八.十.十六进制. 例如22'd0代 ...

  3. iOS 10正式发布:十大新功能,更注重人性化

    6月14日凌晨消息,苹果公司举行2016年WWDC全球开发者大会,介绍了watch OS.tv OS.OS X以及iOS 10系统的新特性. 据苹果介绍,iOS 10在锁屏.Siri.地图等十个各方面 ...

  4. Android回调详解

         很多时候开发遇到一些Ui更新 网络数据获取,或者方法方法传递的时候会借助回调函数,那么什么是回调函数 百度百科是这么解释的  转载请标注出处 http://blog.csdn.net/sk7 ...

  5. Linux input子系统分析

    输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...

  6. StickyListHeaders的使用

    我们知道在ios中字母的导航有悬停的效果,在android中,git上有大神实现了这种悬停的功能,我们只要将普通的Listview改为StickyListHeadersListView然后设置adap ...

  7. Unity的资源管理

    本篇文章翻译自Unity的官方文档,原文地址:https://docs.unity3d.com/Manual/BehindtheScenes.html Unity自动导入资源的同时为你管理这些资源产生 ...

  8. 深度学习-conv卷积

    过滤器(卷积核) 传统的图像过滤器算子有以下几种: blur kernel:减少相邻像素的差异,使图像变平滑. sobel:显示相邻元素在特定方向上的差异. sharpen :强化相邻像素的差异,使图 ...

  9. R实战 第七篇:网格(grid)

    grid包是R底层的图形系统,可以绘制几乎所有的图形.除了绘制图形之外,grid包还能对图形进行布局.在绘图时,有时候会遇到这样一种情景,客户想把多个代表不同KPI的图形分布到同一个画布(Page)上 ...

  10. go socket

    https://tonybai.com/2015/11/17/tcp-programming-in-golang/ Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程 ...