c# Monogame -从文件加载级别
本文关键字:加载 文件 Monogame | 更新日期: 2023-09-27 18:09:11
我正在制作一款2D平台游戏。但制作视频的人只是展示了如何创建地图和平铺类,并从他制作的数组中加载关卡。我听说这是个糟糕的做法,人们应该从文件中加载关卡……我知道如何加载文件与流阅读器,但因为这不是我的代码,我是按照教程,我有困难实现它。
这是我为关卡写的代码:
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);
所以我像这样加载tile:
// 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文件中的贴图大小。也许让它单独出现在关卡文件的第一行或最后一行?这样,如果需要,每个关卡都可以有不同大小的贴图。
那么你会怎么做呢?什么样的解决方案是最好的?我认为加载水平类似于这样的瓷砖:"Levels/level_" + i
也是我很好的方式,只是让游戏增加int i
每次玩家完成一个级别(我应该能够自己做到这一点),但我只是问你将如何读取一个文件。我将对我的代码进行任何建议的修改,我也认为Split
通过,
对文件的内容进行修改是基于意见的,也可以使用空格
,但由于这是类中的数组,我不得不使用,
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[,]));
}
}
那么你会怎么做呢?什么样的解决方案是最好的?
这个问题的最佳解决方案实际上取决于你计划如何编辑你的关卡。
使用你目前的方法(将关卡数据存储在代码中),你可以手动编辑关卡。这并不引人注目,但对于小型游戏来说是可以管理的。
下一种方法是在文本文件中存储关卡。前几天我回答了一个类似的问题。在过去,许多游戏都使用了这种方法并取得了巨大的成功。
如果你想让事情变得简单,你可以把你现在得到的数据使用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]
]
load方法也很简单。
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库有很好的文档。
因为你正在使用一夫一妻制,你可能想要使用TitleContainer.OpenStream
而不是StreamReader
来保持所有平台上的工作。或者,您可以为MonoGame Pipeline工具编写内容阅读器,但这超出了本问题的范围。
最后,但并非最不重要的,你可能要考虑的是使用第三方关卡编辑器软件,如Tiled。在我的《一夫一妻》中有一个很棒的贴图加载器。扩展库或许多其他现有的映射加载器可供选择。
无论你选择什么方法,我的建议是保持简单。每种方法都有优点和缺点,简单的解决方案通常是最容易开始的。一旦你有了基本的工作,你可以升级到复杂的方法来获得额外的好处。
加载级别和二进制数据,我喜欢使用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;
}
}