- 111
- 62
NavMoon v0.1
(Дело было вечером когда делать было нечего)
Библиотека для MoonLoader. Строит навигационную сетку вокруг игрока в реальном времени и считает по ней путь до точки.
Использовано
Оригинальный OpenCL (MoonCL): https://www.blast.hk/threads/257597/
Оригинальный NavMesh: https://www.blast.hk/threads/228996/
Сборка вместе, sliding window, авто профили под железо и hybrid A*/RRT поверх этого моё.
Минимальный запуск:
При init() библиотека сама смотрит CPU, количество ядер, RAM и GPU через WMI и по этим данным выставляет профиль low/medium/high. Профиль задаёт три вещи: шаг сетки (grid_step, расстояние между узлами), радиус сканирования вокруг игрока (map_size_local) и как часто обновлять узлы (map_update_rate). На слабом железе сетка грубее и меньше по радиусу, на сильном плотнее и шире. Результат печатается в консоль:
Сама сетка не висит бесконечно вокруг карты, окно сканирования едет вместе с игроком. Узлы, которые остались далеко позади по направлению движения, выгружаются из памяти, а перед игроком постоянно подгружаются новые. Это sliding window, за счёт него потребление памяти не растёт с течением времени.
Для каждого узла лучом сверху вниз ищется поверхность (земля, крыша, что угодно твёрдое), проверяется уклон и запас по высоте над этой точкой (headroom), чтобы отсеять места, где персонаж физически не пролезет. Соседние узлы связываются между собой через проверку видимости (raycast с учётом стен, объектов, машин, статичных предметов), с учётом перепада высоты между ними (max_step_height). Если перепад больше, чем можно перешагнуть, но геометрия похожа на лестницу или пандус, библиотека пробует пройти по промежуточным точкам между узлами и проверяет уклон на каждом шаге отдельно, а не считает по прямой между двумя высотами.

Часть счёта (попарные дистанции между узлами и приоритет сканирования) можно скинуть на GPU. NavMoon использует движок MoonCL (компиляция OpenCL кернелов на лету, буферы, асинхронный запуск), но кернелы под свои задачи компилирует свои: batch_dist для попарной дистанции и scan_priority для приоритета сканирования узлов.
Если OpenCL недоступен, либо кернел не скомпилировался (бывает на старых драйверах), библиотека сама уходит на CPU без падения скрипта, просто без ускорения на этом конкретном месте.
Профиль можно выставить руками из кода, не дожидаясь автоопределения:
Дальше поиск пути. generate_path_hybrid принимает координаты старта и цели:
Здесь три уровня. Если старт и цель попадают в один узел или видят друг друга напрямую без препятствий, отдаётся прямая линия, без лишнего счёта. Если расстояние больше, сначала RRT* строит грубый черновой маршрут через дерево случайных точек по прогруженной сетке (быстро находит хоть какой-то путь даже в сложной геометрии), а затем на каждом отрезке этого черновика A* достраивает точный путь по узлам сетки между двумя точками RRT, с весами по высоте и повороту (height_heuristic_weight, a_star_weight в конфиге).
Пример целиком, с прогулкой по посчитанному пути:
get_stats() отдаёт таймингах на сканирование и обновление сетки, а также сколько раз реально сработал GPU:
Требует MoonLoader с LuaJIT, используется ffi. mooncl.dll лежит рядом с init.lua и подхватывается автоматически при загрузке.
Нужно всё положить в moonloader/lib
Буду рад за фидбек и каких либо улучшений
(Дело было вечером когда делать было нечего)
Библиотека для MoonLoader. Строит навигационную сетку вокруг игрока в реальном времени и считает по ней путь до точки.
Использовано
Оригинальный OpenCL (MoonCL): https://www.blast.hk/threads/257597/
Оригинальный NavMesh: https://www.blast.hk/threads/228996/
Сборка вместе, sliding window, авто профили под железо и hybrid A*/RRT поверх этого моё.
Минимальный запуск:
Lua:
local NavMoon = require("navmoon")
local nav = NavMoon.new()
nav:init()
function main()
while true do
wait(0)
nav:update_mesh()
end
end
При init() библиотека сама смотрит CPU, количество ядер, RAM и GPU через WMI и по этим данным выставляет профиль low/medium/high. Профиль задаёт три вещи: шаг сетки (grid_step, расстояние между узлами), радиус сканирования вокруг игрока (map_size_local) и как часто обновлять узлы (map_update_rate). На слабом железе сетка грубее и меньше по радиусу, на сильном плотнее и шире. Результат печатается в консоль:
Код:
[NavMoon] profile=low (auto) | CPU=... | cores=4 | RAM=8192MB | GPU=none / OpenCL unavailable
Сама сетка не висит бесконечно вокруг карты, окно сканирования едет вместе с игроком. Узлы, которые остались далеко позади по направлению движения, выгружаются из памяти, а перед игроком постоянно подгружаются новые. Это sliding window, за счёт него потребление памяти не растёт с течением времени.
Для каждого узла лучом сверху вниз ищется поверхность (земля, крыша, что угодно твёрдое), проверяется уклон и запас по высоте над этой точкой (headroom), чтобы отсеять места, где персонаж физически не пролезет. Соседние узлы связываются между собой через проверку видимости (raycast с учётом стен, объектов, машин, статичных предметов), с учётом перепада высоты между ними (max_step_height). Если перепад больше, чем можно перешагнуть, но геометрия похожа на лестницу или пандус, библиотека пробует пройти по промежуточным точкам между узлами и проверяет уклон на каждом шаге отдельно, а не считает по прямой между двумя высотами.
Код:
[NavMoon] profile=high (auto) | CPU=... | cores=8 | RAM=16384MB | GPU=NVIDIA GeForce ...

Часть счёта (попарные дистанции между узлами и приоритет сканирования) можно скинуть на GPU. NavMoon использует движок MoonCL (компиляция OpenCL кернелов на лету, буферы, асинхронный запуск), но кернелы под свои задачи компилирует свои: batch_dist для попарной дистанции и scan_priority для приоритета сканирования узлов.
Если OpenCL недоступен, либо кернел не скомпилировался (бывает на старых драйверах), библиотека сама уходит на CPU без падения скрипта, просто без ускорения на этом конкретном месте.
Профиль можно выставить руками из кода, не дожидаясь автоопределения:
Lua:
NavMoon.set_profile("high", nav)
Дальше поиск пути. generate_path_hybrid принимает координаты старта и цели:
Lua:
local path = nav:generate_path_hybrid(px, py, pz, gx, gy, gz)
-- path = { {x1,y1,z1}, {x2,y2,z2}, ... }
-- или false, "no_mesh_point", если рядом ещё нет прогруженной сетки
Здесь три уровня. Если старт и цель попадают в один узел или видят друг друга напрямую без препятствий, отдаётся прямая линия, без лишнего счёта. Если расстояние больше, сначала RRT* строит грубый черновой маршрут через дерево случайных точек по прогруженной сетке (быстро находит хоть какой-то путь даже в сложной геометрии), а затем на каждом отрезке этого черновика A* достраивает точный путь по узлам сетки между двумя точками RRT, с весами по высоте и повороту (height_heuristic_weight, a_star_weight в конфиге).
Пример целиком, с прогулкой по посчитанному пути:
Lua:
local NavMoon = require("navmoon")
local nav = NavMoon.new()
nav:init()
function main()
while true do
wait(0)
nav:update_mesh()
end
end
function run_to_point(gx, gy, gz)
local px, py, pz = getCharCoordinates(PLAYER_PED)
local path = nav:generate_path_hybrid(px, py, pz, gx, gy, gz)
if not path then return false end
for i = 1, #path do
local x, y, z = path[i][1], path[i][2], path[i][3]
while getDistanceBetweenCoords3d(px, py, pz, x, y, z) > 1.0 do
-- своя логика движения к (x, y, z)
px, py, pz = getCharCoordinates(PLAYER_PED)
wait(0)
end
end
return true
end
get_stats() отдаёт таймингах на сканирование и обновление сетки, а также сколько раз реально сработал GPU:
Lua:
local s = nav:get_stats()
print(s.scan_calls, s.scan_time, s.update_time, s.gpu_hits)
Требует MoonLoader с LuaJIT, используется ffi. mooncl.dll лежит рядом с init.lua и подхватывается автоматически при загрузке.
Нужно всё положить в moonloader/lib
Буду рад за фидбек и каких либо улучшений
Вложения
Последнее редактирование: