Источник: http://digspt.ru/quake/create/effects

Эффекты и трюки

Тень на полу с использованием неба
Тень без использования неба
Свет из-за окна
Взрывающаяся стена
Подмена звуков
Висящие предметы и регенерация предметов
Последовательность событий
Последовательность событий 2 (гуманная)
Триггер для сущностей



Тень на полу с использованием неба

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

Пример: example1.rar (bsp,map)


Тень без использования неба

     Создаем комнату. Дублируем стену, на которую будем наносить тень, и сдвигаем дубль чуть ближе к центру комнаты. Теперь перед дублем создаем рисунок небольшой толщины (буквально юнит-два) и вплотную к нему. Напротив стены ставим источник.
    Далее превращаем стену-дубль в train и в нижней части устанавливаем path_corner таким образом, чтобы стена-дубль сдвинулась совсем немного в сторону источника света, но достаточную для того, чтобы узор, от которого отбрасывается тень утонул в этой стене.
    В итоге рисунок полностью уйдет в стену, но тень от него останется. С учетом того, что стена не сильно изменит свое положение, все динамические источники света будут отображаться на ней корректно. По крайней мере сдвиг для глаз заметен не будет.
    Ну, и не забываем для path_corner установить target_name=cor1, target=cor1 и wait=-1. Для стены target=cor1.

Пример: example2.rar (bsp,map)


Свет из-за окна

     Создаем комнату, в которой делаем мозаичное окно. Окно немного утопает в стену. За окном делаем длинную нишу, в дальнюю верхнюю точку которой помещаем сильный источник света. Теперь если отодвинуть окно в сторону, источник должен давать четкий освещенный прямоугольник на полу возле окна.
    Следующим этапом создаем маленькую комнату над исходной, которая равна (или чуть больше) по высоте окну. Окно помещаем туда и, на сторону, противоположную первому источнику света, ставим другой источник, который осветит окно.
    Теперь в правом нижнем углу оконного проема устанавливаем path_corner. Устанавливаем свойство targetname=cor1, а также target=cor1, чтобы данная точка была как началом пути, так и ее следующим элементом. Свойство wait устанавливаем -1, чтобы не происходило движение "поезда". И далее назначаем окну функцию train, с установленным свойством target=cor1.
    При старте уровня будет вызвана функция func_train, которая поставит окно на место.
    В итоге мы получаем правильное освещение как будто из-за окна, а также ярко освещенное окно.

Пример: example3.rar (bsp,map)

Ps. Вот еще пара модификаций данного трюка:
    1. Можно не создавать верхнюю комнату, а сдвигать окно в первой нише до тех пор, пока оно не окажется за источником света и не будет правильно освещена. В случае надобности нужно источник подвинуть чуть ближе к оконному проему.
    2. Верхнюю комнату использовать, а вот вместо основной ниши, натянуть за окном текстуру неба и использовать какой-нибудь компилятор, который учитывает свет, идущий от текстур неба (имитация лучей солнца) (например, tyr-lite).


Взрывающаяся стена

     Создаем следующие элементы:
    1. Стену, которую собираемся взрывать. Назначаем ей функцию wall, и задаем параметр targetname=wall_cr.
    2. Once таким образом, чтобы закрыть со всех сторон стену. Устанавливаем переменные target=crack и health=1.
    3. Внутрь стены помещаем notnull c параметрами targetname=crack и use=barrel_explode
    4. Где-нибудь добавляем relay с параметрами: killtarget=wall_cr, delay=0.2 и targetname=crack.
    Теперь, как это все работает. Назначив в once параметру health значение 1, мы тем самым заставляем его реагировать на выстрел. При выстреле notnull получает событие и вызывает функцию barrel_explode, которая изначально вызывается в момент взрыва explobox. После взрыва notnull уничтожается.
    Relay также получает это сообщение и с задержкой 0.2 секунды удаляет стену (ссылка на стену в параметре killtarget). 0.2 секунды нужны для того, чтобы notnull взорвался внутри стены и, взрывная волна не вышла наружу. Если сделать то же самое без задержки, то при взрыве игрок может получить до 100 единиц урона. Если же ситуация предусматривает урон, то relay можно вообще не создавать, а указать в once параметр killtarget=wall_cr.

Пример: example4.rar (bsp,map)


Подмена звуков
    Суть трюка: заставить некоторый элемент воспроизводить звук ему не присущий. Чтобы это сделать нужно немного разбираться в QuakeC-коде (в смысле, разбираться как и что работает).
    Проще будет объяснить на примере. Открываем исходный текст функции func_button и смотрим, как анализируется переменная sounds. Как видно, для кнопок, после проверки кода звука, происходит два действия: прекеширование звукового файла и присвоение в переменную noise имени звукового файла. Эта переменная будет использоваться для проигрывания звука. Наша задача самим прокешировать звук и установить значение переменной noise.
    Для кеширования используем такую хитрость: нужно на карту добавить сущность, которая этот звук использует. При этом, в случае надобности, у этой сущности нужно настроить переменную sounds (или что-то другое), чтобы прокешировался нужный нам звук. Если нужный элемент с нужным звуком есть на карте, то можно ничего не предпринимать. В противном случае, такой элемент лучше всего удалить после старта с карты. К примеру, можно поместить в точке появления игрока триггер once и установить ему свойство killtarget.
    C указанием звука проще: нужно присвоить переменной sounds значение, которое не обрабатывается при создании элемента. К примеру, для кнопки sounds=5. В этом случае не происходит присвоения переменной noise и в ней остается значение, которое мы задали в редакторе карт.

Пример: example5.rar (bsp,map)


Висящие предметы и регенерация предметов

     Такие элементы как аптечки, артефакты, патроны и прочее при своем создании с небольшой задержкой вызывают процедуру, которая "роняет" их на пол. Так как процедура вызывается только один раз, этим можно воспользоваться, чтобы заставить висеть элемент на нужной высоте. Для этого можно подложить под элемент стенку и затем ее удалить с карты. Для этого достаточно поместить начальную точку игрока в триггер once в котором в свойстве killtarget указать в качестве удаляемого элемента ту самую стенку. Возможно, есть значение, в какой последовательности поставили триггер и стенку (сам не проверял). Если возникнут проблемы (элемент будет все же падать), то можно в once поставить небольшую задержку.
    Регенерация. При игре в depthmatch элементы восстанавливаются через некоторое время, после их съедания. Такую же вещь можно реализовать и в single player. Принцип регенерации состоит в том, что когда мы подбираем, к примеру, аптечку, она не разрушается как объект, а просто принимает состояние невидимости и неосязаемости. В момент регенерации ее видимость и осязаемость восстанавливаются. Хорошим для нас становится факт, что в single players элементы также не разрушаются, а еще то, что есть процедура, которая выполняет регенерацию. Чтобы запустить эту процедуру присвоим переменной use элемента имя этой функции: SUB_regen. Теперь при получении события, элемент будет восстановлен. Чтобы элемент появлялся через некоторое время, после того, как его подобрали, можно воспользоваться триггером relay. Нужно сделать так, чтобы при подбирании элемента, элемент вырабатывал событие, которое принимает relay. Последний, выждав заданное время (переменная delay), отсылает другое сообщение обратно элементу.

Пример: example6.rar (bsp,map)


Последовательность событий

    Речь пойдет о событиях, которые не происходят, пока не произойдет другое событие. В качестве примера, будем рассматривать кнопку возле двери, которая работает вхолостую, пока не будет нажата еще одна кнопка.
    Честно говоря, гуманного способа я не нашел. В смысле такая комбинация повлечет за собой смерть одного монстра.
    Создаем train, который привязан ко второй кнопке (фиксированной) и который поднимается вверх на несколько юнитов. На train ставим любого монстра, к свойству target которого привязываем открывание двери (той, что возле кнопки). Над монстром вешаем платформу, которой задаем функцию двери открывающейся вниз и привязываем ее к первой кнопке (не фиксированной). Положение и расстояние открывания двери-платформы должны быть такими, чтобы немного не доставать до монстра. И еще у платформы нужно выставить damage, превышающий энергоресурс монстра.
    Работает это просто. Пока жмем первую кнопку, платформа опускается, но не достает до монстра. Если нажать вторую кнопку, то train приподнимет монстра, и тогда платформа запущенная первой кнопкой его раздавит. Последнее повлечет открывание нужной нам двери.

Пример: example7.rar (bsp,map)


Последовательность событий 2 (гуманная)

    Разобрался, как сделать последовательное срабатывание триггеров без убивания монстра. Задача: сделать некоторый триггер (например, trigger_once) не активным, пока не сработает второй триггер.
    Второй триггер делается обычно. Пусть он вырабатывает событие «activate». В качестве первого триггера надо использовать не trigger_once, а объект info_notnull, следующего вида:
{
"classname" "info_notnull"
"target" "open_door"
"targetname" "activate"
"use" "trigger_once"
}

    Работает это следующим образом. Сам info_notnull при соприкосновении никак себя не выдает, потому что не является ни функцией, ни триггером. При срабатывании второго триггера наш объект получает событие «activate» и тем самым запускается функция trigger_once. Т.е. таким образом, происходит подмена класса сущности. Теперь объект полноценный триггер и будет работать так, как ему и полагается.
    Проблема у меня возникла только одна. В GtkRadiant объект info_notnull определен как точечный. Поэтому при установке такого класса, браш триггера редактором просто выбрасывался. Нормальное решение в голову пришло только одно: был открыт файл GtkRadiant\q1.game\id1\entities.ent и в описании info_notnull тип был изменен с «point» на «group».


Триггер для сущностей

    Когда создается обычная дверь, вокруг нее автоматически создается триггер, соприкосновение с которым заставляет дверь открыться. В процессе создания карты sm168_digs2 - "Silver-Gold" мне понадобилось создать свой триггер для открывания двери: стандартный меня не устроил размерами. Обычным путем, с помощью trigger_multiple, этого сделать нельзя, потому что триггер реагирует только на игрока и монстры не могут открывать такую дверь. Ребята с Func_MsgBoard подсказали, как это можно реализовать.
    Триггер делается из сущности info_notnull. Здесь следует обратить внимание, что сущность изначально точечная, не имеет брашей. Поэтому, если Ваш редактор не может создать info_notnull с брашем, то придется залезть (если это возможно) в описание сущностей и поменять тип (в *Radiant это можно сделать).
    Весь "триггер" выглядит так:

"classname" "info_notnull"
"target" "func_door1"
"touch" "multi_trigger"
"think" "InitTrigger"
"nextthink" "0.2"
"wait" "1"

    Здесь "think" и "nextthink" инициализируют сущность как триггер. "touch" указывает на функцию, которая будет вызвана в момент соприкосновения другой сущности с нашим "триггером". "multi_trigger" это функция, принадлежащая trigger_multiple, которая, собственно, и занимается посылкой события "target". Но в отличие от multi_touch она не делает проверку на игрока. Последнее свойство "wait" отличает trigger_once от trigger_multiple. Чтобы после срабатывания триггер не был удален, нужно присвоить этому свойству положительное значение, которое будет определять время между срабатываниями триггера.
    Примечательно то, что получившийся триггер срабатывает от любой небрашевой сущности. Например, он может сработать, если сквозь него пролетит ракета или откуда-то сверху свалится ящик с патронами.