我现在和一个小团队一起开展游戏项目,我们已经遇到了障碍。该游戏的一个特点是用户能够通过使用游戏内编辑器生成自己的关卡。编辑器创建一个Level对象,该对象存储关卡的长度和宽度以及Tile对象的二维数组。我们已经成功实现了相机系统,并且可以在没有太多困难的情况下一起编辑一个简单的概念级别,但是成功保存级别并在以后加载它的过程是一个难以证明的概念,我我希望你们中的一个能够提供一些指导,以降低预期的功能。
在当前状态下,当用户按下' S' key,我们的LevelManager类运行下面的SaveLevel方法:
public static void SaveLevel()
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create("example.xml", settings))
{
IntermediateSerializer.Serialize(writer, CurrentLevel, null);
}
}
将我们的级别(CurrentLevel)序列化为项目中的XML文件(在我们让这个基本设置工作后,我们会担心保存到不同的文件。)我运行程序,创建了一个小地图并保存了它,这是结果XML文件中的输出:
<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
<Asset Type="LevelEditorPrototype.Level">
<TileGrid>
<Item>
<Item Type="LevelEditorPrototype.TileFloor">
<X>0</X>
<Y>0</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFFFFFF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileBlock">
<X>0</X>
<Y>32</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FF0000FF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileVoid">
<X>0</X>
<Y>64</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFF0000</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileFloor">
<X>0</X>
<Y>96</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFFFFFF</Tint>
</Item>
</Item>
<Item>
<Item Type="LevelEditorPrototype.TileVoid">
<X>32</X>
<Y>0</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFF0000</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileFloor">
<X>32</X>
<Y>32</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFFFFFF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileBlock">
<X>32</X>
<Y>64</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FF0000FF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileVoid">
<X>32</X>
<Y>96</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFF0000</Tint>
</Item>
</Item>
<Item>
<Item Type="LevelEditorPrototype.TileBlock">
<X>64</X>
<Y>0</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FF0000FF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileVoid">
<X>64</X>
<Y>32</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFF0000</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileFloor">
<X>64</X>
<Y>64</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFFFFFF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileBlock">
<X>64</X>
<Y>96</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FF0000FF</Tint>
</Item>
</Item>
<Item>
<Item Type="LevelEditorPrototype.TileFloor">
<X>96</X>
<Y>0</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFFFFFF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileBlock">
<X>96</X>
<Y>32</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FF0000FF</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileVoid">
<X>96</X>
<Y>64</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFF0000</Tint>
</Item>
<Item Type="LevelEditorPrototype.TileFloor">
<X>96</X>
<Y>96</Y>
<Width>32</Width>
<Height>32</Height>
<Origin>0 0</Origin>
<Depth>0</Depth>
<Tint>FFFFFFFF</Tint>
</Item>
</Item>
</TileGrid>
</Asset>
</XnaContent>
所以至少我们确实有关于关卡中生成的切片的数据信息,所以这是一些东西。我们希望我们的用户能够在运行时加载已保存的级别,因此我们映射了&#39; L&#39;加载已保存的XML文件的关键,以及问题出现的位置。我们的阅读如下:
public static void LoadLevel()
{
using (FileStream stream = new FileStream("example.xml", FileMode.Open))
{
using (XmlReader reader = XmlReader.Create(stream))
{
currentLevel = IntermediateSerializer.Deserialize<Level>(reader, null);
}
}
}
当我们尝试测试该功能时,我们会收到此错误:
System.MethodAccessException was unhandled
HResult=-2146233072
Message=Attempt by method 'DynamicClass.ReflectionEmitUtils(System.Object, System.Object)' to access method 'DynamicClass.ReflectionEmitUtils(System.Object, System.Object)' failed.
我怀疑IntermediateSerializer并不能完全按照我们希望的方式工作,但我不确定如何有效地解析和存储数据。我应该在这里使用不同的设置吗?
答案 0 :(得分:1)
根据我的经验,100%确保保存和加载基于图块的关卡的最有效方法是使用BinaryWriter和BinaryReader。
我们的关卡结构与你的关系结构有很大不同;我们有很多层。每个图层都使用一个tileset,由Tile实例组成。 Tile保持其位置(Vector2D)和TileId(tileset纹理中tile的索引)。
我们将对象置于关卡中的方式是使用tilesets,在加载时将替换为真实对象。
无论如何,保存和加载数据的一种适当的通用方法是让你的类有一个可以将BinaryReader作为参数的构造函数,以及一个将自己写入BinaryWriter的方法。
像这样:public Tile(BinaryReader reader)
{
Position.X = reader.ReadFloat();
Position.Y = reader.ReadFloat();
TileId = reader.ReadInt32();
}
public void WriteToStream(BinaryWriter writer)
{
writer.Write(Position.X);
writer.Write(Position.Y);
writer.Write(TileId);
}
如果您只有一个要加载的类,则可以简单地:
用于加载:
var tiles = new List<Tile>();
var reader = new BinaryReader(File.Open("level.bin"));
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
var tile = new Tile(reader);
tiles.Add(tile);
}
reader.Close();
保存:
var tiles; //lets pretend this is the level
var writer = new BinaryWriter(File.Create("level.bin"));
foreach (var tile in tiles)
{
tile.WriteToStream(writer);
}
writer.Flush(); //IMPORTANT!!!
writer.Close();
但是,如果您的列表包含不同类型的项目,则还需要存储该类型。 一种非常通用的方法是插入:
writer.Write(tile.GetType().FullName);
在tile.WriteToStream(writer)之前;
然后在加载时你需要:
var tileType = Type.GetType(reader.ReadString()); //Read the type from the stream
var constructor = tileType.GetConstructor(new [] { typeof(BinaryReader)}); //get a constructor that can use a binaryreader
var tile = constructor.Invoke(new [] { reader }); //use said constructor to create an instance
希望这会有所帮助。请注意,我正在写这个内存,所以语法错误很可能......