【问题标题】:Don't Destroy on Load Object and GameData不要破坏加载对象和游戏数据
【发布时间】:2019-11-11 01:21:03
【问题描述】:

我想使用DontDestroyOnLoad 对象在我的 2D 游戏进行时保存我的游戏数据。

我有一个 GameData 类/脚本,它存储诸如健康、magicType 等值,一个 SaveLoad 类/脚本,它将 GameData 类中的当前值保存到 Json 和一个名为 PlayerData 的空游戏对象以上两个脚本。

目前,我正在使用按钮单击事件在一个场景中保存值。我附加到按钮的对象是PlayerData 对象,我在点击事件上使用GameData 脚本更改值。

但这仅适用于第一个场景 - 我无法附加 PlayerData 对象并存储其 GameData 值,因为编辑器中没有 PlayerData 对象。

那么我怎样才能附加这个在不同场景中的对象呢?还是我做错了?

这是我当前项目的照片。

另外,当我保存一个布尔值,然后打开我的 Json 文件时,该文件是空的。这是正常的吗?这是我的保存和加载代码:

加载:

private string _filePath;
private static DataService _instance;

public DataService Instance
{
    get { return _instance; }
}

public GameData _gameData;

private void Awake()
{
    DontDestroyOnLoad(gameObject);
}

public void LoadGameData()
{
    _filePath = Path.Combine(Application.persistentDataPath, "GameData1.json");
    string json;
    if (File.Exists(_filePath))
    {
        json = File.ReadAllText(_filePath);
        JsonUtility.FromJsonOverwrite(json, _gameData);
    }
    else
    {
        Debug.Log("File missing.");
    }
}

保存:

public void SaveGameData1()
{
    string json = JsonUtility.ToJson(_gameData);
    _filePath = Path.Combine(Application.persistentDataPath, "GameData1.json");
    if (File.Exists(_filePath))
    {
        File.Create(_filePath).Dispose();
    }
    File.WriteAllText(_filePath,json);
}

编辑:这是我的 GameData 类的代码:

[Serializable]
    public class GameData : MonoBehavior
    {
        public float Health { get; set; }
    }

【问题讨论】:

  • 根据tagging help,请不要在问题标题中添加标签。
  • 我建议使用播放器偏好而不是 json 文件来将数据保存在 Unity 中。 docs.unity3d.com/ScriptReference/PlayerPrefs.html
  • 我在网上看到 playerprefs 不是很安全,而 Json 是要走的路……
  • @Brandon PlayerPrefs 用于保存用户偏好,如音乐音量,而不是用于存储游戏进度,因为它会在每次应用更新时重置,并且对用户可见和可编辑。 Json 是一个很好的小步骤,但无论如何你应该另外使用一些加密;)
  • 可以添加GameData的代码吗?你确定这两个脚本都需要MonoBehaviour吗?

标签: c# json unity3d


【解决方案1】:

数据路径

首先,正如最近刚刚回答的here,我不会使用persistentDataPath 进行开发,而是使用streamingAssetsPath,并在第一个应用程序运行时复制文件。


单例模式

我看到你有一个单例模式的前半部分但是:你从来没有给instance赋值。我通常会在物业或Awake 中“偷懒”地做,例如

private static DataService instance;
public static DataService Instance
{
    if(instance) return instance;

    instance = FindObjectOfType<DataService>();

    if(instance) return instance;

    // If it wasnt found in the scene create it now
    instance = new GameObject("DataService", typeof (GameData)). AddComponent<DataService>();
    instance._gameData = instance.GetComponent<GameData>();

    DontDestroyOnLoad (instance.gameObject);

    return instance;
}

private void Awake ()
{
    if(instance && instance != this)
    {
        Debug.LogWarning("Another instance of DataService I already in use");
        Destroy(gameObject);
        return;
    }

    if(instance) return;

    instance = this;

    DontDestroyOnLoad (gameObject);

    if(!_gameData) _gameData = GetComponent<GameData>();
}

或静态类

实际上,根据您提供的代码,我怀疑您的两个类中的任何一个确实需要是MonoBehaviour

你可以简单地使用类似的东西

public static class DataService
{
    private const string FILE_NAME = "GameData1.json";

    public static GameData GameData = new GameData();

    public static void SaveGameData1()
    {
        var json = JsonUtility.ToJson(_gameData);
        var filePath = Path.Combine(Application.persistentDataPath, FILE_NAME);

        if (File.Exists(filePath))
        {
            File.Create(filePath);
        }

        File.WriteAllText(filePath,json);
    }

    public static void LoadGameData()
    {
        var filePath = Path.Combine(Application.persistentDataPath, FILE_NAME);
        
        if (File.Exists(filePath))
        {
            var json = File.ReadAllText(_filePath);
            JsonUtility.FromJsonOverwrite(json, GameData);
        }
        else
        {
            Debug.Log("File missing.");
        }
    }
}

[Serializable]
public class GameData
{
    public float SomeValue;

    ...
}

现在您可以从任何地方简单地调用

DataService.LoadGameData();

然后像这样访问值

DataService.GameData.SomeValue = ...;

对于按钮

我会创建一个类 DataServiceButton,它附加到 UI.Button 并将自己注册为 onClick 的侦听器,例如

[RequireComponent(typeof(UI.Button))]
public class DataServiceButton : MonoBehaviour
{
    public enum ButtonType
    {
        Save,
        Load
    }

    // This is set via the Inspector
    [SerializeField] private ButtonType type;

    // Here you can already reference the according Button component
    [SerializeField] private Button button;

    private void Awake()
    {
        if(!button) button = GetComponent<Button>();

        // Register as callback
        button.onClick.AddListener(HandleClick);
    }

    private void HandleClick()
    {
        switch(type)
        {
            case ButtonType.Load:
                DataService.LoadGameData();
                // OR IF YOU STICK TO THE SINGLETON
                //DataService.Instance.LoadGameData();
                break;
            case ButtonType.Save:
                DataService.SaveGameData();
                // OR ACCORDINGLY WITH SINGLETON
                //DataService.Instance.SaveGameData();
                break;
        }
    }
}

如果没有看到GameData类的代码,就无法回答为什么写入数据后文件为空的问题......

确保您的类型是可序列化的。

【讨论】:

  • 谢谢!如果我从 GameData 类中删除 MonoBehavior,FromJsonOverwrite 是否仍然可以接受?还是我需要更改我的代码?
  • 您应该删除 {get;set;} 并使用字段而不是属性
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 1970-01-01
  • 1970-01-01
  • 2015-04-17
  • 2015-01-24
相关资源
最近更新 更多