bài viết được trích của bạn tiến nguyễn trong unityviet
=======================================================
Chúng ta đã biết được cách lưu trữ thông tin đơn giản với PlayerPrefs và ưu điểm của việc sử dụng SimpleJSON để lưu trữ dữ liệu game qua 2 phần trước.
Hôm nay mình xin tiếp tục series Save Game này với Phần 3:
Lưu trữ thông tin phức tạp:
Thế nào là thông tin phức tạp? Đối với các object và thuộc tính của chúng bao gồm các cấu trúc dữ liệu là các loại dữ liệu tập hợp (List, Map, Set…) và ngoài ra còn có các kiểu dữ liệu atomic (bool, int, float) thì chúng ta gọi là phức tạp.
Để phục vụ lưu trữ những object kiểu như thế ta sẽ dùng SimpleJSON để serialize về dạng string và dùng PlayerPrefs để lưu trữ xuống đĩa cứng (persistent).
Trước hết chúng ta sẽ implement một class để phục vụ việc lưu trữ (wrapper PlayerPrefs) và sử dụng interface của class này để phục vụ cho việc dễ dàng thay đổi tầng lưu trữ về sau:
StStorageLocal.cs
using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
public class StStorageLocal {
public static void put(string key, string value) {
PlayerPrefs.SetString(key, value);
PlayerPrefs.Save();
}
public static string get(string key) {
string value = PlayerPrefs.GetString(key);
if(!string.IsNullOrEmpty(value)) {
return value;
}
return value;
}
public static bool exist(string key)
{
return PlayerPrefs.HasKey(key);
}
}
Tiếp tục chúng ta sẽ implement một đối tượng (class) phức tạp và load dữ liệu từ đĩa mỗi khi khởi tạo và lưu trữ xuống đĩa khi có sử thay đổi:
StUserAchiments.cs (lưu trữ thông tin màn chơi và kết quả)
using System.Collections;
using System.Collections.Generic;
using SimpleJSON;
public class StUserAchivements {
private static StUserAchivements mInstance;
private static readonly string keyStorage = "userachivements";
private int curWorld;
private List<List<int>> achievements;
public static int CurWorld
{
get
{
return Instance.curWorld;
}
set
{
Instance.curWorld = value;
Instance.saveLocalStorage();
}
}
public StUserAchivements()
{
curWorld = 0;
achievements = new List<List<int>>();
}
public static StUserAchivements Instance
{
get {
if(mInstance == null) {
mInstance = new StUserAchivements();
mInstance.load();
}
return mInstance;
}
}
public bool assignData(JSONNode jsonNode) {
//Check revision
if (!string.IsNullOrEmpty(jsonNode["curWorld"].Value))
{
curWorld = jsonNode["curWorld"].AsInt;
}
for (int i = 0; i < jsonNode["achievements"].Count; ++i)
{
List<int> list = new List<int>();
for (int j = 0; j < jsonNode["achievements"][i].Count; ++j)
{
list.Add(jsonNode["achievements"][i][j].AsInt);
}
achievements.Add(list);
}
return true;
}
public bool load() {
string data = StStorageLocal.get(keyStorage);
if(!string.IsNullOrEmpty(data)) {
var jsonNode = JSONNode.LoadFromBase64(data);
return assignData(jsonNode);
}
return false;
}
public bool update(string data) {
if(!string.IsNullOrEmpty(data)) {
var jsonNode = JSONNode.Parse(data);
if(assignData(jsonNode)) {
saveLocalStorage();
return true;
}
}
return false;
}
public string saveToBase64() {
var jsonObj = new JSONClass();
jsonObj["curWorld"].AsInt = curWorld;
for (int i = 0; i < achievements.Count; ++i)
{
for (int j = 0; j < achievements[i].Count; ++j)
{
jsonObj["achievements"][i][j].AsInt = (int)achievements[i][j];
}
}
return jsonObj.SaveToBase64();
}
public void saveLocalStorage() {
string data = saveToBase64();
StStorageLocal.put(keyStorage, data);
}
public static List<int> openNewWorld(int index, int numberMap)
{
if (!discoverWorld(index))
{
List<int> list = new List<int>(numberMap);
list.Add(0);
for (int i = 1; i < numberMap; ++i)
{
list.Add(-1);
}
Instance.achievements.Add(list);
Instance.saveLocalStorage();
return list;
}
return null;
}
public static bool isLockMap(int index)
{
if (discoverWorld(Instance.curWorld))
{
if (Instance.achievements[CurWorld][index] != -1)
{
return false;
}
}
return true;
}
public static bool setRankLevel(int index, int type)
{
if (discoverWorld(Instance.curWorld))
{
if (index < Instance.achievements[CurWorld].Count)
Instance.achievements[CurWorld][index] = type;
int nextIndex = index + 1;
if (nextIndex < Instance.achievements[CurWorld].Count)
{
if (Instance.achievements[CurWorld][nextIndex] == -1)
{
Instance.achievements[CurWorld][nextIndex] = 0;
}
}
else
{
Debug.Log("Complete map");
}
Instance.saveLocalStorage();
return true;
}
return false;
}
}
Thảo luận:
+ Chúng ta đã áp dụng mẫu Singleton vào đối tượng StUserAchiment.
+ Đối tượng này chưa thực sự đươc tối ưu hóa (abstract để phục vụ mở rộng, trách việc lập code ở những đối tượng cùng loại)
+ Việc serialize chưa được tự động khi thêm một thuộc tính mới vào đối tượng.
+ Ở các hàm set mình đã thực hiện thao tác put (lưu trữ xuống đĩa) ngày tức thì, việc này sẽ ảnh hưởng đến performance của chương trình. Ở đây chúng ta có thể kiểm tra và lưu trữ một cách thông tin hơn.