Проблема C#, связанная с простой картой тайлов для roguelike-движка

Хорошо, во-первых, это мой первый пост здесь, и обычно я могу обойтись и просто найти ответ, однако, поскольку это немного специфично, я не смог найти ответ на этот.

Я немного новичок в кодировании (в основном я знаю основы, просмотрел много видео и прочитал много руководств по С#) и пытаюсь закодировать консольную «игру» ASCII. Какое-то время все шло довольно хорошо, но теперь я застрял на картографической части. Сначала я думал, что буду вводить вручную все координаты для стен. Однако я нашел сообщение здесь, объясняющее, что более объектно-ориентированный способ сделать это - создать такой класс Tile.

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 заключается в том, что я ограничиваю плитки определенной частью консоли, а остальная часть используется для графического интерфейса.

В любом случае, если нет лучшего способа (а он должен быть, я просто не знаю, что это может быть), я пытаюсь получить только части этого списка, чтобы изменить свойство 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

Так что прямо сейчас я немного растерялся, так как я действительно предпочел бы перебирать список, а не вводить все координаты вручную в 2D-массиве... а затем перебирать его, чтобы «построить» стены.

Мой вопрос: возможно ли получить только некоторые части списка (например, индекс 0–7, 19–26, 29, 31, 35–46), а затем перебрать основной список плиток, чтобы использовать этот новый «Список индексов». ", чтобы изменить свойство ISWALL плиток по этим индексам... Если это имеет смысл.

Заранее спасибо и не стесняйтесь сообщать мне, если я допустил какие-то ошибки в посте.


person Schaezar    schedule 24.04.2017    source источник
comment
Я потерялся, читая это. В чем именно заключается ваш вопрос?   -  person Smartis    schedule 24.04.2017
comment
По сути, я хочу иметь возможность перебирать список плиток и выбирать только некоторые из них, чтобы установить для их свойства ISWALL значение TRUE. Мне нужно только выбрать определенный диапазон из них, например плитку 0–7, 19–26 и тому подобное. Если это имеет смысл   -  person Schaezar    schedule 24.04.2017
comment
Вы не хранили свои плитки в формате 80x25? имитировать вывод на экран? это сделало бы вашу жизнь проще... так как я думаю, что вы пытаетесь сделать комнаты банкоматами, верно? (Я провел слишком много времени в roguelike-играх)   -  person BugFinder    schedule 24.04.2017
comment
Я не уверен, что понимаю, что вы имеете в виду, сохраняя плитки в формате 80 * 25, но массив плиток на самом деле 158 * 41, так как я также изменил размер консоли. Это (160, 60)   -  person Schaezar    schedule 24.04.2017


Ответы (1)


Много лишней информации, поэтому вопрос трудно осмыслить. Однако, похоже, вы не хотите перебирать каждый игровой объект, чтобы просто найти несколько. Это проблема, с которой вы сталкиваетесь в играх, в основном со столкновением. Я предполагаю, что вы создаете эту игру на консоли, поэтому какие-либо функции игрового движка, вероятно, не в вашем распоряжении.

Итак, я бы посоветовал вам разбить свои объекты на списки в зависимости от их важности или функционирования. Делая это, вам нужно будет только перебрать каждый игровой объект при запуске, а затем избавить себя от утомительной обработки во время выполнения.

Например, вы можете инициализировать все так:

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

Это не идеальное решение, и все действительно зависит от того, как вы планируете запускать свою игру. Но я думаю, что это хорошее начало для идеи.

Обновление: если вы хотите найти лучший способ организовать создание ваших игровых объектов, а не создавать их все вручную, вы можете разбить свои объекты на большее количество классов. Например:

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);, который будет обрабатывать все, что ему нужно для работы в качестве стены, не требуя дополнительных параметров.

ОБНОВЛЕНИЕ: поскольку это принято как ответ, я чувствую себя обязанным указать, что этот ответ предназначен для небольшой и конкретной проблемы. Если у вас будет много объектов, которым требуются свои собственные классы, это не лучший метод (в котором «Стена» - это собственный класс). Если у нас будет намного больше объектов, этот дизайн, скорее всего, приведет к взрыву класса.

person Toskr    schedule 24.04.2017
comment
Это именно то, что я хотел бы сделать, однако я не знаю, как изменить свойство на IsWall (кроме ввода их вручную с помощью такого оператора: gameObjects.Add(new GameObject(X, Y, true) для каждой отдельной плитки, которая является стеной ... которая не будет очень сухой и в конечном итоге потребует 100 строк кодов для каждого утверждения. - person Schaezar; 24.04.2017