reckfellENGINE

GridMap: модульный 3D-уровень и коллизии

Создать MeshLibrary, разместить и повернуть блоки, согласовать меши с физикой и безопасно сохранить библиотеку.

На этой странице

Модель данных

GridMap хранит по одному ID элемента MeshLibrary и индексу ориентации в каждой целочисленной клетке. MeshLibrary описывает блоки: меш, его трансформацию и отдельные физические формы. Это не иерархия из отдельной Entity на каждый блок.

Подходит для стен, пола и повторяющихся модулей фиксированного шага. Двери со скриптами, инвентарь и другие интерактивные объекты удобнее размещать отдельными Entity/префабами, если им нужно собственное состояние. Сама сетка не создаёт ECS, стриминг мира или навигацию из видимой картинки.

Запустить готовую сетку

Возьмите BlockoutLab3D.cs из примера CSG и BlockoutLibrary.cs ниже. Нужны C# SDK 1.0.0, net10.0 и Entity3D-корень уровня .rscene. Выполните dotnet build, назначьте BlockoutLab3D.cs корню и запустите.

Код создаёт 15 клеток пола, а затем заменяет четыре из них синими стенами. В каждой клетке остаётся один элемент: запись стены не добавляет второй слой пола в ту же клетку. Общая тёмная плоскость под примером — только визуальный фон, без физической формы.

BlockoutLibrary.cs
C#
using Reckfell;

public static class BlockoutLibrary
{
    public const int Floor = 0;
    public const int Wall = 1;

    public static StandardMaterial3D Material(Color color) => new()
    {
        AlbedoColor = color,
        ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded
    };

    public static MeshLibrary Create()
    {
        var library = new MeshLibrary();
        AddBox(library, Floor, "Floor", new Vector3(2, 0.2f, 2),
            new Vector3(0, -0.1f, 0), new Color(0.15f, 0.18f, 0.23f));
        AddBox(library, Wall, "Wall", new Vector3(2, 3, 0.4f),
            new Vector3(0, 1.5f, 0), new Color(0.08f, 0.25f, 1));
        return library;
    }

    private static void AddBox(MeshLibrary library, int id, string name,
        Vector3 size, Vector3 offset, Color color)
    {
        library.CreateItem(id);
        library.SetItemName(id, name);
        library.SetItemMesh(id, new BoxMesh { Size = size, Material = Material(color) });
        var transform = new Transform3D(Basis.Identity, offset);
        library.SetItemMeshTransform(id, transform);
        library.SetItemShapes(id, new Reckfell.Collections.Array
        {
            new BoxShape3D { Size = size }, transform
        });
    }
}

Синие модульные стены GridMap рядом с белой CSG-композицией

Кадр снят в текущем локальном runtime 1.0.0. Управление персонажем и рисование клеток мышью в редакторе в пример не входят.

Согласовать меш и коллизию

CreateItem создаёт пустую запись. SetItemMesh назначает изображаемую геометрию; SetItemShapes — отдельный массив пар [Shape3D, Transform3D, …]. Число элементов массива должно быть чётным. SetItemMeshTransform не заменяет трансформации физических форм.

В примере стена имеет размер (2,3,0.4), а её центр поднят на 1.5, чтобы нижний край лежал на Y=0. Одинаковое смещение задано мешу и форме отдельно. Пол толщиной 0.2 смещён на -0.1, поэтому верхняя поверхность находится на Y=0.

Если картинка совпадает с сеткой, а тело сталкивается «рядом», сначала сравните эти две трансформации. После изменения клеток дождитесь физического обновления: GetCellItem уже может вернуть новое значение, когда прежняя коллизия ещё не перестроена.

Различать размер и центрирование

НастройкаВ примереЧто меняет
CellSize(2,2,2)Шаг между позициями клеток
CellCenterX/ZtrueСмещение на половину шага по X/Z
CellCenterYfalseНет половинного смещения по высоте
CellScale1Масштаб размещённых мешей и физических форм
Размер BoxMeshСобственный для каждого блокаГеометрию ресурса; не меняется от CellSize

При этих настройках MapToLocal((2,3,-1)) даёт (5,6,-1). LocalToMap((-0.01,-0.01,-0.01)) даёт (-1,-1,-1), потому что выполняется округление вниз, а не отбрасывание дробной части.

Мировую точку сначала переводите Grid.ToLocal, затем передавайте LocalToMap. Обратный путь: MapToLocal, затем ToGlobal. Для попадания луча точно на границу клетки определите нужную сторону по normal и небольшому смещению; слепое округление мировой позиции часто выбирает соседнюю клетку. Эта схема преобразования проверена, готовый редактор рисования по клику здесь не реализован.

CellSize=(4,2,2) увеличит шаг по X, но не растянет исходную стену. CellScale=0.5 уменьшит меш и физическую форму, оставив прежний шаг и MapToLocal. Не пытайтесь исправить неверный масштаб модели одной настройкой сетки.

Повернуть или удалить блок

Третий аргумент SetCellItem — индекс одной из 24 ортогональных ориентаций, а не градусы. Для поворота на 90° получите Basis вокруг Vector3.Up с углом Mathf.Pi/2 и передайте его GetOrthogonalIndexFromBasis. Полученный индекс используйте в SetCellItem; для чтения есть GetCellItemOrientation и GetCellItemBasis. Проверка подтвердила поворот и меша, и физической формы.

SetCellItem(cell,-1) удаляет клетку. Отрицательный item здесь не «блок без картинки». В нативной проверке GetCellItem сразу показал -1, а после обновления физики луч перестал попадать в удалённую стену.

Попадание в физику GridMap указывает на сам GridMap, не на отдельную Entity клетки. Если нужно состояние конкретной двери или разрушаемого блока, храните его по координате клетки либо используйте отдельный префаб.

Проверять ID и сохранение

Используйте последовательные ID от 0. В текущей сборке подтверждён дефект .rdata: библиотека с элементами 0 и 7 работает в памяти, ResourceSaver возвращает Error.Ok, но данные элемента 7 после загрузки теряются. В опубликованном примере используются 0 и 1; меши, формы и смещения проходят сохранение и загрузку без кэша.

До исправления не полагайтесь на сохранение разреженных библиотек и не удаляйте исходные ассеты после одной успешной записи. При миграции ID обновляйте и ссылки клеток карты, иначе они начнут указывать на другой блок. Проверка полной .rscene с GridMap и поведение меню экспорта MeshLibrary не входят в этот тест.

Есть ещё два ограничения:

  • Неизвестный ID может записаться в клетку и вернуться через GetCellItem, но не создать меш или физику. Проверяйте наличие через GetItemList библиотеки; в C# нет метода MeshLibrary.HasItem.
  • Внутреннее поле элемента клетки — 16-битное. Допустимые неотрицательные ID для GridMap — 0..65535; значение 65536 в проверке стало 0. По исходникам абсолютные координаты клеток должны быть меньше 2^20 по каждой оси. Это не бесконечная карта.

Диагностика

СимптомЧто проверить
Клетка записана, блока нетMeshLibrary назначена, ID существует, у элемента есть Mesh
Блок виден, коллизии нетПары SetItemShapes и физические слои/маски
Стены парят или утопленыCellCenterY, смещение меша и отдельное смещение формы
Поворот не соответствует 90°Передан индекс ортогональной ориентации, а не число 90
После загрузки часть блоков пропалаРазрывы ID и фактические данные загруженной библиотеки
AI не находит путь по полуНавигационные данные и их регистрация настраиваются отдельно

GridMap API · MeshLibrary API

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.