C# Monogame - 从文件加载关卡
C# Monogame - Load level from file
我正在构建一个 2D 平台游戏。但是制作视频的人只是展示了如何创建地图和图块 class 并从他制作的数组中加载关卡。我听说这是一种不好的做法,人们应该从文件中加载他们的关卡...我知道如何使用流 reader 加载文件,但由于那不是我的代码,我正在按照教程进行操作,但在实现它时遇到了困难.
这是我的关卡代码:
private Map map;
// This is in my LoadContent() method
map.Generate(new int[,]
{
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
{2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2},
{2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2},
{2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2},
{2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2},
{2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
}, 64);
所以我正在像这样加载图块:
// Tiles.cs class
public CollisionTiles(int i, Rectangle newRectangle)
{
texture = Content.Load<Texture2D>("Graphics/Tiles/tile_" + i);
this.Rectangle = newRectangle;
}
并将上述每一个整数添加到文件名的字符串中,并直接加载并绘制在屏幕上(如下所示:map.Draw(spriteBatch);
)。 64
int 是绘制实际图块的大小。这就是我想要避免的,必须为每个级别编写这些数组,而不是我需要一种从文件加载级别的方法。加载关卡的图块时,不应忘记 .txt 文件中的图块大小。也许让它单独留在关卡文件的第一行或最后一行?这样每个级别都可以根据需要使用不同大小的图块。
这是我的 Map.cs and Tiles.cs class。我没有将它们直接粘贴到 SO 上,这会使问题太长...
那么你会如何处理这个问题?什么样的解决方案最好?我认为加载关卡类似于这样的图块:"Levels/level_" + i
也是我的好方法,每次玩家完成关卡时游戏都会增加 int i
(我自己应该可以做到这一点) ,但我只是问你如何读取文件。我将对我的代码进行任何建议的修改,而且我认为 Split
通过 ,
调整文件内容是基于意见的,也可以使用空格
,但由于这是一个数组class 我不得不使用 ,
s。在文本文件中,我还应该删除 { }
大括号。无论如何,我感谢您的任何反馈和带有解释的代码示例会很棒!
编辑:
那么我应该像这样将图块添加到 collisionTiles
吗?
public int[,] LoadLevelData(string filename)
{
using (var streamReader = new StreamReader(filename))
{
var serializer = new JsonSerializer();
Generate((int[,])serializer.Deserialize(streamReader, typeof(int[,])), 64);
return (int[,])serializer.Deserialize(streamReader, typeof(int[,]));
}
}
加载关卡和二进制数据,我喜欢用BinaryReader
和BinaryWriter
。对于数组,我先写大小,再写数据。像这样:
void Write(string fileName, int[,] data)
{
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileName)))
{
writer.Write(data.GetLength(0));
writer.Write(data.GetLength(1));
for (int x = 0; x < data.GetLength(0); x++)
{
for (int y = 0; y < data.GetLength(1); y++)
{
writer.Write(data[x, y]);
}
}
}
}
int[,] Read(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(fileName)))
{
int width = reader.ReadInt32();
int height = reader.ReadInt32();
int[,] result = new int[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
result[x, y] = reader.ReadInt32();
}
}
return result;
}
}
So how would you approach this? What kind of solution is best?
这个问题的最佳解决方案实际上取决于您打算如何编辑您的关卡。
使用您当前的方法(在代码中存储关卡数据),您可以实际手动编辑关卡。这并不壮观,但对于小游戏来说是可以管理的。
下一个方法是将级别存储在文本文件中。前几天我 answered a similar question 关于这个。过去许多游戏都使用这种方法取得了巨大成功。
如果你想让事情变得非常简单,你可以获取你现在获得的数据并使用 JSON.NET 对其进行反序列化。很酷的是,JSON 中表示的数据看起来几乎与它在代码中出现的完全一样,并且可以用您最喜欢的文本编辑器进行编辑。
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2],
[2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2],
[2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2],
[2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2],
[2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
]
加载方法也很简单
public int[,] LoadLevelData(string filename)
{
using (var streamReader = new StreamReader(filename))
{
var serializer = new JsonSerializer();
return (int[,])serializer.Deserialize(streamReader, typeof(int[,]));
}
}
要使用该方法,您可以将结果传递给您的 Generate
方法:
// This is in my LoadContent() method
var levelData = LoadLevelData(filename);
map.Generate(levelData, 64);
如果您始终使用大小为 64 的块,那将很好地工作,但是,如果您愿意,您也可以将块大小存储在 JSON 文件中。虽然它有点复杂,但对于这个答案来说可能太多了。 JSON.NET 图书馆有 excellent documentation.
由于您使用的是 MonoGame,因此您可能想使用 TitleContainer.OpenStream
instead of a StreamReader
to keep things working on all platforms. Alternately, you could write a content reader for the MonoGame Pipeline tool 但这超出了这个问题的范围。
最后但并非最不重要的一点是,您可能要考虑使用 Tiled. There's a great Tiled map loader in my MonoGame.Extended library or a number of other existing map loaders 等第 3 方级别的编辑器软件进行选择。
无论您选择哪种方法,我的建议都是让事情简单。每种方法都有利有弊,简单的解决方案通常最容易上手。一旦掌握了基础知识,您就可以升级到复杂的方法以获得更多好处。
我正在构建一个 2D 平台游戏。但是制作视频的人只是展示了如何创建地图和图块 class 并从他制作的数组中加载关卡。我听说这是一种不好的做法,人们应该从文件中加载他们的关卡...我知道如何使用流 reader 加载文件,但由于那不是我的代码,我正在按照教程进行操作,但在实现它时遇到了困难.
这是我的关卡代码:
private Map map;
// This is in my LoadContent() method
map.Generate(new int[,]
{
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
{2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2},
{2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2},
{2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2},
{2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2},
{2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
}, 64);
所以我正在像这样加载图块:
// Tiles.cs class
public CollisionTiles(int i, Rectangle newRectangle)
{
texture = Content.Load<Texture2D>("Graphics/Tiles/tile_" + i);
this.Rectangle = newRectangle;
}
并将上述每一个整数添加到文件名的字符串中,并直接加载并绘制在屏幕上(如下所示:map.Draw(spriteBatch);
)。 64
int 是绘制实际图块的大小。这就是我想要避免的,必须为每个级别编写这些数组,而不是我需要一种从文件加载级别的方法。加载关卡的图块时,不应忘记 .txt 文件中的图块大小。也许让它单独留在关卡文件的第一行或最后一行?这样每个级别都可以根据需要使用不同大小的图块。
这是我的 Map.cs and Tiles.cs class。我没有将它们直接粘贴到 SO 上,这会使问题太长...
那么你会如何处理这个问题?什么样的解决方案最好?我认为加载关卡类似于这样的图块:"Levels/level_" + i
也是我的好方法,每次玩家完成关卡时游戏都会增加 int i
(我自己应该可以做到这一点) ,但我只是问你如何读取文件。我将对我的代码进行任何建议的修改,而且我认为 Split
通过 ,
调整文件内容是基于意见的,也可以使用空格
,但由于这是一个数组class 我不得不使用 ,
s。在文本文件中,我还应该删除 { }
大括号。无论如何,我感谢您的任何反馈和带有解释的代码示例会很棒!
编辑:
那么我应该像这样将图块添加到 collisionTiles
吗?
public int[,] LoadLevelData(string filename)
{
using (var streamReader = new StreamReader(filename))
{
var serializer = new JsonSerializer();
Generate((int[,])serializer.Deserialize(streamReader, typeof(int[,])), 64);
return (int[,])serializer.Deserialize(streamReader, typeof(int[,]));
}
}
加载关卡和二进制数据,我喜欢用BinaryReader
和BinaryWriter
。对于数组,我先写大小,再写数据。像这样:
void Write(string fileName, int[,] data)
{
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileName)))
{
writer.Write(data.GetLength(0));
writer.Write(data.GetLength(1));
for (int x = 0; x < data.GetLength(0); x++)
{
for (int y = 0; y < data.GetLength(1); y++)
{
writer.Write(data[x, y]);
}
}
}
}
int[,] Read(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(fileName)))
{
int width = reader.ReadInt32();
int height = reader.ReadInt32();
int[,] result = new int[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
result[x, y] = reader.ReadInt32();
}
}
return result;
}
}
So how would you approach this? What kind of solution is best?
这个问题的最佳解决方案实际上取决于您打算如何编辑您的关卡。
使用您当前的方法(在代码中存储关卡数据),您可以实际手动编辑关卡。这并不壮观,但对于小游戏来说是可以管理的。
下一个方法是将级别存储在文本文件中。前几天我 answered a similar question 关于这个。过去许多游戏都使用这种方法取得了巨大成功。
如果你想让事情变得非常简单,你可以获取你现在获得的数据并使用 JSON.NET 对其进行反序列化。很酷的是,JSON 中表示的数据看起来几乎与它在代码中出现的完全一样,并且可以用您最喜欢的文本编辑器进行编辑。
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2],
[2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2],
[2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2],
[2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2],
[2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
]
加载方法也很简单
public int[,] LoadLevelData(string filename)
{
using (var streamReader = new StreamReader(filename))
{
var serializer = new JsonSerializer();
return (int[,])serializer.Deserialize(streamReader, typeof(int[,]));
}
}
要使用该方法,您可以将结果传递给您的 Generate
方法:
// This is in my LoadContent() method
var levelData = LoadLevelData(filename);
map.Generate(levelData, 64);
如果您始终使用大小为 64 的块,那将很好地工作,但是,如果您愿意,您也可以将块大小存储在 JSON 文件中。虽然它有点复杂,但对于这个答案来说可能太多了。 JSON.NET 图书馆有 excellent documentation.
由于您使用的是 MonoGame,因此您可能想使用 TitleContainer.OpenStream
instead of a StreamReader
to keep things working on all platforms. Alternately, you could write a content reader for the MonoGame Pipeline tool 但这超出了这个问题的范围。
最后但并非最不重要的一点是,您可能要考虑使用 Tiled. There's a great Tiled map loader in my MonoGame.Extended library or a number of other existing map loaders 等第 3 方级别的编辑器软件进行选择。
无论您选择哪种方法,我的建议都是让事情简单。每种方法都有利有弊,简单的解决方案通常最容易上手。一旦掌握了基础知识,您就可以升级到复杂的方法以获得更多好处。