关于 roguelike 引擎的简单瓦片地图的 C# 问题

C# Issue regarding a simple tile map for roguelike engine

好的,首先,这是我第一次 post 在这里,通常我可以通过简单地搜索答案,但是,由于这有点具体,我找不到这个问题的答案。

我在编码方面有点初学者(我主要了解基础知识并且看过很多视频并阅读了很多关于 C# 的教程)并且正在尝试编写 ASCII 控制台 "game"。一段时间以来一切都进展顺利,但现在我陷入了映射部分。起初,我以为我会手动输入墙壁的所有坐标。然而,我在这里找到了一个 post 解释说,一种更面向对象的方法是像这样创建一个 Tile class。

public class Tile
{
    public int X { get; set; }
    public int Y { get; set; }
    public bool IsWall { get; set; }
}

然后通过执行此操作填充列表

private static void PopulateTiles()
    {
        for (int x = 1; x < 159; x++)
            for (int y = 3; y < 45; y++)
                Tiles.Add(new Tile { X = x, Y = y });
    }

奇怪的起始 x 和 y 值的原因是因为我将图块限制在控制台的特定部分,其余部分用于 GUI。

无论如何,除非有更好的方法(而且必须有一个,我只是不知道它可能是什么),我正在尝试仅检索该列表的一部分以更改 ISWALL 属性 将其设置为 TRUE,然后使用此

    static void DrawObstacles()
    {
        foreach (Tile tile in World.Tiles)
        {
            if (tile.IsWall)
            {
                Console.SetCursorPosition(tile.X, tile.Y);
                Console.Write("#");
            }
        }
    }

到目前为止,我尝试了几个不同的解决方案,但显然,我 运行 遇到了每个问题(如果可能的话,我还试图避免减慢程序速度,并避免出现另一个循环有 1000 多次迭代)。

    //Tried this
    private static void SetWalls()
    {
        int[] walls = new int[] { };
        walls += Tiles.GetRange(0, 7); //Fails because I cannot use tiles in an int[]... of course
    }

    //This
    List<Tile> walls = new List<Tile>();
    List<Tile> walls = Tiles.GetRange(0, 7);
    List<Tile> walls = Tiles.GetRange(19, 7; //Doesn't work either but I've no idea why here

    //And finally
    List<Tile> walls = new List<Tile>();
    walls.Add(Tiles[Tiles.GetRange(0, 7)]); //Gives me an error saying that System.Collections.Generic.List<Engine.Tile> cannot be converted to int

所以现在,我有点迷茫,因为我真的更喜欢遍历列表,而不是在二维数组中手动​​输入每个坐标...然后遍历它 "build" 墙壁。

我的问题是:是否可以仅检索列表的某些部分(如索引 0 - 7、19 - 26、29、31、35 - 46),然后遍历要使用的主要图块列表这个新的 "Index List" 用于更改这些索引处的图块的 ISWALL 属性...如果这有意义的话。

提前致谢,如果我在 post 中犯了一些错误,请随时告诉我。

很多不必要的信息,所以问题很难理解。但是,您似乎不想遍历每个游戏对象来简单地找到一些。这是您在游戏中遇到的问题,主要是碰撞。我假设您是在控制台上构建此游戏,因此您可能无法使用任何游戏引擎功能。

因此,我建议您可以根据对象的重要性或功能将对象分成列表。这样做,您只需要在启动时循环遍历每个游戏对象,然后在 运行 时间内避免耗尽处理。

例如,您可以像这样初始化所有内容:

    List<GameObject> GameObjects { get; set; }
    List<GameObject> Walls { get; set; }
    List<GameObject> Items { get; set; }

    public Map()
    {
        PopulateTiles();
        StructureCollections();
    }

    //Small processing loop
    private void DrawObstacles()
    {
        foreach(var wall in Walls)
        {
            Console.SetCursorPosition(tile.X, tile.Y);
            Console.Write("#");
        }
    }

    //Called only on startup
    private  void PopulateTiles()
    {
        for (int x = 1; x < 159; x++)
            for (int y = 3; y < 45; y++)
                GameObjects.Add(new GameObject() { X = x, Y = y });
    }

    //Called only on starup
    private void StructureCollections()
    {
        foreach(GameObject gameObj in GameObjects)
        {
            if (gameObj.IsWall)
                Walls.Add(gameObj);

            if (gameObj.IsItem)
                Items.Add(gameObj);
        }
    }

这不是一个完美的解决方案,这完全取决于您打算如何 运行 您的游戏。但我认为这是一个好的开始。

更新:如果您想找到一种更好的方式来协调游戏对象的创建,而不是全部手工制作,您可以将对象分解为更多 classes。例如:

public class GameObject
{
    public int X { get; set; }
    public int Y { get; set; }
    public bool IsWall { get; set; }

    public GameObject(int x, int y)
    {
        X = x;
        Y = y;
    }

}

public class Wall : GameObject
{
    public Wall(int x, int y) : base(x,y)
    {
        IsWall = true;
    }
}

现在在这种情况下,您只需调用一个新的 new Wall(x, y); 即可处理它作为墙运行所需的一切,而无需更多参数。

更新:由于这被接受为答案,我觉得有义务指出这个答案是针对一个小而具体的问题。如果您要拥有许多需要自己的 class 的对象,这不是最好的方法(其中 "Wall" 是它自己的 class)。如果我们要有更多的对象,这种设计可能会导致 class 爆炸。