Алгоритм автоматической укладки тайлов на двухмерной тайловой карте

Введение

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

Вход

алгоритм дает сгенерированную карту с правильно соединенными плитками следующим образом

Выход

Алгоритм

Общая идея алгоритма состоит в том, чтобы создать отдельную битовую маску, которая указывает, в каких направлениях уже доступны соседи одного и того же типа.

Настраивать

В образце изображения тайл в центре имеет значение, основанное на его соседних тайлах (и углах при определенных условиях, описанных ниже). Поскольку соседние плитки в N, NE и E одного типа, общая стоимость плитки составляет 2 + 4 + 16.

Углы влияют на ценность плитки только в том случае, если их основные соседи, соединяющие углы с центром, также одного вида. Следовательно, плитки SE и SW не будут учитываться для вычисления значения плитки независимо от их типа, в отличие от плитки NE.

Сопоставление смежностей с тайлами визуализируется в моем случае использования следующим образом:

Картография

Выполнение

Используя этот алгоритм, можно получить 48 различных конфигураций и сопоставлений значения тайла с фактическим тайлом / спрайтом из набора тайлов. Я очистил некоторые части своего кода и хотел бы получить дополнительную информацию о том, что можно улучшить. Основная проблема заключалась в том, чтобы вычислить индекс атласа, чтобы вывести координаты в атласе через std::unordered_map.

Все несвязанные данные и методы были удалены из кода в целях обзора:

class TileMap : public sf::Drawable, public sf::Transformable {
public:
  auto loadFromString(const std::string &String, const sf::Vector2<uint16> Size)
      -> bool {
    Data = String;
    Width = Size.x;
    Height = Size.y;

    // ... snip ...

    for (auto Column = 0; Column < Width; ++Column) {
      for (auto Row = 0; Row < Height; ++Row) {
        auto Tile = Data[Column + Row * Width];

        // ... snip ...

        auto Index = atlasIndexAt(Column, Row, Tile);
        auto AtlasPosition = Atlas[Index];
        
        // ... snip ...
      }
    }

    return true;
  }

private:
  auto atlasIndexAt(const uint16 Column, const uint16 Row, const char Tile)
      -> uint8 {
    auto T = tileValueAt(Column, Row - 1, Tile);
    auto L = tileValueAt(Column - 1, Row, Tile);
    auto R = tileValueAt(Column + 1, Row, Tile);
    auto B = tileValueAt(Column, Row + 1, Tile);
    // Corners are only relevant if both cardinals connecting the corner to the
    // center tile have positive value.
    auto TL = (T == 0 || L == 0) ? 0 : tileValueAt(Column - 1, Row - 1, Tile);
    auto TR = (T == 0 || R == 0) ? 0 : tileValueAt(Column + 1, Row - 1, Tile);
    auto BL = (B == 0 || L == 0) ? 0 : tileValueAt(Column - 1, Row + 1, Tile);
    auto BR = (B == 0 || R == 0) ? 0 : tileValueAt(Column + 1, Row + 1, Tile);
    return TL * 1 + T * 2 + TR * 4 + L * 8 + R * 16 + BL * 32 + B * 64 +
           BR * 128;
  }

  auto tileValueAt(const uint16 Column, const uint16 Row, const char Tile)
      -> uint8 {
    if (Column >= Width || Row >= Height ||
        Column == std::numeric_limits<uint16>::max() ||
        Row == std::numeric_limits<uint16>::max()) {
      return 1;
    }
    return Data[Column + Row * Width] == Tile ? 1 : 0;
  }

  // ... snip ...

  uint16 Width;
  uint16 Height;
  std::string Data;
  std::unordered_map<uint8, sf::Vector2<uint8>> Atlas{
      {208, sf::Vector2<uint8>(0, 0)},
      {248, sf::Vector2<uint8>(1, 0)},
      {104, sf::Vector2<uint8>(2, 0)},
      // ... snip ...
  };
};

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *