Докуметация Cтарт Статьи Форум Лента Вход
Не официальное русскоязычное сообщество

Recast Navigation для JME

Опубликованно: 06.07.2017, 13:21
Последняя редакция, Andry: 06.07.2017 15:28

Что такое Recast Navigation

Recast Navigation — это библиотека C++ для поиска путей в 3D, пространстве. Recast имеет два больших модуля:

  1. Recast
  2. Detour

Recast

Recast — это набор инструментов для создания навигационных сеток для игры.

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

Detour

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

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

jNavigation

Навигация — это портированная на Java библиотека, Recas навигацииt. jNavigation — это проект находящийся в развитии, и в настоящее время он позволяет создавать навигационные сетки и поиск путей для одного агента(бота).

Пример

В следующем коде описывается, как пользователь должен создавать навигационную сетку и запрашивать ее.

// Шаг 1. Инициализация сборка настройка.
Config config = new Config();

Mesh mesh = ((Geometry) scene.getChild("terrain")).getMesh();

Vector3f minBounds = RecastBuilder.calculateMinBounds(mesh);
Vector3f maxBounds = RecastBuilder.calculateMaxBounds(mesh);

config.setMaxBounds(maxBounds);
config.setMinBounds(minBounds);
config.setCellSize(0.3f);
config.setCellHeight(0.2f);
config.setWalkableSlopeAngle(45);
config.setWalkableClimb(1);
config.setWalkableHeight(2);
config.setWalkableRadius(2);
config.setMinRegionArea(8);
config.setMergeRegionArea(20);
config.setBorderSize(20);
config.setMaxEdgeLength(12);
config.setMaxVerticesPerPoly(6);
config.setDetailSampleMaxError(1f);
config.setDetailSampleDistance(6);

RecastBuilder.calculateGridWidth(config);
RecastBuilder.calculatesGridHeight(config);

// Шаг 2. Rasterize input polygon soup.

//Контекст необходим для ведения журнала, который еще не полностью поддерживается в родной библиотеке.
//Он НЕ должен быть нулевым.
Context context = new Context();

// Allocate voxel heightfield where we rasterize our input data to.
Heightfield heightfield = new Heightfield();
if (!RecastBuilder.createHeightfield(context, heightfield, config)) {
    System.out.println("Could not create solid heightfield");
    return;
}

// Allocate array that can hold triangle area types.

// In Recast terminology, triangles are what indices in jME is. I left this,

// Find triangles which are walkable based on their slope and rasterize them.
char[] areas = RecastBuilder.markWalkableTriangles(context, config.getWalkableSlopeAngle(), mesh);
RecastBuilder.rasterizeTriangles(context, mesh, areas, heightfield, 20);


// Шаг 3. Filter walkables surfaces.
// Once all geometry is rasterized, we do initial pass of filtering to
// remove unwanted overhangs caused by the conservative rasterization
// as well as filter spans where the character cannot possibly stand.
RecastBuilder.filterLowHangingWalkableObstacles(context, config.getWalkableClimb(), heightfield);
RecastBuilder.filterLedgeSpans(context, config, heightfield);
RecastBuilder.filterWalkableLowHeightSpans(context, config.getWalkableHeight(), heightfield);


// Шаг 4. Partition walkable surface to simple regions.
// Compact the heightfield so that it is faster to handle from now on.
// This will result more cache coherent data as well as the neighbours
// between walkable cells will be calculated.
CompactHeightfield compactHeightfield = new CompactHeightfield();

if (!RecastBuilder.buildCompactHeightfield(context, config, heightfield, compactHeightfield)) {
    System.out.println("Could not build compact data");
    return;
}

if (!RecastBuilder.erodeWalkableArea(context, config.getWalkableRadius(), compactHeightfield)) {
    System.out.println("Could not erode");
    return;
}

// Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas.
// There are 3 martitioning methods, each with some pros and cons:
// 1) Watershed partitioning
//   - the classic Recast partitioning
//   - creates the nicest tessellation
//   - usually slowest
//   - partitions the heightfield into nice regions without holes or overlaps
//   - the are some corner cases where this method creates produces holes and overlaps
//      - holes may appear when a small obstacles is close to large open area (triangulation can handle this)
//      - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail
//   * generally the best choice if you precompute the nacmesh, use this if you have large open areas
// 2) Monotone partioning
//   - fastest
//   - partitions the heightfield into regions without holes and overlaps (guaranteed)
//   - creates long thin polygons, which sometimes causes paths with detours
//   * use this if you want fast navmesh generation
String partitionType = "Sample partition watershed";

if (partitionType.equals("Sample partition watershed")) {
    if (!RecastBuilder.buildDistanceField(context, compactHeightfield)) {
        System.out.println("Could not build distance field");
        return;
    }
    if (!RecastBuilder.buildRegions(context, compactHeightfield, config)) {
        System.out.println("Could not build watershed regions");
        return;
    }
}

if (partitionType.equals("Sample partition monotone")) {
    if (!RecastBuilder.buildRegionsMonotone(context, compactHeightfield, config)) {
        System.out.println("Could not build monotone regions");
        return;
    }
}

// Шаг 5. Trace and simplify region contours.
// Create contours.
ContourSet contourSet = new ContourSet();

if (!RecastBuilder.buildContours(context, compactHeightfield, 2f, config.getMaxEdgeLength(), contourSet)) {
    System.out.println("Could not create contours");
    return;
}

// Шаг 6. Build polygons mesh from contours.
// Build polygon navmesh from the contours.
PolyMesh polyMesh = new PolyMesh();

if (!RecastBuilder.buildPolyMesh(context, contourSet, config.getMaxVertsPerPoly(), polyMesh)) {
    System.out.println("Could not triangulate contours");
    return;
}

// Шаг 7. Создайте детальную сетку, которая позволяет получить приблизительную высоту каждого полигона.
PolyMeshDetail polyMeshDetail = new PolyMeshDetail();

if (!RecastBuilder.buildPolyMeshDetail(context, polyMesh, compactHeightfield, config, polyMeshDetail)) {
    System.out.println("Could not build detail mesh.");
    return;
}

// (Необязательно) Шаг 8. Create Detour data from Recast poly mesh.
// The GUI may allow more max points per polygon than Detour can handle.
// Only build the detour navmesh if we do not exceed the limit.
if (config.getMaxVertsPerPoly() > DetourBuilder.VERTS_PER_POLYGON()) {
    return;
}
NavMeshCreateParams createParams = new NavMeshCreateParams();
createParams.getData(polyMesh);
createParams.getData(polyMeshDetail);
//setting optional off-mesh connections (in my example there are none)
createParams.getData(config);
createParams.setBuildBvTree(true);

char[] navData = DetourBuilder.createNavMeshData(createParams);

if (navData == null) {
    System.out.println("Could not build Detour navmesh.");
    return;
}

NavMesh navMesh = new NavMesh();

if (!navMesh.isAllocationSuccessful()) {
    System.out.println("Could not create Detour navmesh");
    return;
}

Status status;
status = navMesh.init(navData, TileFlags.DT_TILE_FREE_DATA.value());
if (status.isFailed()) {
    System.out.println("Could not init Detour navmesh");
    return;
}

NavMeshQuery query = new NavMeshQuery();
status = query.init(navMesh, 2048);
if (status.isFailed()) {
    System.out.println("Could not init Detour navmesh query");
    return;
}

После этого (если все будет успешным) вы можете использовать методы в запросе, который был создан для целей поиска пути.

Как получить jNavigation

Существует 2 способа получить jNavigation:

  • Как форма плагина
  • Как Developmental project

Plugin

Вы можете скачать «стабильную» версию из репозитория

Developmental project

Инструкции по загрузке и настройке:

  • Загрузите оболочку для C++ из jNavigationNative репозитория
  • Создайте загруженный проект с помощью C++ компилятора
  • Загрузить java-библиотеку из репозитория jNavigation
  • В проекте Java в классе com.jme3.ai.navigation.utils.RecastJNI.java измените URL-адрес, где находится ваша сборка C++ проекта.
static {
    // Этот URL нужно изменить
    System.load(".../jNavigationNative.dll");
}

Если есть проблема с C++ проектом, см. Ссылку.

Вопросы и предложения

  • Внести предложение и/или вопрос о jNavigation в посте на форуме
  • На вопрос о Recast (библиотека C++) задайте в Группы Google

Источник

Полезные ссылки


Переведено для jmonkeyengine.ru, оригинал
Автор перевода: Andry

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

jMonkeyEngine.ru © 2017. Все права сохранены.