Маппинг и SDK

KF SDK: муверы, двери, триггеры и лифты

Руководство по муверам в KF SDK, которое написал K-2 для форума killingfloor.ru в 2011 году: создание муверов, триггеры и система Event > Tag, свойства муверов и разбор готовых примеров от простой двери до лифта. Основы работы с редактором описаны в основах редактора карт и в руководстве по маппингу. На том же форуме DiaLight выкладывал видеогайды по дверям и триггерам.

Муверы — это объекты, которые двигаются прямо во время игры. В KF это двери, лифты, машины, скейтборды и прочее. Создаются они из статиков и имеют похожие свойства, но у них есть и ряд дополнительных возможностей.

Создание мувера

  1. Выберите в браузере Static Mesh меш будущего мувера (например, дверь). Мувер можно сделать и из Solid-брашей, предварительно конвертировав их в статик.
  2. Нажмите правой кнопкой на кнопку Add Mover Brush и выберите обычный Mover. Он появится внутри браша-строителя, так что их Pivot совпадут. На 2D-видах мувер обведён розовым контуром.
  3. Созданная дверь находится в закрытом положении. Переместите её на место и подгоните размер через DrawScale3D.
  4. Закрытое положение — это ключ 0 (начальное). Чтобы дверь могла открываться, задайте положение 1: правый клик по двери, Mover > Key 1, затем передвиньте или поверните её в открытое положение.
  5. Проверьте: правый клик, Mover > Key 0 — дверь должна вернуться в начальное положение.

Движение хранится как разница координат, поэтому если двигать дверь в начальном положении, вместе с ней сдвинется и её открытый вариант.

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

Важно выбирать меш именно в браузере мешей и сразу создавать мувер кнопкой Mover. Если сначала поставить статик на карту, выделить его и нажать Mover, появится мувер в виде пистолета: это меш по умолчанию, который берётся, когда в браузере ничего не выбрано (уточнение K-2 из комментариев).

Тип активации (InitialState)

Тип активации задаётся в свойствах мувера на вкладке Object, главный параметр здесь — InitialState. Актуальные значения:

ЗначениеПоведение
ConstantLoopОбъект движется постоянно и не требует активации
TriggerToggleМувер меняет положение, только если его активировал Event. По умолчанию используется для всех дверей в Killing Floor
TriggerOpenTimedМувер «открывается» по Event и через определённое время возвращается в исходное положение
BumpOpenTimedМувер «открывается» при контакте с ним и «закрывается» через определённое время. По умолчанию используется для Mover
StandOpenTimedМувер активируется, когда на него встают, и «закрывается» через определённое время

Триггеры и система Event > Tag

Триггеры — это активаторы, работающие по простой схеме Event > Tag (событие > метка). По сути это «кнопки», которые создают событие (Event), активирующее всё, у чего стоит соответствующая метка (Tag). Запускать так можно не только муверы.

У триггеров и муверов в свойствах есть вкладка Events. В свойствах триггера в поле Event пишется название события (любое придуманное), а в свойствах мувера в поле Tag — та же метка.

Триггеры находятся в браузере акторов (Actor Class), во вкладке Triggers. Для дверей подходят два типа:

  • KFUseTrigger во вкладке UseTrigger (для лифтов есть также KFElevatorTrigger);
  • KFProxyTrigger во вкладке Trigger.

Чтобы поставить триггер на карту, выберите его класс в браузере Actor Class, затем щёлкните ПКМ в нужном месте карты и выберите Add <название класса> Here (так добавляется любой объект из Actor Class). Для двери: Triggers → UseTrigger → KFUseTrigger, ставите «голову орла» в центр двери и связываете: в мувере в Tag пишете метку (например 555), а в триггере ту же метку в Event (K-2 и Gaiver5, тема «Триггеры», форум killingfloor.ru).

KFUseTrigger

Используется для стандартных дверей KF (KFDoorMover) с типом активации TriggerToggle: двери открываются и закрываются по кнопке «использовать». Выглядит как птичья голова и ставится в центр двери, туда, где будет индикатор заварки.

За сварку отвечает именно триггер; кроме того, двери можно запереть на ключ. Основные параметры:

ПараметрНазначение
MaxWeldStrengthСколько очков сварки нужно, чтобы заварить дверь на 100%
ReFireDelayЗадержка в секундах до повторной активации триггера
Message (вкладка Use Trigger)Сообщение на экране, когда игрок стоит рядом и может активировать триггер. Если ввести Press USE key, будет выводиться стандартное сообщение, которое переводится на русский в русской локализации

О том, что KFUseTrigger работает только с KFDoorMover, подробнее в руководстве по маппингу.

KFProxyTrigger

Активируется при каком-либо взаимодействии с ним, типов активации у него больше, но для завариваемых дверей он не подходит.

ПараметрНазначение
bInitiallyActiveАктивен ли триггер изначально
bTriggerOnceOnlyЕсли True, триггер срабатывает только один раз
ClassProximityTypeКласс, на контакт с которым реагирует триггер при TT_ClassProximity
DamageTresholdУрон, нужный для активации (только при активации выстрелом)
MessageСообщение в углу экрана при взаимодействии игрока с триггером
RepeatTriggerTimeВремя, через которое действие триггера повторяется; для этого триггера не актуально
ReTriggerDelayЗадержка в секундах от активации до возможности повторной активации
TriggerTypeТип активации триггера (см. ниже)

Значения TriggerType:

ЗначениеКогда срабатывает
TT_PlayerProximityПри контакте с игроком
TT_PawnProximityПри контакте с игроками, зомби и ботами
TT_ClassProximityПри контакте с классом, указанным в ClassProximityType
TT_AnyProximityПри контакте с любым материальным объектом
TT_ShootПри выстреле; триггер при этом становится осязаемым для пуль
TT_HumanPlayerProximity, TT_LivePlayerProximityПри контакте с игроком-человеком и с живым игроком, что по сути одно и то же

Свойства муверов

Муверы настраиваются довольно гибко и сами могут работать как триггеры, создавая события. Основные свойства, общие для всех муверов, на вкладке Mover:

ПараметрНазначение
bDamageTriggeredЕсли True, мувер активируется выстрелом или ударом по нему
bTriggerOnceOnlyПри True мувер активируется только один раз
BumpEventEvent, который мувер создаёт (как триггер) при соприкосновении с ним
BumpTypeНа какое касание реагирует мувер: Player — игрок, Pawn — игрок или AI, Any — любой объект
DamageTresholdМинимальный урон по муверу для активации при bDamageTriggered = True
DelayTimeЗадержка перед началом движения
EncroachDamageУрон при столкновении мувера с чем-либо; игроку наносится вдвое меньше указанного
KeyNumТекущее положение; переключается и в выпадающем меню, но через этот параметр можно задать больше 8 положений
MoverEncroachTypeРеакция на столкновение с игроком или зомби (см. ниже)
MoverGlideTypeТип ускорения: GlideByTime — мувер разгоняется постепенно, MoveByTime — скорость постоянная
MoveTimeВремя, за которое мувер проходит путь от одного ключа до другого
NumKeysКоличество ключей-положений. По умолчанию 2, но можно задать много; каждый отрезок проходится за MoveTime
PlayerBumpEventКак BumpEvent, но срабатывает только при контакте с игроком
StayOpenTimeСколько дверь остаётся открытой при типах активации ...OpenTimed

Значения MoverEncroachType:

ЗначениеПоведение
ME_StopWhenEncroachДвижение останавливается
ME_ReturnWhenEncroachМувер возвращается в предыдущее положение
ME_CrushWhenEncroachМувер буквально убивает препятствие
ME_IgnoreWhenEncroachМувер продолжает движение

На вкладке MoverEvents задаются события, которые срабатывают на разных стадиях движения мувера, на вкладке MoverSounds — звуки, сопровождающие движение.

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

Свойства KFDoorMover

Двери в KF создаются как KFDoorMover: их можно заварить, и в них ломятся зомби. Дополнительные свойства:

ПараметрНазначение
bBlockDamagingOfWeldЕсли True, сварка неразрушима, и дверь можно открыть только сварочным аппаратом
bDisallowWeldПри True дверь нельзя заварить
bElevOuterDoorTop / bElevOuterDoorBottomЯвляется ли дверь верхней / нижней дверью лифта
bKeyLockedЗаперта ли дверь на ключ
bSmallArmsDamageПри True двери можно нанести урон не только гранатами
bStartSealedБудет ли дверь заварена в начале каждой волны
bZombiesIgnoreБудут ли зомби игнорировать заваренную дверь
StartSealedWeldPrcНа сколько процентов заварена дверь при bStartSealed
ZombieDamageReductionFactorУрон от зомби: чем больше значение, тем быстрее зомби её сломают

Здесь же задаются звуки разрушения двери.

Примеры муверов

На первый взгляд всё выглядит сложно, но это не так. K-2 собрал тестовую карту KF-MoversExample с простыми примерами; ниже описаны муверы с неё.

Обычная завариваемая дверь

  • Мувер: KFDoorMover
  • Триггер: KFUseTrigger
  1. Создайте KFDoorMover с двумя ключами-положениями (0 и 1). В примере взят Static Mesh двери из KF-Offices с теми же звуками открытия и закрытия.
  2. Поставьте KFUseTrigger в центр двери.
  3. Задайте Event триггера и Tag двери одинаковыми: Door1.
  4. В свойствах триггера поставьте MaxWeldStrength = 200. Остальные значения стандартные.

Двойная дверь, заваренная в начале каждой волны

  • Муверы: 2x KFDoorMover
  • Триггер: KFUseTrigger
  1. Создайте два отдельных мувера, у каждого свои ключи. В примере за основу взята двойная дверь из KF-BioticsLab.
  2. Задайте обоим одинаковые свойства и одинаковый Tag.
  3. Поставьте один KFUseTrigger с Event, равным этому тегу. Здесь MaxWeldStrength = 500.
  4. Чтобы дверь заваривалась в начале каждой волны, у обоих муверов поставьте bStartSealed = True.

Дверь, запертая на ключ

  • Мувер: KFDoorMover
  • Триггер: KFUseTrigger
  1. Сделайте обычную дверь и в её свойствах поставьте bKeyLocked = True и bDisallowWeld = True.
  2. Сделайте Tag двери и Event триггера одинаковыми.
  3. Добавьте ключ: в браузере акторов Actor > Pickup > KFKeyPickUp.
  4. В свойствах ключа на вкладке Events задайте Tag, такой же как у двери.

Из-за бага такую дверь можно взорвать обычными гранатами.

Дверь торговца

  • Мувер: KFTraderDoor
  • Триггер: нет (активируется через ShopVolume)

Такие муверы нельзя сломать, заварить или взорвать. Открываются и закрываются они как обычные муверы; в KF их обычно ставят в магазинах. Случайно выбранная зона магазина (ShopVolume) после уничтожения волны создаёт Event, как триггер, а по окончании времени закупки создаёт его повторно, закрывая дверь.

  1. Создайте KFTraderDoor с ключами 0 и 1 и задайте ей Tag.
  2. В свойствах ShopVolume на вкладке Events задайте Event, совпадающий с тегом двери. У разных магазинов события должны быть разными.

Если дверь магазина не открывается в конце волны, почти всегда не прописан Event в ShopVolume (из ответа K-2 в комментариях). В крайнем случае откройте в редакторе любую карту с рабочим магазином и повторите её настройки.

Двойная автоматическая дверь

  • Муверы: 2x Mover, тип активации TriggerOpenTimed
  • Триггеры: 2x KFProxyTrigger, тип активации TT_PlayerProximity
  1. Создайте двойную дверь, как обычно, но из Mover.
  2. Поставьте KFProxyTrigger по обе стороны двери с Event, равным тегу дверей. Когда игрок «наступает» на триггер, Event открывает двери. Благодаря типу активации мувер нельзя активировать повторно, пока он не «закроется».
  3. Несмотря на TT_PlayerProximity, такой дверью могут пользоваться и зомби. Чтобы дверь не блокировала их пути, у муверов на вкладке Collision поставьте bPathColliding = False.

Дверь, открывающаяся выстрелом по кнопке

  • Муверы: 2x Mover (кнопка и дверь), тип активации TriggerToggle
  • Триггер: KFProxyTrigger, тип активации TT_Shoot
  1. Поставьте KFProxyTrigger с Event = bt и bTriggerOnceOnly = True, чтобы его можно было активировать только один раз.
  2. Сделайте мувер-кнопку с Tag = bt.
  3. У кнопки на вкладке MoverEvents задайте OpenedEvent = ShootDoor — это тег мувера-двери. Event срабатывает, когда кнопка «откроется».
  4. Сделайте мувер-дверь с Tag = ShootDoor и bPathColliding = False.

Выстрел по триггеру нажимает кнопку, а кнопка открывает дверь.

Секретная дверь, открывающаяся от урона

  • Мувер: Mover, тип активации TriggerOpenTimed
  • Триггер: KFProxyTrigger, тип активации TT_PlayerProximity (здесь не имеет значения)
  1. Сделайте мувер из Solid-браша, конвертированного в статик.
  2. Поставьте bDamageTriggered = True.
  3. Задайте 3 ключа (0-2) и NumKeys = 3.

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

Скейт, прыгающий через трамплин

  • Мувер: Mover, тип активации TriggerToggle
  • Триггер: нет (мувер активирует сам себя при контакте с игроком)
  1. Задайте муверу 9 ключей так, чтобы за одинаковое время он проходил одинаковое расстояние (не считая подъёма и спуска).
  2. Поставьте PlayerBumpEvent, равный его же Tag: мувер запускается, когда до него дотрагивается игрок.
  3. Чтобы при переходе между ключами не было торможения, поставьте MoverGlideType = MoveByTime.

Со стороны выглядит немного кривовато, но это всё-таки обычный мувер.

Раздатчик

  • Муверы: 4x Mover, тип активации TriggerOpenTimed
  • Триггер: KFProxyTrigger, тип активации TT_ClassProximity
  1. Внутри «приёмника» поставьте триггер, который реагирует на брошенные деньги: ClassProximityType = Class'KFMod.CashPickup'.
  2. Event триггера закрывает дверцы приёмника и одновременно открывает другие, за которыми появляется броня.

Заставить триггер реагировать на определённую сумму стандартными средствами нельзя: триггер реагирует на сам контакт с классом, а не на его свойства. Для этого понадобится свой класс актора со скриптом. Можно, например, сделать «пункт обмена» из нескольких триггеров, реагирующих на определённое брошенное оружие (из ответов K-2 в комментариях).

Подъёмник-платформа

  • Мувер: Mover, тип активации StandOpenTimed (срабатывает, когда на него встают)

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

Лифт-платформа — простейший рабочий лифт, такие часто использовались в UT2004. От обычного мувера он отличается только специальными точками: выходами LiftExit и центром LiftCenter (браузер акторов, вкладка NavigationPoint).

  1. Создайте мувер-платформу с InitialState = StandOpenTimed.
  2. Поставьте LiftCenter в «центр» мувера, находящегося в положении 0. Точка двигается вместе с лифтом, на ней AI стоит во время движения. В свойствах LiftCenter, на одноимённой вкладке, укажите Tag лифта.
  3. Поставьте точки LiftExit — условные выходы, через которые боты уходят после остановки мувера. В их свойствах задайте Tag лифта и SuggestedKeyFrame — при каком ключе-положении активен этот выход.

Обычный лифт на 2 этажа

  • Муверы: KFElevator, 2x KFElevatorDoorMover, 4x KFTraderDoor
  • Тип активации: Elevator (лифт), TriggerToggle (двери)
  • Триггеры: 3x KFElevatorTrigger

Разработчики изначально задумывали лифты в KF, поэтому для них есть специальные муверы и триггеры. Но с лифтами было связано много багов, и ни на одной официальной карте их нет. Такой лифт можно вызывать, и у него есть двери, которые открываются и закрываются в зависимости от положения лифта.

  1. Создайте мувер KFElevator, задайте ему Tag и ключи-положения.
  2. В свойствах лифта, KFElevator > ElevatorFloorTags, задайте теги для каждого ключа, например BottomFloor для нижнего этажа и TopFloor для верхнего.
  3. Поставьте KFElevatorTrigger внутрь лифта (в начальном положении) и задайте ему Tag, например ElevatorLiftTrigger. Тот же тег впишите в свойствах лифта: вкладка KFElevator, поле ElevatorCenterTrigger. Этим триггером лифт запускается изнутри.
  4. На каждый этаж поставьте по KFElevatorTrigger с Tag, заданным ранее в ElevatorFloorTags для этого этажа. Этими триггерами лифт вызывают.
  5. Добавьте точки LiftExit и LiftCenter, как у платформы, но в свойствах LiftCenter, кроме тега лифта, впишите ещё тег центрального триггера (KFElevatorTrigger внутри лифта).
  6. Двери. У KFElevatorDoorMover есть недостатки: их можно сломать или взорвать. Лучше использовать двери торговца, которые приводятся в действие KFElevatorDoorMover. Создайте верхнюю и нижнюю условные двери лифта как KFElevatorDoorMover и спрячьте их где-нибудь за картой, вне поля зрения игроков.
  7. Нижней условной двери на вкладке KFDoorMover поставьте bElevOuterDoorBottom = True, верхней — bElevOuterDoorTop = True.
  8. На вкладке MoverEvents каждой условной двери задайте события Closing и Opening, которые будут двигать настоящие нижние или верхние двери, например BottomDoors для нижней и TopDoors для верхней.
  9. Поставьте по паре KFTraderDoor на нижний и верхний этаж; каждой паре задайте Tag, указанный в MoverEvents соответствующей условной двери.

Таким лифтом могут пользоваться и вызывать его не только игроки.

Уничтожитель

  • Муверы: 11x Mover, тип активации: 10x TriggerOpenTimed, рычаг — BumpOpenTimed
  • Триггер: нет (запускается мувером-рычагом)

На серьёзных картах, претендующих на белый список, такое лишнее, но собирается конструкция легко и выглядит очень зрелищно. В неё входят 3 пары крушителей, 2 циркулярные пилы, 2 держателя для пил и 1 рычаг.

  1. У пил и крушителей поставьте MoverEncroachType = ME_CrushWhenEncroach и большой EncroachDamage.
  2. Рычагу задайте Event, который запускает пилы с держателями и первую пару крушителей.
  3. OpenedEvent первой пары крушителей запускает вторую пару, а та — третью: получается небольшая цепная реакция.
  4. Чтобы пилы вращались независимо от активации, у них на вкладке Movement задайте RotationRate по оси Yaw и поставьте bFixedRotationDir = True.

Через некоторое время все муверы возвращаются в исходное положение, и конструкцию можно запустить снова.

Муверы — интересная тема, и при наличии фантазии с ними можно сделать уникальные вещи. Игра таким разнообразием не балует, так что необычная идея обязательно запомнится игрокам.

Ответы K-2 из тем «Вопросы по картостроению» и «Непростые вопросы по МАППИНГУ»

По двум темам вопросов к мапперам на форуме killingfloor.ru.

Остальные значения InitialState (K-2 писал по памяти и предупреждал, что может ошибаться):

ЗначениеПоведение
LeadInOutLooperПри активации мувер циклично перемещается между ключами 0 и 1 и возвращается в положение 0 при повторной активации
TriggerAdvanceПока игрок находится в «активной зоне» триггера, мувер движется к конечному положению; если игрок выходит, движение останавливается и при повторном входе продолжается с той же точки
TriggerControlПока игрок активирует триггер, мувер движется к конечному положению и остаётся в нём; если «контакт» прерывается, мувер возвращается в исходное положение, даже если не «открылся» до конца
TriggerPoundКак TriggerControl, но по прошествии OtherTime закрывается; каждый отрезок проходится за MoveTime, поэтому полузакрывшийся мувер откроется за то же время, что и из исходного положения
LoopMoveКак ConstantLoop, но требует постоянной активации от триггера

Баррикада, которую нельзя заварить, но которая восстанавливается к каждой волне. Сделайте её как KFDoorMover и на вкладке KFDoorMover поставьте bDisallowWeld = True и bStartSealed = True, а в StartSealedWeldPrc впишите, на сколько процентов она будет заварена. Остальные параметры этой вкладки оставьте False. Оптимальные значения ZombieDamageReductionFactor — 0.75-0.95, по умолчанию 0.85. У автора вопроса баррикады получились неубиваемыми, пока он не поставил к ним триггеры.

KFGlassMover (разрушаемые машины и вертолёты, как на KF-Abandoned-Base-MacabreDay) — «неразмещаемый» класс, его нет в списке Add Mover Brush. Проще всего скопировать такой мувер с другой карты и отредактировать: StaticMesh меняется на вкладке Display. Огонь и обломки при ударах по нему, скорее всего, заданы кастомным Emitter-ом в полях GlassBits и BreakGlassBits.

Другая текстура у копии мувера. Опция Display > Texture > Use для мувера не срабатывает; текстуру дубликата меняйте на вкладке Skins (раздел Display свойств).

Зона срабатывания триггера. Огромными триггеры на картах делают, чтобы их было легче найти и отличить. Сама «активная зона» определяется коллизией и расширяется на соответствующей вкладке свойств. Чтобы видеть её, включите в окне редактора RadiiView (ПКМ по рамке > Actors > RadiiView).

Поштучный спавн зомби через ScriptedTrigger. KFProxyTrigger с RepeatTriggerTime, запускающий ScriptedTrigger-зомбогенератор, потом не остановить, а повтор через GOTOACTION «зацикливается» и тоже не отменяется. K-2 советовал сделать ProxyTrigger одноразовым (bTriggerOnceOnly) и поставить 2-4 ScriptedTrigger-а: каждому в последовательность добавить нужное число спавнов (1 зомби — 1 action), а между действиями SpawnActor — интервал в 1-2 секунды (Action_WAITFORTIMER). Много ScriptedTrigger-ов на карте практически не влияет ни на компиляцию, ни на производительность.

Тихий звук мувера. Импортированные звуки мувера слышно еле-еле, даже на максимальной громкости, а стандартные звучат нормально. По словам K-2, редактор при импорте автоматически конвертирует звуки в стерео; попробуйте импортировать моно-звуки, хотя 100% гарантии нет. Gaiver5 тоже не советовал стерео: моно работает в SDK оптимально, но немного тише.