属性绑定式的响应——全自动序列化和反序列化

属性绑定式的响应——全自动序列化和反序列化

昨天在做游戏配置项的本地化存储

想着把这个东西做的智能一点 完全不需要操心序列化的时机和反序列化的时机

所以就考虑了属性绑定式的响应

去监听属性的修改 只要有属性被修改就能够自动被序列化到本地

求助了一下万能的gpt 在它的帮助下最终实现是这样的

using System;
using UnityEngine;
using System.ComponentModel;

[Serializable]
public class GameSetting : INotifyPropertyChanged
{
    [SerializeField]
    private bool birthPlace = true;

    public bool BirthPlace
    {
        get => birthPlace;
        set
        {
            if (birthPlace != value)
            {
                birthPlace = value;
                OnPropertyChanged(nameof(BirthPlace));
            }
        }
    }

    [SerializeField]
    private bool test_0 = true;

    public bool Test_0
    {
        get => test_0;
        set
        {
            if (test_0 != value)
            {
                test_0 = value;
                OnPropertyChanged(nameof(test_0));
            }
        }
    }

    //新增属性只要像Test_0这样就可以了

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class GameSettingManager
{
    private static readonly string KEY = "GameSetting";

    public static GameSettingManager Instance { get; } = new GameSettingManager();

    private GameSetting config;
    public GameSetting Config
    {
        get
        {
            if (config == null)
            {
                //单例内的config为空的时候 要去反序列化一遍
                config = LoadGameSetting();
                //注册一下监听的方法
                config.PropertyChanged += OnConfigPropertyChanged;
            }
            return config;
        }
        set
        {
            if (value == null)
            {
                Debug.LogError("不能空 如果要直接Set一定要传值");
            }
            if (config != null)
            {
                //不为空 先取消旧的监听事件
                config.PropertyChanged -= OnConfigPropertyChanged;
            }
            config = value;
            //重新修改后 要再添加新的监听事件
            config.PropertyChanged += OnConfigPropertyChanged;
            SaveGameSetting(config);
        }
    }

    private GameSettingManager()
    {
        if (Instance != null)
        {
            Debug.LogError("出现了多个游戏设置单例,要检查一下为什么");
        }
    }

    /// <summary>
    /// 保存游戏设置
    /// </summary>
    /// <param name="config"></param>
    private void SaveGameSetting(GameSetting config)
    {
        string json = JsonUtility.ToJson(config);
        PlayerPrefs.SetString(KEY, json);
        Debug.Log("触发游戏设置序列化操作" + json);
    }

    /// <summary>
    /// 加载游戏设置
    /// </summary>
    /// <returns></returns>
    private GameSetting LoadGameSetting()
    {
        if (PlayerPrefs.HasKey(KEY))
        {
            string json = PlayerPrefs.GetString(KEY);
            Debug.Log("触发游戏设置反序列化操作" + json);
            var config = JsonUtility.FromJson<GameSetting>(json);
            //每次新的对象都要注册一下
            config.PropertyChanged += OnConfigPropertyChanged;
            return config;
        }
        else
        {
            //没有的话就创建一个新的 参数都给默认值
            var config = new GameSetting { BirthPlace = true };
            //同上 新对象都要注册一下
            config.PropertyChanged += OnConfigPropertyChanged;
            SaveGameSetting(config);
            return config;
        }
    }

    private void OnConfigPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (sender == Config)
        {
            //在这里还可以通过判断e的属性名来做不同的操作
            switch (e.PropertyName)
            {
                case nameof(GameSetting.BirthPlace):
                    // 处理 BirthPlace 属性改变的逻辑
                    break;
                case nameof(GameSetting.Test_0):
                    // 处理 Test_0 属性改变的逻辑
                    break;
                // 添加其他属性的处理逻辑
                default:
                    break;
            }
            SaveGameSetting(Config);
        }
    }
}

这个方法缺点也很明显

全自动看起来很高端 但完全不可控

假如说有一个玩家很手贱 一直在修改这些配置

那序列化就会在短时间内一直发生

可能会引起比较明显的卡顿的现象

所以最终老大哥没有采用这个方案

还是选了最简单的 把序列化暴露出来交给外面控制

当然你还可以用比较古典的SetDirty的方法

做一下标记 退出页面的时候只要带有脏标记就弹窗提示一下保存

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注