Исходник [1.1.1-alpha] pathfinding / Библиотека для реализации алгоритмов поиска пути

Musaigen

abobusnik
Автор темы
Проверенный
1,634
1,428
Pathfinding - библиотека для реализации алгоритмов поиска пути в GTA:SA. Это означает, что данная библиотека стремится реализовать не только один алгоритм поиска пути, а несколько, пытаясь привести их в пригодное для использования состояние. На данный момент ведется работа над наиболее известным и простым в реализации алгоритмом А*.

Использование: Библиотека экспортирует всего одну основную функцию:
Lua:
local pathfinding = require("pathfinding")
local path = pathfinding:process([algorithm-name: string], [start: Vector], [goal: Vector], (configuration: Configuration|nil))
По аргументам:
1. Первым аргументом является название алгоритма. В данный момент, пока алгоритм один, оно может быть либо a*, либо astar (регистр не важен). Аргумент обязательный.
2-3. Вторым аргументом является вектор начальной точки и вектор конечной точки. Это не просто таблицы, а специальный тип Vector. Чтобы его получить, можно воспользоваться полем "Point", который идёт в комплекте с экспортируемой функцией. Аргументы обязательный. Примеры:
Lua:
-- Кейс 1:
local Point = pathfinding.Point

-- Кейс 2:
local Point = require("pathfinding.point")

-- Кейс 3: (Класс Point и встроенная библиотека в moonloader Vector3d по сути одинаковы, но Point немного упрощает конструирование)
local Vector3D = require("vector3d") -- moonloader/lib/vector3d

-- Использование:
local pointA = Point.new(1, 2) -- Указывать все координаты не обязательно, но порядок важен (X, Y, Z).

local pointB = Vector3D(3, 4, 5)
4. Четвертый аргумент - это конфигурация алгоритма, которая влияет на его результат. По сути, это просто таблица с некоторыми параметрами. Аргумент опциональный, библиотека подставит конфигурацию по умолчанию:
Lua:
-- Каждый параметр здесь - опционален, библиотека подставит недостающие на параметры по умолчанию.
local default_configuration = {
  -- Чем больше "шаг", тем быстрее алгоритм, меньше стоимость по ОЗУ, но путь менее точен.
  Step = 1.5,
  -- Эвристическая функция. По умолчанию это просто дистанция от одного узла к другому.
  Heuristics = function(self, node0: Node, node1: Node)
    return (node0.point - node1.point):length()
  end,
  -- Функция для проверки точки координат на валидность. Здесь можно проверить на разницу высот...
  -- Либо в принципе на то, можно ли эту точку достигнуть. Используется при создании узлов.
  Validate = function(self, point: Vector)
    return (point - getGroundZFor3dCoord(point:get())) <= 5
  end,
  -- Функция для проверки препятствий между двумя точками. Вернет true, если пусть чист и falsе, если наоборот.
  Collision = function(self, target: Vector, origin: Vector)
    return isLineOfSightClear(target, origin, ...)
  end,
  -- Функция, которая возвращает оффсеты для кординат соседних узлов. Если кратко, чем больше здесь точек вы вернете, тем больше лагов.
  -- По стандарту, здесь 20+ точек. В первую очередь это нужно для мотивации изменять алгоритм под свою ситуацию, а также для тестирования.
  -- step - это параметр Step, который самый первый.
  Neighbors = function(self, step: number)
    return {
      Point.new(...),
      -- другие точки.
    }
  end
}
Демо:
В архиве с библиотекой есть папка demo. Можете взять оттуда файлик init.lua и протестировать алгоритм прямо в игре. Имеет три команды:
1. /a - возьмёт координаты локального игрока и поставит их для точки А.
2. /b - сделает тоже самое, но для точки B.
3. /path - создаст путь для этих двух точек и отрисует, либо выведет "no path" в консоль SAMPFUNCS, либо у вас всё зависнет из-за недостатка ОЗУ (зависит от расстояния между этими двумя точками).
Предостережение: Подобные алгоритмы, как ни крути, очень затратны по ОЗУ, если не адаптировать их под конкретную ситуацию. Например: вряд ли вам требуется 20+ точек, если вы пишите бота для работы в буквально узком коридорном помещении и так далее. В общем, скорость зависит от вашего процессора и количества + качества ОЗУ.
Также не стоит пытаться построить путь с точками, у которых огромное расстояние между собой. Лучше разделить их на кучу других точек, но ближе друг к другу.
Установка: Скачать библиотеку можно с GitHub. С архива нужно переместить содержимое папки src в вашу папку moonloader/lib.
Также:
библиотека написана при помощи moonly
Референсы: Википедия, FiveTuning, тема c Blast.hk (a.k.a Терминатор от @Rei)
 

UBP

Известный
360
222
Обход краша при слишком большом расстоянии. Ты забыл вырезать у меня из кода step, тоесть разрезание пути на сегменты, а затем прохождение итерации между этими сегментами🧐

Попробуй сначала поработать с rrt, а так же применять сглаживание чтобы не было зиг загов.
Попробуй проверять на видимость между точками пути на прямую, чтобы укоротить его и тоже убрать зиг заги
 
Последнее редактирование:

Musaigen

abobusnik
Автор темы
Проверенный
1,634
1,428
V1.0.1-alpha:
Исправлено поведение, когда путь любой сложности был недостижимым из-за функции Neighbors, которая возвращала маленькое количество точек (4-6 штук)

Ты забыл вырезать у меня из кода step, тоесть разрезание пути на сегменты, а затем прохождение итерации между этими сегментами
Окей, посмотрю
а так же применять сглаживание чтобы не было зиг загов.
Сглаживание присутствует, зигзаги из-за алгоритма. Есть другие алгоритмы, вроде theta*, которые такой проблемы не имеют, насколько я знаю. (Хоть это и не проблема, т. к алгоритм отрабатавывает по самым дешевым и эффективным путям как раз таки).
Попробуй проверять на видимость между точками пути на прямую, чтобы укоротить его и тоже убрать зиг заги
Здесь не понял. При создании узла соседа, на его точку сразу же применяется Validate и Collision.
 

UBP

Известный
360
222
V1.0.1-alpha:
Исправлено поведение, когда путь любой сложности был недостижимым из-за функции Neighbors, которая возвращала маленькое количество точек (4-6 штук)


Окей, посмотрю

Сглаживание присутствует, зигзаги из-за алгоритма. Есть другие алгоритмы, вроде theta*, которые такой проблемы не имеют, насколько я знаю. (Хоть это и не проблема, т. к алгоритм отрабатавывает по самым дешевым и эффективным путям как раз таки).

Здесь не понял. При создании узла соседа, на его точку сразу же применяется Validate и Collision.
Я имею ввиду постобработку, уже на построенном пути сверять точки на прямую видимость чтобы убрать зиг заги.

Ррт такую проблему не имеет.

Вообще есть hierarchical path find, и можно комбинировать. Сначала его, потом ррт и астар
 

Musaigen

abobusnik
Автор темы
Проверенный
1,634
1,428
Я уже переписывал его для теста, взяв за основу FiveTuning, однако он не работал, как бы я не старался. Ну и ещё в FiveTuning в принципе команда /autopilot не работает. Выплюну сюда файл.

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

Я понял про что ты, подобное есть в theta*, чуть позже его реализую наверное

V1.1.1-alpha:
1. Добавлена функция "ReachedEnd" в класс конфигурации, которая позволяет определить, достигла ли точка конца пути. Первый аргумент это всегда конечная точка пути, второй аргумент это новая точка, взятая из дерева. По умолчанию, её реализация это config:Heuristics(end_point, point) <= config.Step
2. Функция `Heuristics` теперь принимает векторы, а не ноды, как это было раньше.
3. Убрана неиспользуемая функция `clamp` из утилит, также оттуда убрана функция `smooth_path`, так как любила отгрызать пути, количество точек в котором не кратно 4, также это требуется для совместимости с кастомными функциями `ReachedEnd`.

Небольшой пример, для чего может использоваться новая функция:
Lua:
local path = pathfinding:process("a*", point_a, point_b, {
    ReachedEnd = function(self, end_point, point)
        -- Теперь если end_point находится в прямой видимости point, поиск пути сразу же завершится.
        return isLineOfSightClear(end_point, point, ...)
    end
})
 

Вложения

  • rrt.lua
    4.4 KB · Просмотры: 4
Последнее редактирование:

UBP

Известный
360
222
Я уже переписывал его для теста, взяв за основу FiveTuning, однако он не работал, как бы я не старался. Ну и ещё в FiveTuning в принципе команда /autopilot не работает. Выплюну сюда файл.


По такой логике они и будут видны, так что постобработка ничего особо не даст.

Я понял про что ты, подобное есть в theta*, чуть позже его реализую наверное

V1.1.1-alpha:
1. Добавлена функция "ReachedEnd" в класс конфигурации, которая позволяет определить, достигла ли точка конца пути. Первый аргумент это всегда конечная точка пути, второй аргумент это новая точка, взятая из дерева. По умолчанию, её реализация это config:Heuristics(end_point, point) <= config.Step
2. Функция `Heuristics` теперь принимает векторы, а не ноды, как это было раньше.
3. Убрана неиспользуемая функция `clamp` из утилит, также оттуда убрана функция `smooth_path`, так как любила отгрызать пути, количество точек в котором не кратно 4, также это требуется для совместимости с кастомными функциями `ReachedEnd`.

Небольшой пример, для чего может использоваться новая функция:
Lua:
local path = pathfinding:process("a*", point_a, point_b, {
    ReachedEnd = function(self, end_point, point)
        -- Теперь если end_point находится в прямой видимости point, поиск пути сразу же завершится.
        return isLineOfSightClear(end_point, point, ...)
    end
})
Потому что в библиотеке fivetuning нужно правильно все склеить в одну функцию buildpath. Включая некоторые улучшения и несколько методов

Я уже переписывал его для теста, взяв за основу FiveTuning, однако он не работал, как бы я не старался. Ну и ещё в FiveTuning в принципе команда /autopilot не работает. Выплюну сюда файл.


По такой логике они и будут видны, так что постобработка ничего особо не даст.

Я понял про что ты, подобное есть в theta*, чуть позже его реализую наверное

V1.1.1-alpha:
1. Добавлена функция "ReachedEnd" в класс конфигурации, которая позволяет определить, достигла ли точка конца пути. Первый аргумент это всегда конечная точка пути, второй аргумент это новая точка, взятая из дерева. По умолчанию, её реализация это config:Heuristics(end_point, point) <= config.Step
2. Функция `Heuristics` теперь принимает векторы, а не ноды, как это было раньше.
3. Убрана неиспользуемая функция `clamp` из утилит, также оттуда убрана функция `smooth_path`, так как любила отгрызать пути, количество точек в котором не кратно 4, также это требуется для совместимости с кастомными функциями `ReachedEnd`.

Небольшой пример, для чего может использоваться новая функция:
Lua:
local path = pathfinding:process("a*", point_a, point_b, {
    ReachedEnd = function(self, end_point, point)
        -- Теперь если end_point находится в прямой видимости point, поиск пути сразу же завершится.
        return isLineOfSightClear(end_point, point, ...)
    end
})
Ты не понял. Сверять точки в сегментах пути на предмет прямой видимости иначе перестраивать путь.

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

Musaigen

abobusnik
Автор темы
Проверенный
1,634
1,428
Потому что в библиотеке fivetuning нужно правильно все склеить в одну функцию buildpath. Включая некоторые улучшения и несколько методов


Ты не понял. Сверять точки в сегментах пути на предмет прямой видимости иначе перестраивать путь.

В постобработке использовать эту же функцию, для очищения от зиг загов. Допустим весь путь из зиг загов, брать точки из середины и проверять прямую видимость, включая весь путь от точки а до точки б
Это реализовано уже в другом алгоритме theta*, а* не предполагает таких выкрутасов, потому все останется как есть.

Как только допилю основные моменты в а* реализуем и theta*, зигзаги на прямой дороге пофикшены и остались только на поворотах, как и имеет место быть.
 

UBP

Известный
360
222
Это реализовано уже в другом алгоритме theta*, а* не предполагает таких выкрутасов, потому все останется как есть.

Как только допилю основные моменты в а* реализуем и theta*, зигзаги на прямой дороге пофикшены и остались только на поворотах, как и имеет место быть.
Я уверен, ты найдёшь откуда взять рабочий код
 

Musaigen

abobusnik
Автор темы
Проверенный
1,634
1,428
Я уверен, ты найдёшь откуда взять рабочий код
В википедии давно лежит псевдокод, да и такое чувство, будто ты сам своими нейронными связями придумывал тот же А*, который был написан в прошлом веке; или же читал научную статью, где и кода почти нет.
 

chromiusj

$TheWorldIsFvcked
Модератор
5,700
4,009
Без имени-1.png