Raycasting — это процесс отправки виртуального луча в сцену для
обнаружения столкновений с объектами. В контексте ИИ он используется для
проверки видимости, определения препятствий и расчёта траектории
движения. В Ammo.js, портированной версии
Bullet Physics для JavaScript, raycasting реализуется
через класс btCollisionWorld и методы типа
rayTest.
Луч создаётся между двумя точками: источник
(from) и конечная точка (to).
Ammo.js проверяет пересечение луча с коллайдерами физического мира и
возвращает информацию о точке столкновения, нормали поверхности и
объекте.
let from = new Ammo.btVector3(x1, y1, z1);
let to = new Ammo.btVector3(x2, y2, z2);
let rayCallback = new Ammo.ClosestRayResultCallback(from, to);
physicsWorld.rayTest(from, to, rayCallback);
if(rayCallback.hasHit()) {
let hitPoint = rayCallback.get_hitPointWorld();
let hitNormal = rayCallback.get_hitNormalWorld();
console.log(hitPoint.x(), hitPoint.y(), hitPoint.z());
}
Ключевые элементы:
btVector3 — трёхмерный вектор позиции
начала и конца луча.ClosestRayResultCallback — callback,
возвращающий информацию о ближайшем пересечении.hasHit() — проверка, было ли
столкновение.get_hitPointWorld() и
get_hitNormalWorld() — координаты точки
контакта и нормали поверхности.Для ИИ часто важно не только ближайшее столкновение, но и все
возможные объекты на пути луча. Для этого используется
AllHitsRayResultCallback. Он возвращает
массивы точек столкновений, нормалей и ссылок на объекты.
let rayCallback = new Ammo.AllHitsRayResultCallback(from, to);
physicsWorld.rayTest(from, to, rayCallback);
if(rayCallback.hasHit()) {
let hitPoints = rayCallback.get_m_hitPointWorld();
let hitNormals = rayCallback.get_m_hitNormalWorld();
let collisionObjects = rayCallback.get_m_collisionObjects();
for(let i = 0; i < hitPoints.size(); i++) {
let point = hitPoints.at(i);
console.log(`Hit at: ${point.x()}, ${point.y()}, ${point.z()}`);
}
}
Использование AllHits позволяет ИИ анализировать окружающее пространство, учитывать все препятствия и выбирать оптимальный путь.
Raycasting применяется для:
Пример проверки видимости:
function canSeeTarget(aiPos, targetPos) {
let from = new Ammo.btVector3(aiPos.x, aiPos.y, aiPos.z);
let to = new Ammo.btVector3(targetPos.x, targetPos.y, targetPos.z);
let rayCallback = new Ammo.ClosestRayResultCallback(from, to);
physicsWorld.rayTest(from, to, rayCallback);
return !rayCallback.hasHit(); // true если путь свободен
}
Для больших сцен и частого использования raycasting необходимо учитывать производительность:
rayCallback.set_m_collisionFilterMask(Ammo.btBroadphaseProxy.DefaultFilter);
С помощью фильтров можно исключить нежелательные коллайдеры и ускорить расчёты.
Raycasting особенно эффективен для ИИ, моделирующего поле зрения. Создаётся конус видимости, в котором рассылаются несколько лучей. Пересечения с объектами позволяют определить, кто находится на линии sight.
Пример разбрасывания лучей по конусу:
for(let angle = -FOV/2; angle <= FOV/2; angle += step) {
let dx = Math.cos(angle) * maxDistance;
let dz = Math.sin(angle) * maxDistance;
let to = new Ammo.btVector3(aiPos.x + dx, aiPos.y, aiPos.z + dz);
let rayCallback = new Ammo.ClosestRayResultCallback(from, to);
physicsWorld.rayTest(from, to, rayCallback);
if(rayCallback.hasHit()) {
// Объект в зоне видимости
}
}
Такой метод обеспечивает реалистичное поведение ИИ без необходимости проверять все объекты в мире.
Ammo.js возвращает данные в формате Bullet:
btVector3 — точка столкновения.btCollisionObject — ссылка на
объект.Эти данные интегрируются в алгоритмы поведения, позволяя принимать решения о движении, атаках или укрытии.
Raycasting в Ammo.js сочетает точность физического движка Bullet с гибкостью JavaScript, позволяя создавать сложное, реактивное поведение ИИ, моделировать видимость и препятствия, а также оптимизировать производительность в больших сценах.