所以我正在制作游戏,它可以在二进制文件中保存用户在计算机上的进度。 User
类存储了一些内容:
Achievement
班级和InventoryItem
班级的列表。以下是User
字段:
public string Username = "";
// ID is used for local identification, as usernames can be changed.
public int ID;
public int Coins = 0;
public List<Achievement> AchievementsCompleted = new List<Achievement>();
public List<InventoryItem> Inventory = new List<InventoryItem>();
public List<string> Skins = new List<string>();
public string CurrentSkinAsset { get; set; }
Achievement
类存储int
s,bool
和string
s,它们都是可序列化的。 InventoryItem
类存储其名称(字符串)和InventoryAction,它是在使用该项时调用的委托。
这些是Achievement
类的字段:
public int ID = 0;
public string Name = "";
public bool Earned = false;
public string Description = "";
public string Image;
public AchievmentDifficulty Difficulty;
public int CoinsOnCompletion = 0;
public AchievementMethod OnCompletion;
public AchievementCriteria CompletionCriteria;
public bool Completed = false;
以下是InventoryItem
类的字段:
InventoryAction actionWhenUsed;
public string Name;
public string AssetName;
InventoryAction变量的来源位于我的XNAGame
类中。我的意思是XNAGame
类有一个名为“UseSword()”的方法或其他方法,它传递给InventoryItem类。以前,这些方法存储在Game1
类中,但Game
类继承自Game1
类,不可序列化,我无法控制它。这就是为什么我有一个XNAGame
类。
我在尝试序列化时遇到错误:“'SpriteFont'类没有标记为可序列化”,或类似的东西。好吧,我的XNAGame
类中有一个SpriteFont对象,一些快速测试表明这是问题的根源。好吧,我无法控制SpriteFont类是否可以序列化。
为什么游戏会这样做?为什么XNAGame
类中的所有字段都必须是可序列化的,当我需要的只是一些方法时?
在回答我13岁时,请记住,并且可能无法理解您正在使用的所有术语。如果您需要任何代码示例,我很乐意为您提供。提前谢谢!
编辑:我想到的一个解决方案是将InventoryAction代理存储在Dictionary
中,除了这将是一个痛苦并且不是很好的编程实践。如果这是唯一的方法,我会接受它(尽管此时我认为这是最好的解决方案)。
编辑2:这是User.Serialize方法的代码(我知道我在做什么效率低下,我应该使用数据库,等等,等等,等等。我现在正在做的事情很好,请耐心等待。):
FileStream fileStream = null;
List<User> users;
BinaryFormatter binaryFormatter = new BinaryFormatter();
try
{
if (File.Exists(FILE_PATH) && !IsFileLocked(FILE_PATH))
{
fileStream = File.Open(FILE_PATH, FileMode.Open);
users = (List<User>)binaryFormatter.Deserialize(fileStream);
}
else
{
fileStream = File.Create(FILE_PATH);
users = new List<User>();
}
for (int i = 0; i < users.Count; i++)
{
if (users[i].ID == this.ID)
{
users.Remove(users[i]);
}
}
foreach (Achievement a in AchievementsCompleted)
{
if (a.CompletionCriteria != null)
{
a.CompletionCriteria = null;
}
if (a.OnCompletion != null)
{
a.OnCompletion = null;
}
}
users.Add(this);
fileStream.Position = 0;
binaryFormatter.Serialize(fileStream, users);
答案 0 :(得分:0)
您无法按设计序列化SpriteFont
,实际上这是可能的(.XNB文件),但尚未公开。
<强>解决方案:强>
将其从序列化类中删除。
<强>备选方案:强>
如果由于某些原因你必须序列化一些字体,我想到的第一件事就是推出你自己的字体系统,例如BMFont,但这是一项艰巨的任务,因为你将会必须在你可能已经做过的其他任何地方使用它......
使用 XNA内容应用生成预定义数量的字体(即Arial / Times / Courier,大小为10/11/12等...)(无法回想起它确切名称);然后将此用户首选项存储为两个字符串。使用string.Format(...)
,您应该能够轻松地加载正确的字体。
备选方案2肯定是最简单的,推出时间不会超过几分钟。
修改强>
基本上,我没有保存委托,而是执行以下操作:
因此,虽然您最终会有更多的课程,但您可以将问题分开,并且可以保持主循环清洁且相对通用。
代码:
public static class Demo
{
public static void DemoCode()
{
// create new profile
var profile = new UserProfile
{
Name = "Bill",
Gold = 1000000,
Achievements = new List<Achievement>(new[]
{
Achievement.Warrior
}),
Inventory = new Inventory(new[]
{
new FireSpell()
})
};
// save it
using (var stream = File.Create("profile.bin"))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, profile);
}
// load it
using (var stream = File.OpenRead("profile.bin"))
{
var formatter = new BinaryFormatter();
var deserialize = formatter.Deserialize(stream);
var userProfile = (UserProfile) deserialize;
// set everything on fire :)
var fireSpell = userProfile.Inventory.Items.OfType<FireSpell>().FirstOrDefault();
if (fireSpell != null) fireSpell.Execute("whatever");
}
}
}
[Serializable]
public sealed class UserProfile
{
public string Name { get; set; }
public int Gold { get; set; }
public List<Achievement> Achievements { get; set; }
public Inventory Inventory { get; set; }
}
public enum Achievement
{
Warrior
}
[Serializable]
public sealed class Inventory : ISerializable
{
public Inventory() // for serialization
{
}
public Inventory(SerializationInfo info, StreamingContext context) // for serialization
{
var value = (string) info.GetValue("Items", typeof(string));
var strings = value.Split(';');
var items = strings.Select(s =>
{
var type = Type.GetType(s);
if (type == null) throw new ArgumentNullException(nameof(type));
var instance = Activator.CreateInstance(type);
var item = instance as InventoryItem;
return item;
}).ToArray();
Items = new List<InventoryItem>(items);
}
public Inventory(IEnumerable<InventoryItem> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
Items = new List<InventoryItem>(items);
}
public List<InventoryItem> Items { get; }
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
var strings = Items.Select(s => s.GetType().AssemblyQualifiedName).ToArray();
var value = string.Join(";", strings);
info.AddValue("Items", value);
}
#endregion
}
public abstract class InventoryItem
{
public abstract void Execute(params object[] objects);
}
public abstract class Spell : InventoryItem
{
}
public sealed class FireSpell : Spell
{
public override void Execute(params object[] objects)
{
// using 'params object[]' a simple and generic way to pass things if any, i.e.
// var world = objects[0];
// var strength = objects[1];
// now do something with these !
}
}
答案 1 :(得分:0)
好的,所以我明白了。
最好的解决方案是在XNAGame类中使用Dictionary,它存储两件事:ItemType(枚举)和InventoryAction。基本上,当我使用一个项目时,我检查它的类型,然后查找它的方法。感谢所有尝试的人,如果这个问题令人困惑,我很抱歉。