Лучевое трассирование (Raycasting) — это технология, используемая в компьютерной [[Graphics (Графика)|графике]] и игровой разработке для определения того, какой объект или поверхность "встречается" с лучом, отправленным из определенной точки в определенном направлении. Это метод часто используется для различных задач: - Проверка видимости объектов - Поиск пути (pathfinding) - Выбор объектов мышью или прикосновением - Физика проектайлов и стрельба - Обнаружение препятствий - Планирование траектории движения - Анализ видеопотока для обнаружения движения или определенных объектов ### Принцип работы 1. **Отправка луча**: Луч отправляется из начальной точки (`Origin`) в определенном направлении (`Direction`). 2. **Проверка пересечения**: Математический алгоритм вычисляет пересечение луча с объектами или поверхностями в сцене. 3. **Анализ данных**: После того, как найдены все пересечения, они обычно сортируются по удаленности от начальной точки. Вы можете получить первое пересечение или же все пересечения в зависимости от вашей задачи. ### Производительность 1. **Оптимизация количества лучей**: Чем меньше лучей вы используете, тем лучше. Лучи — это вычислительно затратная операция. 2. **Ограничение дистанции**: Укажите максимальное расстояние для луча, чтобы уменьшить количество возможных пересечений для проверки. 3. **Layer Masking (Маскирование слоёв)**: Используйте маски слоёв, чтобы лучи проверяли только определенные объекты, исключая другие. 4. **Избегайте постоянных проверок**: Не используйте Raycasting в `Update()` без необходимости, так как это может снизить производительность. ### Применение в Unity В Unity это можно сделать с помощью метода `Physics.Raycast` для 3D или `Physics2D.Raycast` для 2D. ```csharp RaycastHit hit; if (Physics.Raycast(transform.position, transform.forward, out hit, 100f)) { Debug.Log("Мы столкнулись с " + hit.collider.name); } ``` ### Написание аналога `Physics.Raycast` на чистом C\# Для создания аналога кода из Unity на чистом C#, вы можете определить структуру `RaycastHit` и использовать метод, который возвращает эту структуру при успешной трассировке луча. Предположим, нам нужно проверить, стоит ли перед юнитом любой другой юнит на расстоянии до 100 единиц. ##### Производительность 1. **Цикл по юнитам**: Эффективность этого метода зависит от количества юнитов. Операционная сложность будет O(n), где n — число юнитов. 2. **Дополнительные оптимизации**: Для большого количества юнитов, использование структур данных типа октодерева (Octree) может быть полезным. #### Код на чистом C\# ###### Классы и структуры ```csharp using System; using System.Collections.Generic; public struct Vector3 { public float x, y, z; // ... операторы и методы для работы с векторами public static float Dot(Vector3 a, Vector3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } } public struct Ray { public Vector3 Origin; public Vector3 Direction; // ... Конструкторы и методы } public struct RaycastHit { public string ColliderName; public float Distance; } public class Unit { public Vector3 Position; public float Radius; public string Name; } ``` ###### Метод для трассировки луча ```csharp public bool Raycast(Ray ray, List<Unit> units, out RaycastHit hitInfo, float maxDistance) { float closestT = maxDistance; bool hasHit = false; hitInfo = new RaycastHit(); foreach (Unit unit in units) { if (IsUnitInFront(ray, unit, out float t)) { if (t < closestT) { closestT = t; hitInfo.ColliderName = unit.Name; hitInfo.Distance = t; hasHit = true; } } } return hasHit; } // Параметр `t` здесь выступает как коэффициент, который показывает, на каком // расстоянии вдоль направления луча (`Direction`) произошло пересечение с // юнитом. public bool IsUnitInFront(Ray ray, Unit targetUnit, out float t) { Vector3 oc = ray.Origin - targetUnit.Position; float a = Vector3.Dot(ray.Direction, ray.Direction); float b = 2.0f * Vector3.Dot(oc, ray.Direction); float c = Vector3.Dot(oc, oc) - targetUnit.Radius * targetUnit.Radius; float discriminant = b * b - 4 * a * c; if (discriminant < 0) { t = 0; return false; } else { t = (-b - (float)Math.Sqrt(discriminant)) / (2.0f * a); return true; } } ``` ###### Пример использования ```csharp List<Unit> allUnits = new List<Unit> { new Unit { Position = new Vector3 { x = 0, y = 0, z = 5 }, Radius = 1, Name = "Unit1" }, new Unit { Position = new Vector3 { x = 0, y = 0, z = 10 }, Radius = 1, Name = "Unit2" } }; Ray ray = new Ray { Origin = new Vector3 { x = 0, y = 0, z = 0 }, Direction = new Vector3 { x = 0, y = 0, z = 1 } }; if (Raycast(ray, allUnits, out RaycastHit hitInfo, 100f)) { Console.WriteLine("Мы столкнулись с " + hitInfo.ColliderName); } else { Console.WriteLine("Столкновений нет."); } ```