繁体   English   中英

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

[英]How to Save / Load game-time? (Unity Engine)

我的游戏中有两个场景 - 主菜单和游戏。 我已经使用二进制格式化程序和序列化制作了播放器的 position 和旋转在 2 个场景之间持续存在。 但是,我很难让游戏场景中的时间在两个场景(和游戏会话)之间持续存在。 简而言之:

  1. 游戏开始 - 时间是'x'。
  2. 一段时间后我保存游戏并离开 / go 到菜单 - 时间是“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);
    }
}

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

谢谢!

首先,为什么float[]如此复杂。 在我看来,它只会让人难以阅读/理解并且容易出错。

我会简单地使用如下结构(如果你不喜欢它 - 一定要坚持float[7]为什么不 ^^)

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);

        using(var stream = File.Open(dataPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
        {
            var bf = new BinaryFormatter();
            bf.Serialize(stream, data);
        }
    }

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

        if (!File.Exists(dataPath)) 
        {
            Debug.LogError("File does not exist.");
            return false;
        }
        
        using(var stream = File.Open(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            var bf = new BinaryFormatter();                
            data = bf.Deserialize(stream) as PlayerData;    
        } 

        return data != null; 
    }            
}

[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;

    优点:系统会处理它,这是一次计算

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

  • 或者您可以简单地使用Time.unscaledTime ,它是自上次应用程序启动以来的时间。

    优点:用户不能简单地改变这个值

    缺点:根据您的用例,您可能无法使用Time.unscaledTime如果您允许重置到应用程序中的最后一次保存(因为在这种情况下Time.time只是从应用程序启动开始继续计数)

  • 或者你可以 go 完全“手动”并使用Time.unscaledDeltaTime完全自己跟踪播放时间,例如

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

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

    缺点:每帧调用Update方法的开销……但这太小了,根本不重要; ;)

Fazit 我会用最后一个选项 go 做例如

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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM