Введение
Я реализовал алгоритм, который применяет автоматическое разбиение к двумерной тайловой карте для игры, выбирая правильные тайлы в соответствии с их окрестностями. Учитывая строковое представление в классической форме 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 ...
};
};