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文件中的贴图大小。也许让它单独出现在关卡文件的第一行或最后一行?这样,如果需要,每个关卡都可以有不同大小的贴图。

这是我的Map.cs和Tiles.cs类。我没有把它们直接贴上去,这样会使问题太长……

那么你会怎么做呢?什么样的解决方案是最好的?我认为加载水平类似于这样的瓷砖:"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[,]));
            }
        }

c# Monogame -从文件加载级别

那么你会怎么做呢?什么样的解决方案是最好的?

这个问题的最佳解决方案实际上取决于你计划如何编辑你的关卡。

使用你目前的方法(将关卡数据存储在代码中),你可以手动编辑关卡。这并不引人注目,但对于小型游戏来说是可以管理的。

下一种方法是在文本文件中存储关卡。前几天我回答了一个类似的问题。在过去,许多游戏都使用了这种方法并取得了巨大的成功。

如果你想让事情变得简单,你可以把你现在得到的数据使用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。在我的《一夫一妻》中有一个很棒的贴图加载器。扩展库或许多其他现有的映射加载器可供选择。

无论你选择什么方法,我的建议是保持简单。每种方法都有优点和缺点,简单的解决方案通常是最容易开始的。一旦你有了基本的工作,你可以升级到复杂的方法来获得额外的好处。

加载级别和二进制数据,我喜欢使用BinaryReaderBinaryWriter。对于数组,我先写大小,然后写数据。这样的:

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;
    }
}