【Unity学习记录】存档功能

前言

源代码来自M_Studio的《勇者传说》教程,以下是视频链接。
 【存储点及画面效果|Unity2022.2 最新教程《勇士传说》入门到进阶|4K】

我在源代码基础上加入了json序列化保存到本地。

虽然这篇文章以记录为主,为了自己回顾使用,但也稍微加了一点注释方便大家阅读学习。

代码:

DataDefination.cs

为将要保存的对象生成一个uid

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DataDefination : MonoBehaviour
{
    public PersistentType persistentType;
    public string ID;
    private void OnValidate() 
    {
        if(persistentType==PersistentType.ReadWrite){
            if(ID==string.Empty){
                ID = System.Guid.NewGuid().ToString();
            }
        }else{
            ID = string.Empty;
        }
    }
}

public enum PersistentType{
    ReadWrite,DoNotPersist
}

ISavable.cs

接口,规范可保存数据脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ISaveable
{
    DataDefination GetDataId();
    void RegisterSaveData()=>DataManager.instance.RegisterSaveData(this);
    void UnRegisterSaveData()=>DataManager.instance.UnRegisterSaveData(this);

    void GetSaveData(Data data);
    void LoadData(Data data);
}

Data.cs

存储的数据(此处包括场景、角色当前的位置、一些数值信息)和辅助场景存储的方法。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Data
{
    public string sceneToSave;
    public Dictionary<string,Vector3> characterPosDict = new Dictionary<string, Vector3>();
    public Dictionary<string,float> floatSavedData = new Dictionary<string, float>();

    public string getGameSceneJson(GameSceneSO scene){
        return JsonUtility.ToJson(scene);
    }
    public void SaveGameScene(GameSceneSO savedScene){
        sceneToSave = JsonUtility.ToJson(savedScene);
    }
    public GameSceneSO GetSavedScene(){
        var newScene = ScriptableObject.CreateInstance<GameSceneSO>();
        JsonUtility.FromJsonOverwrite(sceneToSave, newScene);
        return newScene;
    }
}

DataManager.cs

存档的主要逻辑

数据会被存放在本地的Application.persistentDataPath\save.json中

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

[DefaultExecutionOrder(-100)]//保证在其他脚本之前执行
public class DataManager : MonoBehaviour
{
    [Header("事件监听")]
    public VoidEventSO saveDataEvent;//监听保存请求,调用save()进行保存
    public VoidEventSO loadDataEvent;//监听加载请求,调用load()加载存档
    public SceneLoadEventSO unloadSceneEvent;
    public VoidEventSO afterSceneLoadEvent;


    public SceneLoader sceneLoader;

    private List<ISaveable> saveableList = new List<ISaveable>();//保存项列表,注册后的保存项会被存在这里
    public Data saveData;//保存的数据

    public static DataManager instance;//单例模式


    // 自定义 JsonSerializerSettings,注册 Vector3Converter,用于vector3的序列化
    private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings {
        Formatting = Formatting.Indented,
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        Converters = new List<JsonConverter> { new Vector3Converter() }
    };
    
    void Update()//测试用(按L保存 按K加载)
    {
        if(Keyboard.current.lKey.wasPressedThisFrame){
            save();
        }
        if(Keyboard.current.kKey.wasPressedThisFrame){
            load();
        }
    }

    void OnEnable()
    {
        saveDataEvent.OnEventRaised += save;
        loadDataEvent.OnEventRaised += load;

        //退出事件注册
        Application.wantsToQuit += OnWantsToQuit;
        Application.quitting += save;
    }
    void OnDisable()
    {
        saveDataEvent.OnEventRaised -= save;
        loadDataEvent.OnEventRaised -= load;

        //退出事件取消注册
        Application.wantsToQuit -= OnWantsToQuit;
        Application.quitting -= save;
    }

    public void Awake()
    {
        //单例模式
        if (instance == null){
            instance = this;
            DontDestroyOnLoad(gameObject);
        }else{
            Destroy(gameObject);
        }
        saveData = new Data();//实例化一个Data
    }
    //退出前时存档
    private bool OnWantsToQuit()
    {
        save();
        return true;
    }
    public void RegisterSaveData(ISaveable saveData){//处理保存项的注册
        if(!saveableList.Contains(saveData)){如果没在则加入
            saveableList.Add(saveData);
        }
    }
    public void UnRegisterSaveData(ISaveable saveData){//处理保存项的注销
        if(saveableList.Contains(saveData)){//如果有则移除
            saveableList.Remove(saveData);
        }
    }
    //保存
    public void save(){
        //防止在主菜单场景保存(这个跟场景加载系统相关)
        if(sceneLoader.currentLoadScene.sceneType==sceneType.Location){
            //遍历每一个保存项,将他们需要保存的数据存放到saveData中
            foreach(ISaveable savable in saveableList){
                savable.GetSaveData(saveData);
            }
            //将saveData序列化存到本地
            SaveToFile(saveData);
        }
    }
    //读取存档
     public void load()
    {   
        //从文件中读取数据到saveData
        saveData = LoadFromFile();
        //这里默认如果存在角色位置信息,则saveData中有数据可读取
        if(saveData.characterPosDict.Count>0){
            Debug.Log("读取存档");
            foreach(var savable in saveableList){//遍历每一个保存项,各自读取数据
                savable.LoadData(saveData);
            }
        }
    }
    //处理文件操作
    public void SaveToFile(Data data)
    {
        string path = Path.Combine(Application.persistentDataPath, "save.json");
        string json = JsonConvert.SerializeObject(data, _jsonSettings);
        File.WriteAllText(path, json);
        Debug.Log($"存档已写入:{path}");
    }

    public Data LoadFromFile()
    {
        string path = Path.Combine(Application.persistentDataPath, "save.json");
        if (!File.Exists(path))
        {
            Debug.LogWarning($"读取失败:文件不存在 ({path})");
            return saveData;
        }

        string json = File.ReadAllText(path);
        Data data = JsonConvert.DeserializeObject<Data>(json, _jsonSettings);
        Debug.Log("存档读取完成");
        return data;
    }

}

VoidEventSO.cs

用于简单的事件通知

using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(menuName = "Event/VoidEventSO")]
public class VoidEventSO : ScriptableObject
{
    public UnityAction OnEventRaised;
    public void RaiseEvent()
    {
        OnEventRaised?.Invoke();
    }
}

Vector3Converter.cs

我让GPT写的

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;

// 1. 定义一个只处理 x,y,z 的 Vector3 转换器
public class Vector3Converter : JsonConverter<Vector3>
{
    public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        writer.WritePropertyName("x"); writer.WriteValue(value.x);
        writer.WritePropertyName("y"); writer.WriteValue(value.y);
        writer.WritePropertyName("z"); writer.WriteValue(value.z);
        writer.WriteEndObject();
    }

    public override Vector3 ReadJson(JsonReader reader, System.Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jo = JObject.Load(reader);
        float x = jo["x"].Value<float>();
        float y = jo["y"].Value<float>();
        float z = jo["z"].Value<float>();
        return new Vector3(x, y, z);
    }
}

加载项的写法

public class 含有需要保存数据的类 : MonoBehaviour,ISaveable
{
    public VoidEventSO loadDataEvent;//如果需要用来在某个时机加载loadDataEvent?.RaiseEvent();广播事件来读取数据

    private void OnEnable(){
        //注册保存数据
        ISaveable savable = this;
        savable.RegisterSaveData();
    }

    private void OnDisable(){
        //注销保存数据
        ISaveable savable = this;
        savable.UnRegisterSaveData();
    }
    //获取uid
    public DataDefination GetDataId()
    {
        return GetComponent<DataDefination>();
    }

    public void GetSaveData(Data data)
    {  
        //TODO:将数据存入data
    }

    public void LoadData(Data data)
    {
        //TODO:从data中读取数据,并完成加载处理
    }
}

结尾

加油!ヾ(◍°∇°◍)ノ゙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值