如何保存/加载游戏时间? (统一引擎)

问题描述 投票:0回答:1

我的游戏中有两个场景 - 主菜单和游戏。我已经使用二进制格式化程序和序列化使玩家的位置和旋转在两个场景之间保持不变。但是,我很难让游戏场景中的时间持续在两个场景(和游戏会话)之间。简单来说:

  1. 游戏开始 - 时间为“x”。
  2. 我在一段时间后保存游戏并离开/转到菜单 - 时间是“x + 已过去的时间”。
  3. 我加载游戏 - 时间是“x + 过去的时间”而不是“x”。

我粘贴了一些代码(保存和加载 pos 和 rot),以便您了解发生了什么。

public static class DataSerializer
{
    public static void SerializeData(Player player)
    {
        BinaryFormatter bf = new BinaryFormatter();
        string dataPath = Application.persistentDataPath + "/data.txt";
        FileStream stream = new FileStream(dataPath, FileMode.Create);
        PlayerData data = new PlayerData(player);

        bf.Serialize(stream, data);
        stream.Close();
    }

    public static float[] DeserializeData()
    {
        string dataPath = Application.persistentDataPath + "/data.txt";
        if (File.Exists(dataPath))
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream stream = new FileStream(dataPath, FileMode.Open);
            PlayerData data = bf.Deserialize(stream) as PlayerData;

            stream.Close();
            return data.stats;
        }
        else
        {
            Debug.LogError("File does not exist.");
            return new float[6];
        }
    }
}

[Serializable]
public class PlayerData
{
    public float[] stats;

    public PlayerData(Player player)
    {
        stats = new float[6];

        stats[0] = player.transform.position.x;
        stats[1] = player.transform.position.y;
        stats[2] = player.transform.position.z;

        stats[3] = player.transform.eulerAngles.x;
        stats[4] = player.transform.eulerAngles.y;
        stats[5] = player.transform.eulerAngles.z;
    }
}
public class Player : MonoBehaviour
{
    public GameObject player;

    void Start()
    {
        float[] loadedStats = DataSerializer.DeserializeData();

        Vector3 position = new Vector3(loadedStats[0], loadedStats[1], loadedStats[2]);
        Vector3 rotation = new Vector3(loadedStats[3], loadedStats[4], loadedStats[5]);

        player.transform.position = position;
        player.transform.rotation = Quaternion.Euler(rotation);
    }

    public void Save()
    {
        DataSerializer.SerializeData(this);
    }
}

如果您能帮助我以同样的方式保存和加载游戏时间,我将不胜感激。

谢谢!

c# unity-game-engine serialization time
1个回答
0
投票

首先为什么这么复杂

float[]
。在我看来,它只会让人难以阅读/理解并且容易出错。

我会简单地使用如下结构(如果你不喜欢它 - 一定坚持

float[7]
为什么不^^)

更新

注意:不要再使用

BinaryFormatter
→改用
JsonUtility

public static class DataSerializer
{
    private static readonly dataPath = Path.Combine(Application.persistentDataPath, "data.txt");

    public static void SerializeData(Player player)
    {
        var data = new PlayerData(player);
        var json = JsonUtility.ToJson(data);

        File.WriteAllText(dataPath, json);
    }

    public static bool DeserializeData(out PlayerData data)
    {
        data = null;

        if (!File.Exists(dataPath)) 
        {
            Debug.LogError("File does not exist.");
            return false;
            // alternatively create some default data instead
        }
        
        var json = File.ReadAllText(dataPath);
        var data = JsonUtility.FromJson<PlayerData>(json);

        return true;
    }            
}

[Serializable]
public class PlayerData
{
    public SerializableVector3 Position;
    public SerializableVector3 EulerAngles;
    public float TotalTimePlayed;

    public PlayerData(Player player)
    {
        Position = player.transform.position;

        EulerAngles = player.transform.eulerAngles;

        TotalPlayTime = player.TotalTimePlayed;
    }
}

[Serializable]
public class SerializableVector3
{
    public float x;
    public float y;
    public float z;

    public SerializableVector3(Vector3 vec)
    {
        x = vec.x;
        y = vec.y;
        z = vec.z;
    }

    public Vector3 ToVector3()
    {
        return new Vector3(x,y,z);
    }

    public static implicit operator Vector3(SerializableVector3 vec)
    {
        return vec.ToVector3();
    }

    public static implicit operator SerializableVector3(Vector3 vec)
    {
        return new SerializableVector3(vec);
    }
}

追踪总游戏时间有多种方法。

  • 存储最后一次保存

    DateTime.Now
    ,这样您就可以在保存时简单地添加当前播放的时间:

     float timePlayedThisSession = (DateTime.Now - dateTimeLastSaved).TotalSeconds;
    

    Pro:系统会处理它,并且是一次性计算

    缺点:用户可以简单地更改系统时间 -> 可能“破解”您的应用程序统计数据

  • 或者您可以简单地使用

    Time.unscaledTime
    ,无论如何这是自上次应用程序启动以来的时间。

    优点:用户不能简单地更改此值

    缺点:根据您的使用情况,您可能无法使用

    Time.unscaledTime
    ,例如您允许重置到应用程序中的最后一次保存(因为在这种情况下,
    Time.time
    只是继续计数自应用程序启动以来

  • 或者您可以完全“手动”并使用

    Time.unscaledDeltaTime
    完全自行跟踪播放时间,例如

     private float timePlayedThisSession;
    
     private void Update()
     {
         timePlayedThisSession += Time.unscaledDeltaTime;
     }
    

    专业版:完全控制+可以轻松重置或将值加载到该字段中。

    缺点:每帧调用

    Update
    方法的开销......但这很小,根本不重要! ;)

Fazit 我会选择最后一个选项,例如

public class Player : MonoBehaviour
{
    public GameObject player;
    private float totalTimePlayed;

    // Read-only access
    public float TotalTimePlayed => totalTimePlayed;

    void Start()
    {
        ResetToLastSave ();
    }

    private void Update ()
    {
        totalTimePlayed += Time.unscaledDeltaTime;
    }

    // Just adding this to show you could even simply add a method to allow your user revert to the last save
    public void ResetToLastSave()
    {
        if(!DataSerializer.DeserializeData(out var loadedStats))
        {
            Debug.LogWarning("Could not load!", this);
            return;
        }

        player.transform.position = loadedStats.Position;
        player.transform.eulerAngles = loadedStats.Rotation;
        totalTimePlayed = loadedStats.TotalTimePlayed;
    }

    public void Save()
    {
        DataSerializer.SerializeData(this);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.