前言
前段時間完成了自己的小游戲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開發指南》 作者:羅盛謄