Lưu tiến trình: PlayerPrefs, JSON, và khi nào dùng cái nào
Làm sao lưu tiến trình của người chơi?
Dùng PlayerPrefs cho vài thiết lập lặt vặt, và file JSON trong Application.persistentDataPath cho mọi thứ thật sự là trạng thái game. Ghi file trong cả OnApplicationPause chứ không chỉ OnApplicationQuit — trên điện thoại, Quit thường không bao giờ chạy.
Unity cho bạn hai cách lưu và chúng không cạnh tranh nhau: PlayerPrefs dành cho vài thiết lập lặt vặt, file JSON dành cho trạng thái game. Dùng PlayerPrefs để lưu trạng thái game là cái sai chỉ lộ ra khi dự án đã đi được nửa đường tới ngày phát hành.
PlayerPrefs — thiết lập, và chỉ thiết lập
PlayerPrefs.SetFloat("music", 0.7f);
PlayerPrefs.SetInt("language", 1);
PlayerPrefs.Save();
float music = PlayerPrefs.GetFloat("music", 1f); // 1f nếu chưa từng đặtNó chỉ lưu ba kiểu: int, float và string. Mọi thứ khác phải tự tay ép về một trong ba.
Nó là văn bản thuần trên ổ đĩa — registry trên Windows, file .plist trên macOS, file XML trên Android. Người chơi muốn sửa điểm thì mở trình soạn thảo là sửa được.
Không có cách nào liệt kê những gì đang nằm trong đó, cũng không có cách đánh phiên bản. Định dạng lưu mà lớn dần sẽ vượt quá sức PlayerPrefs, và không có đường nâng cấp.
JSON — trạng thái game thật sự
using System.IO;
using UnityEngine;
[System.Serializable]
public class SaveData
{
public int version = 1; // có lời ngay lần đầu bạn đổi định dạng
public int level;
public float health;
public string[] unlocked;
}
public static class SaveSystem
{
// persistentDataPath là thư mục duy nhất sống sót qua một lần cập nhật app
// trên điện thoại, và cũng là chỗ duy nhất iOS cho phép ghi.
static string Path => System.IO.Path.Combine(Application.persistentDataPath, "save.json");
public static void Save(SaveData data)
{
// Ghi ra file tạm rồi mới tráo. Sập nguồn giữa chừng thì mất bản lưu
// mới, chứ không phá mất bản cũ.
string tmp = Path + ".tmp";
File.WriteAllText(tmp, JsonUtility.ToJson(data, true));
File.Copy(tmp, Path, true);
File.Delete(tmp);
}
public static SaveData Load()
{
if (!File.Exists(Path)) return new SaveData();
return JsonUtility.FromJson<SaveData>(File.ReadAllText(Path));
}
}JsonUtility serialize được gì và không được gì
Nó lưu trường public, và trường private có đánh dấu [SerializeField]. Nó bỏ qua hoàn toàn property có { get; set; } — một bất ngờ rất hay gặp.
Nó không serialize được Dictionary. Hãy lưu hai mảng song song, hoặc một danh sách các class nhỏ có [System.Serializable].
Bản thân class cần [System.Serializable] và không được kế thừa MonoBehaviour. Dữ liệu lưu là dữ liệu, không phải component.
Lưu đúng lúc
// Android và iOS giết app chạy nền mà không hề gọi OnApplicationQuit.
// Pause là callback cuối cùng bạn chắc chắn nhận được.
void OnApplicationPause(bool paused)
{
if (paused) SaveSystem.Save(current);
}
void OnApplicationQuit()
{
SaveSystem.Save(current);
}bài học làm ra thứ này
Lưu tiến trình
Bài này là một công thức đứng riêng. Trong khoá học, cùng thứ đó được dựng như một phần của dự án xuyên suốt năm tab — Xuất lên điện thoại, bài 04.