TileSet: атлас, коллизии и данные тайлов
Создать два тайла, определить физику стены и переиспользовать ресурс в слоях карты.
Разделить ресурс и карту
TileSet описывает, какие тайлы доступны и что они означают: изображение, форму коллизии и пользовательские данные. TileMapLayer хранит расположение клеток, ссылающихся на этот ресурс. Изменение тайла в общем TileSet влияет на все клетки, использующие его; состояние конкретной клетки храните отдельно.
В примере два тайла размером 16×16: пол и синяя стена. Только у стены есть физический контур. Данные walkable=true/false — метаданные для вашего кода, а не автоматическое включение навигации или коллизии.
Создать полный ресурс кодом
Нужен C#-проект Reckfell.NET.Sdk 1.0.0, net10.0. Сохраните ExampleTileSet.cs ниже. Для готового запуска добавьте TileWalker.cs и TileLevel.cs из руководства карты, выполните dotnet build и прикрепите TileLevel.cs к Entity2D уровня .rscene.
Атлас 32×16 создаётся кодом: сторонняя картинка не нужна. В редакторе тот же принцип состоит из выбора текстуры, размера региона и создания тайлов; точные действия интерфейса этим примером не проверяются.
ExampleTileSet.cs
using Reckfell;
public static class ExampleTileSet
{
public const int SourceId = 0;
public static readonly Vector2I Floor = new(0, 0);
public static readonly Vector2I Wall = new(1, 0);
public static TileSet Create()
{
using var image = Image.CreateEmpty(32, 16, false, Image.Format.Rgba8);
image.Fill(new Color(0.12f, 0.14f, 0.18f));
image.FillRect(new Rect2I(1, 1, 14, 14), new Color(0.17f, 0.19f, 0.23f));
image.FillRect(new Rect2I(16, 0, 16, 16), new Color(0.2f, 0.38f, 0.9f));
image.FillRect(new Rect2I(17, 1, 14, 3), new Color(0.4f, 0.57f, 1));
var atlas = new TileSetAtlasSource
{
Texture = ImageTexture.CreateFromImage(image),
TextureRegionSize = new Vector2I(16, 16)
};
var tiles = new TileSet { TileSize = new Vector2I(16, 16) };
tiles.AddPhysicsLayer();
tiles.SetPhysicsLayerCollisionLayer(0, 1);
tiles.SetPhysicsLayerCollisionMask(0, 1);
tiles.AddCustomDataLayer();
tiles.SetCustomDataLayerName(0, "walkable");
tiles.SetCustomDataLayerType(0, Variant.Type.Bool);
tiles.AddSource(atlas, SourceId);
atlas.CreateTile(Floor);
atlas.CreateTile(Wall);
atlas.GetTileData(Floor, 0).SetCustomData("walkable", true);
var wall = atlas.GetTileData(Wall, 0);
wall.SetCustomData("walkable", false);
wall.SetCollisionPolygonsCount(0, 1);
wall.SetCollisionPolygonPoints(0, 0, new[]
{
new Vector2(-8, -8), new Vector2(8, -8),
new Vector2(8, 8), new Vector2(-8, 8)
});
return tiles;
}
}Не смешивать размеры и идентификаторы
| Значение | В примере | Смысл |
|---|---|---|
| TileSet.TileSize | (16,16) | Размер клетки карты |
| TextureRegionSize | (16,16) | Размер базового региона изображения в пикселях |
| SourceId | 0 | ID источника в TileSet |
| Floor | (0,0) | Координата тайла пола в сетке атласа |
| Wall | (1,0) | Координата стены в сетке атласа, не пиксель (16,0) |
| alternativeTile | 0 | Базовый вариант выбранного тайла |
Размер клетки и размер картинки могут различаться. Для более крупного рисунка учитывайте его origin/смещение и занимаемые регионы; не меняйте TileSize только ради масштаба текстуры. В простом примере оба размера совпадают, чтобы отделить настройку физики от позиционирования сложного арта.
CreateTile вызывается для каждого доступного региона. Одного назначения Texture недостаточно: карта должна ссылаться на реально созданный тайл источника.
Настроить физику
Сначала создаётся physics layer в TileSet. Его индекс 0 и битовая маска 1 — разные понятия. Затем для стены задаются число полигонов и вершины относительно центра тайла: от −8 до 8 по обеим осям. Контур не задаётся в пикселях всего атласа или координатах клетки уровня.
Физическое тело персонажа должно иметь совместимые collision layer/mask. Картинка стены не является коллизией; walkable=false тоже не создаёт форму. В примере стену останавливает именно полигон TileData, а не цвет или имя тайла.
Не накладывайте поверх той же стены ещё одно физическое тело без необходимости: это затрудняет диагностику дублирующихся контактов. Для проверенного движения используется CharacterBody2D в режиме Floating, без гравитации.
Добавить данные для игры
Слой пользовательских данных сначала объявляется в TileSet: имя walkable, тип Variant.Type.Bool. Затем TileData получает значение. При чтении через GetCellTileData проверяйте, что клетка существует и использует atlas-источник, прежде чем обращаться к GetCustomData.
Эти данные относятся к типу тайла. Если одной двери в клетке нужно состояние «открыта», не изменяйте общий TileData так, будто это данные только этой клетки. Храните состояние уровня отдельно либо выбирайте подходящий тайл/альтернативу с нужными свойствами.
Сохранить и расширить
TileSet можно сохранить как ресурс .rdata и переиспользовать в нескольких TileMapLayer. В нативной проверке ResourceSaver сохранил сгенерированный атлас, физический полигон и bool-данные; ResourceLoader загрузил их обратно. При ручном сохранении проверяйте Error.Ok и различайте user:// для пользовательских данных и asset:// для ресурсов проекта.
Другие задачи требуют отдельных настроек:
- альтернативы дают варианты тайла, но не самостоятельное состояние каждой клетки;
- terrain-соединения требуют настроенных terrain и правил соседства, а не только красивой картинки;
- навигационные и световые контуры отличаются от physics layer и не появляются автоматически из него;
- анимированный атлас требует определения кадров и их длительностей.
Эти расширенные сценарии не реализованы в данном двухтайловом примере. Для их точных свойств используйте TileSet, TileSetAtlasSource и TileData.