Использование трассировки
Один из важнейших навыков для программиста МОДов для Half-Life 2 — это работа с трассировщиками (TraceLines). В данной статье описаны несколько простейших приемов работы с этими средствами.
В данной статье мы рассмотрим следующие вопросы:
1 Что такое трассировка?
2 Получение начальной и конечной точек
3 Самотрассировка
4 Анализ и использование результатов

Что такое трассировка?

Трассировка это процесс следования из точки A по направлению в точку B и нахождения первого объекта который «попался» на этом пути. Самый простой пример использования трассировщика — в оружии. Оружие использует трассировщики для определения места попадения в кого-либо и произошло ли попадение.

Линия начинается от дула оружия (точка A) и заканчиваетсяв произвольной точке на расстоянии 8000 или больше юнитов перед ней. Если она попадает в мир (стенаl, ящик, что-небудь твердое) код сообщает ему что нужно отобразить эффект (например, облако дыма исходящее из стены, или искры от металла…). Если линия попадает в игрока, она наносит повреждения.

Получение начальной и конечной точек

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

GetAbsOrigin (): Указывает на точку лежащуюю «у ног» игрока. Для многих энтитей-моделей, это также является «низом» модели, или иногда центром.

Weapon_ShootPosition (): Только для класса игрока. Это точка откуда начинается выстрел оружия. Позиция приблизительная. Если вам требуется отображать эффекты (например трассеры от пуль, видимые линии выходящие из ствола оружия) используйте функцию приведенную ниже:

GetAttachment () и LookupAttachment (): GetAttachment () возвращает позицию присоедениения (attachment point) на модели энтити. Точка аттачмента является точкой на модели, определенную моделлером в QC файле. Вы можите просмотреть список точек аттачмента для моделей при помощи утилиты HLMV. Список нумеруется с нуля. LookupAttachment () принимает в качестве аргумента char* (стрка с именем аттачмента) и возвращает номер соответствующего аттачмента, или -1 если он не найден.

Теперь, когда мы имеем «точку A» требуется определить «точку B».

Source SDK предоставляет много мощных функций для преобразования углов в векторы. Что попросту называется поворотом на «45 по вертикали, 90 по горизонтали, 0 вращения», можно представить в виде вектора напарвленного влево и вверх на 45 градусов. Функция AngleVectors (), перегруженная как принимающая в качетве параметров один QAngle и один указатель на Vector, и как QAngle и три указателя на Vector. Функция вовращает векторы: передний (forward), котрый направлен по направлению указывающему углами, верхний (up), который повернут на 90 градуссов «вверх», и правый, который повернут на 90 градусов «вправо» от переднего.

AngleVectors следует используют в сочетании с следующими функциями:

EyeAngles (): Только для игроков. Возвращает текущие углы зрения.

GetAbsAngles (): Для игроков, возвращает направление в котором направлена их модель (здесь нужно быть осторожным, обычно эти значения не синхронизируются с EyeAngles, потому что модель игрока поворачивается только в пределах увеличения на 15 или 30 градусов так что это не выглядит пугающе). Для других энтитей, это их ориентация.

Тепрь, когдамы знаем как получить точку A, и направление, теперь дело за простым математическим расчетом по существующей оси направления до точки B — «точка A + напраление * X,» где X это расстояние на которое требуется трассировка.

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

Самотрассировка

Существует два набора параметров которые возможно передавть трассировщикам, один из них TraceFilter, который не особенно часто используется, рассмотрим второй из них:

UTIL_TraceLine (const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr)

Рассмотрим агрументы этой функции:

const Vector& vecAbsStart: точка A. модификатор «const» сдесь указывает что значение не может изменяться внутри функции.

const Vector& vecAbsEnd: точка B. (также не меняется в функции).

unsigned int mask: Этот аргумент очень интересен. Это целое число (int) является битовой маской (bitmask) указывающей что требуется трассировать для попадения. Список определений для масок (MASK) находятся в файле public\bspflags.h. Наиболее часто используемыми из которых являются MASK_SHOT, MASK_SOLID_BRUSHONLY и MASK_SOLID. Вы можите также создавать ваши собственные маски, комбинируя различные CONTENTS_ флаги.

const IHandleEntity *ignore: Указатель на энтить которую следует игнорировать. Для собственной энтити можно передать указатель на CBaseEntity, это будет работать со всеми энтитями. Можно использовать указатель «this» если трассировка производится изнутри модели энтити, в таком случае трассировка приведет «на выходе» из собственной геометрии. Если в вашем случае трассировка не должна быть прервана прилегающим объектом, используйте этотм аргументе энтить игнорирования.

int collisionGroup: Этот аргумент указывает какая группа коллизии должна подвергаться трассировке, константы определены в public\const.h. Определенные группы могут попадать только в другие определнные группы. Установив COLLISION_GROUP_NONE приведет к трассировке попдения в что-либо.

trace_t *ptr: Указатель на класс трассировщика (как может показаться что это структура, но это на самом деле класс!) который должен быть заполнен. Проще говоря это «таблица результатов» получаемая в результате трассировки. Просто определите: «trace_t tr;» и передайте в эту функцию «&tr»…

Анализ и использование результатов

Отлично, теперь мы произвели трассировку, что теперь?! Как было только-что сказано выше, мы будем использовать резултаты из указателя trace_t. Теперь, это то что потребуется для получения данных о том как прошла трассировка, и если возможно, извлечь данные из него. Не будем рассматривать каждую функцию и переменную trace_t, рассмотрим только наиболее часто используемые. Детальнее можите самостоятельно рассмотреть в объявлении trace_t или CGameTrace.

startpos, endpos: startpos это точка A, endpos это то место где трассировка натолкнулась на что-то и остановилась. В нашем примере это точка, где пуля попала в стену. Нужно обратить внимание, это та точка в которой произошло попадение, а не несколько юнитов спереди или позади, так что если понадобиться породить что-либо в этом месте, нужно убедиться в наличии пространства для него.

fraction: Это вещественное значение в пределах от 0 до 1 выражающее расстояние в процентах между точкой A и B «пройденное» до момента попадания. Важно по разным причинам, главная из них это условие если tr. fraction == 1.0, то трассировка не попала ни во что, и если tr. fraction ≠ 1.0, трассировка попала во что-то.

startsolid и allsolid: startsolid это булевое значение, означающее что трасса «внутри» чего-либо (обычно мир) в месте начала (точка A), allsolid означает что точка B «внутри».

plane: Это информация о поверхности попадания. Нужно обратить внимание, если allsolid равен true, это означает что плоскость не действительна. plane содержит структуру с большим количеством информации о плоскости попадания. Например plane. normal — нормаль к поверхности плоскости попадания, которая может использоваться для разных целей, например «отражение» от точки попадания используя endpos + plane. normal*количество юнитов от плоскости.

surface: Это информация о месет попадения, включает имя поверхности, материалы (props), если это указано в материале, и наиболее вжное: флаги. Вы можите найти список флагов поверхностей в файле public\bspflags.h. Один из важнейших — SURF_SKY, который означает поверхность скайбокса. Он важен для нескольких целей, например, если вы не хотите анимировать эффекты от попадения в небо, это есть способ определить было попадение в небо или нет.

m_pEnt: О, это наиболее важно. Это энтить в которую было попадение! Обычно если это мир значение будет NULL, также NULL будет в случае если попадения не было, поэтому следует сначала проверять его перед использованием. Это может быть использовано для многих целей, но обычно наиболее важная — определение: что это за объект? CBaseEntity имеет несколько функций котрые могут помочь в этом:

IsXXXX (): IsPlayer (), IsNPC (), и т.д. Название говорит само за себя. Все эти методы определены в CBaseEntity.h и переопределены в проризводных классах.

GetClassname (): Возвращает имя энтити в которую было попадение. На стороне сервера, обычно это будет имя указанное в LINK_ENTITY_TO_CLASS (), но на клиенте, это имя будет отображаться если энтитя была предсказательной (predictable) тоесть, отображаемая на клиенте. В противном случае функция возвращает настоящее имя класса, которое выглядит например как «class C_BaseMyClass». Если нет уверенности в имени определенной энтити, можно предварительно проверить его при помощи Warning () выведя в консоль имя класса всех энтитей, которые окружают игрока (можно сделать для этого собственную консольную комманду которая выполняет это на стороне клиента) после чего далее можно производить корректные действия.

GetTeamNumber (): Это еще ондна важная функция: номер комманды (team) в которой находится энтитя. Для не-игроков, это обычно значение равное UNASSIGNED, а в других случаях если энтити связаны с игроком (оружие, гранаты, и т.д.).

Перевод: DarkLight.
15 июля 2005, 12:12