英文:
Trying to keep a total coin variable located in random device location Unity2D
问题
以下是您要翻译的部分:
从这个视频 ,我的朋友 Brackeys 教了如何使用二进制格式化器来保存和加载关键数据。我使用了他的代码来创建自己的最高分保存系统。我可以保存最长距离。请查看上面的代码:
HighScoreData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public HighScoreData(Player player){
bestDistanceCount = player.distanceCount;
}
}
HighScoreSaveSystem.cs
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class HighScoreSaveSystem
{
public static void SaveHighScore(Player player){
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/highscore.highscorefile";
FileStream stream = new FileStream(path,FileMode.Create);
HighScoreData data = new HighScoreData(player);
formatter.Serialize(stream,data);
stream.Close();
}
public static HighScoreData LoadHighScore(){
string path = Application.persistentDataPath + "/highscore.highscorefile";
if(File.Exists(path)){
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path,FileMode.Open);
HighScoreData data = formatter.Deserialize(stream) as HighScoreData;
stream.Close();
return data;
}
else{
Debug.LogError("找不到保存文件!");
return null;
}
}
}
通过在KillPlayer()方法中调用这些方法,这是可以正常工作的。但是当涉及到保存硬币时,我无法弄清楚。
我必须在这个二进制文件中创建一个变量,在玩家安装游戏时它将取0。每当玩家死亡,应该将该级别中收集到的硬币添加到我在二进制文件中保存的硬币上。但我不知道如何实现它。
我尝试过的方法:
我尝试将totalCoins变量添加到HighScoreData.cs中:
HighScoreData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public int totalCoins;
public HighScoreData(Player player){
bestDistanceCount = player.distanceCount;
totalCoins += player.coinCount;
}
}
在我的KillPlayer()方法中,我尝试将totalCoins数据存储在一个临时变量中,将当前的硬币数量添加到该临时变量中,并使用名为totalCoinRef的临时变量更新data.totalCoins。
public void KillPlayer(){
isDead = true;
HighScoreData data = HighScoreSaveSystem.LoadHighScore();
int totalCoinRef = data.totalCoins;
if(distanceCount > data.bestDistanceCount){
totalCoinRef += coinCount;
data.totalCoins = totalCoinRef;
HighScoreSaveSystem.SaveHighScore(this);
}
totalCoinRef += coinCount;
data.totalCoins = totalCoinRef;
HighScoreSaveSystem.SaveHighScore(this);
Time.timeScale = 0f;
}
结果:
这个解决方案只保留每个级别收集的硬币数量,它不保留总硬币数的总和。例如,如果我收集了5个硬币,data.TotalCoins将返回5。如果我收集了6个硬币,data.TotalCoins将返回6。我希望它返回11。
我希望这一点很清楚。非常感谢您的时间。
英文:
So I have an infinite platformer runner game where I keep values of total travelled distance and total collected coins during the game. From this video, my boy Brackeys taught how to save and load critical data by formatting the data using Binary Formatter. I used his code to create my own high score saving system. I can save highest travelled distance. Check above code:
HighScoreData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public HighScoreData(Player player){
bestDistanceCount = player.distanceCount;
}
}
HighScoreSaveSystem.cs
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class HighScoreSaveSystem
{
public static void SaveHighScore(Player player){
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/highscore.highscorefile";
FileStream stream = new FileStream(path,FileMode.Create);
HighScoreData data = new HighScoreData(player);
formatter.Serialize(stream,data);
stream.Close();
}
public static HighScoreData LoadHighScore(){
string path = Application.persistentDataPath + "/highscore.highscorefile";
if(File.Exists(path)){
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path,FileMode.Open);
HighScoreData data = formatter.Deserialize(stream) as HighScoreData;
stream.Close();
return data;
}
else{
Debug.LogError("Save file not found!");
return null;
}
}
}
And by calling these methods everytime my player die in KillPlayer() method,
public void KillPlayer(){
isDead = true;
HighScoreData data = HighScoreSaveSystem.LoadHighScore();
if(distanceCount > data.bestDistanceCount){
HighScoreSaveSystem.SaveHighScore(this);
}
Time.timeScale = 0f;
}
This is working just fine. But when it comes to saving coins, I couldn't figure it out.
I have to create a variable in this binary file which will take 0 when player installs the game. And everytime player die, coins collected in that level should be added to the one I keep in my binary file. But I don't know how to implement it.
What did I try?
I tried adding totalCoins variable to HighScoreData.cs:
HighScoreData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public int totalCoins;
public HighScoreData(Player player){
bestDistanceCount = player.distanceCount;
totalCoins += player.coinCount;
}
}
And in my KillPlayer() method, I tried to store that totalCoins data in a temp variable, add current coin count to that temp variable and update data.totalCoins with the temp variable, which named totalCoinRef.
public void KillPlayer(){
isDead = true;
HighScoreData data = HighScoreSaveSystem.LoadHighScore();
int totalCoinRef = data.totalCoins;
if(distanceCount > data.bestDistanceCount){
totalCoinRef += coinCount;
data.totalCoins = totalCoinRef;
HighScoreSaveSystem.SaveHighScore(this);
}
totalCoinRef += coinCount;
data.totalCoins = totalCoinRef;
HighScoreSaveSystem.SaveHighScore(this);
Time.timeScale = 0f;
}
Result:
This solution only keeps the count of coins collected each level. It is not keeping a sum of total coins. For example if I collect 5 coins, data.TotalCoins will return 5. If i collect 6 coins, data.TotalCoins will return 6. I need it to return 11.
I hope this is clear. Thanks a lot for your time.
答案1
得分: 0
通用建议:停止使用BinaryFormatter
!
使用其他格式,如JSON
、原始文本文件等,或者自定义二进制序列化。
在SaveHighScore
中,你执行了以下操作:
HighScoreData data = new HighScoreData(player);
完全忽略了任何先前存在的数据!
你似乎认为:
int totalCoinRef = data.totalCoins;
创建了一个引用,因此编辑totalCoinRef
也会自动编辑data.totalCoins
.. 实际上不是这样的! int
是一个值类型,因此你创建的是值的拷贝,它与data
不再有任何连接!
我通常更愿意直接传递HighScoreData
并执行以下操作(现在仅使用JSON进行演示):
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public int totalCoins;
}
private static readonly string path = Path.Combine(Application.persistentDataPath, "highscore.highscorefile");
public static void SaveHighScore(HighScoreData data)
{
var json = JsonUtility.ToJson(data, true);
File.WriteAllText(path, json);
}
public static HighScoreData LoadHighScore()
{
if(!File.Exists(path)) return new HighScoreData();
var json = File.ReadAllText(path);
return JsonUtility.FromJson<HighScoreData>(json);
}
最后,在你的玩家类中:
public void KillPlayer()
{
isDead = true;
var data = HighScoreSaveSystem.LoadHighScore();
data.totalCoins += coinCount;
data.bestDistanceCount = Mathf.Max(data.bestDistanceCount, distanceCount);
HighScoreSaveSystem.SaveHighScore(data);
Time.timeScale = 0f;
}
如果你想继续使用二进制,你还可以查看BinaryWriter
和BinaryReader
.. 在你的情况下,JSON可能有点过度,因为你只需要8
字节.. 4用于float bestDistanceCount
,4用于int totalCoins
。当然,JSON增加了相当多的开销,字符串解析总是比简单的字节转换更昂贵。
可能会看起来像:
public static void SaveHighScore(HighScoreData data)
{
using(var stream = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
{
using(var writer = new BinaryWriter(stream))
{
writer.Write(data.bestDistanceCount);
writer.Write(data.totalCoins);
}
}
}
public static HighScoreData LoadHighScore()
{
var data = new HighScoreData();
if(File.Exists(path))
{
using(var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using(var reader = new BinaryReader(stream))
{
data.bestDistanceCount = reader.ReadSingle();
data.totalCoins = reader.ReadInt32();
}
}
}
return data;
}
无论哪种方式(甚至,也许尤其是使用BinaryFormatter
),请记住用户可以完全访问持久数据,并且可以随意修改其值,你可以使用一些加密方法来增加难度.. 但要完全防止作弊,你需要一个服务器解决方案
英文:
In general STOP USING BinaryFormatter
!
Use other formats e.g. (JSON
, raw text file, etc) or a custom binary serialization.
Then in SaveHighScore
you do
HighScoreData data = new HighScoreData(player);
completely ignoring any previous existing data!
You seem to believe that
int totalCoinRef = data.totalCoins;
creates a reference so editing totalCoinRef
will also automatically edit data.totalCoins
.. it does not! int
is a Value Type and therefore you create a COPY of the value which is in no way connected to the data
anymore at all!
I would always rather directly pass in a HighScoreData
and go (will simply demo using JSON for now)
[System.Serializable]
public class HighScoreData
{
public float bestDistanceCount;
public int totalCoins;
}
and
private static readonly string path = Path.Combine(Application.persistentDataPath, "highscore.highscorefile");
public static void SaveHighScore(HighScoreData data)
{
var json = JsonUtility.ToJson(data, true);
File.WriteAllText(path, json);
}
public static HighScoreData LoadHighScore()
{
if(!File.Exists(path)) return new HighScoreData();
var json = File.ReadAllText(path);
return JsonUtility.FromJson<HighScoreData>(json);
}
And then finally in your player
public void KillPlayer()
{
isDead = true;
var data = HighScoreSaveSystem.LoadHighScore();
data.totalCoins += coinCount;
data.bestDistanceCount = Mathf.Max(data.bestDistanceCount, distanceCount);
HighScoreSaveSystem.SaveHighScore(data);
Time.timeScale = 0f;
}
If you want to stay binary you can also look into BinaryWriter
and BinaryReader
.. in your case JSON might be a bit overkill since you anyway only really need 8
bytes .. 4 for the float bestDistanceCount
and 4 for the int totalCoins
. JSON of course adds quite some overhead and string parsing is always more expensive than simple byte conversions
Might look like e.g.
public static void SaveHighScore(HighScoreData data)
{
using(var stream = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)
{
using(var writer = new BinaryWriter(stream))
{
writer.Write(data.bestDistanceCount);
writer.Write(data.totalCoins);
}
}
}
public static HighScoreData LoadHighScore()
{
var data = new HighScoreData();
if(File.Exists(path))
{
using(var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)
{
using(var reader = new BinaryReader(stream))
{
data.bestDistanceCount = reader.ReadSingle();
data.totalCoins = reader.ReadInt32();
}
}
}
return data;
}
Either way (even - and maybe especially - using BinaryFormatter
) have in mind that a user has full access to the persistent data and can just modify their values there, you can use some encryption to make it harder .. but in order to fully prevent cheating you will need a server solution
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论