0

我已经和一个小组一起做一个游戏项目已经有一段时间了,我们遇到了一个障碍。该游戏的特色之一是用户能够通过使用游戏内编辑器生成自己的关卡。编辑器会创建一个 Level 对象,该对象存储关卡的长度和宽度以及 Tile 对象的二维数组。我们已经成功实现了相机系统,并且可以毫无困难地一起编辑一个简单的概念关卡,但是成功保存关卡并稍后重新加载它的过程是一个困难的概念,我希望你们中的一个人可以提供一些指导以降低预期的功能。

在当前状态下,当用户按下“S”键时,我们的 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>

所以至少我们确实有关于在关卡中生成的图块的数据信息,所以这就是一些东西。我们希望我们的用户也能够在运行时加载保存的关卡,因此我们映射了“L”键来加载保存的 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 并不能完全按照我们希望的方式工作,但我不确定如何有效地解析和存储数据。我应该在这里使用不同的设置吗?

4

1 回答 1

1

根据我的经验,100% 确定保存和加载基于磁贴的关卡的最有效方法是使用 BinaryWriter 和 BinaryReader。

不过,我们的关卡结构与你们的有很大不同;我们有很多层。每个层都使用一个tileset,并由Tile 的实例组成。Tile 保存它的位置(Vector2D)和一个 TileId(tileset-texture 中的 tile 的索引)。

我们在关卡中放置对象的方式是使用加载时被真实对象替换的图块集。

无论如何,保存和加载数据的适当通用方法是让您的类有一个可以将 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

希望这可以帮助。请注意,我是在内存不足的情况下写的,所以很可能有语法错误....

于 2013-02-19T10:15:49.207 回答