Max Payne: Часть 1. Дверь с кнопкой

Содержание:
Для начала нам понадобится какое-нибудь здание. В моем случае это некий ангар.
В качестве разминки давайте сделаем ангарную дверь. Вся работа с этой дверью пойдет в режиме F5. Постройте дверь в закрытом состоянии. Выделите ее и нажмите D (аналогично СКМ -> Make Selection Dynamic). На оба вопроса отвечайте утвердительно. Дверь превратилась в динамический объект. Теперь предстоит задать ей анимацию. Выделите дверь и нажмите Tab (или СКМ -> Dynamics and FSM -> Keyframe Dialog).
Анимация динамических объектов выполняется по ключевым кадрам. Keyframe_0 – это именно он. Можно указать, какой кадр будет при загрузке уровня (make default) и в каком состоянии просчитывать освещение (make lighting). Для дверей я обычно ставлю освещение в открытом состоянии. Итак, у нашей двери будет два ключевых кадра – «дверь закрыта» и «дверь открыта». Нажмите на Add и добавьте еще один кадр. Выделите новый кадр и закройте это окно.
Активным является второй кадр. Но мы не видим никаких изменений, т.к. по умолчанию он совпадает с предыдущим (то есть с активным в момент создания второго). Нажмите N (СКМ -> Animate toggle); вокруг экрана появится красная рамка, показывающая, что производится редактирование текущих ключевых кадров. Переместите дверь в открытое состояние и выключите режим анимации (снова N). Теперь при выделении в предыдущем диалоге ключевых кадров положение двери будет меняться.
Нажмите Shift+Tab (СКМ -> Dynamic and FSM -> Animation Dialog). Это список анимации для выбранного объекта. Добавьте новую анимацию и укажите ключевые кадры.
Кнопка Keyframes позволяет поменять ключевые кадры, Length – длительность анимации в секундах. В Position и Rotation Graph можно установить неравномерное движение и поворот объекта. Обращаю Ваше внимание на то, что поворот в течение анимации осуществляется относительно Pivot Point. Иногда это существенно влияет на вид движения. Двойной щелчок по анимации продемонстрирует ее в редакторе.

Отступление – Pivot Point и поворот объектов

Аналогичным образом сделайте анимацию закрытия двери. Я назвал их Opening и Closing. Теперь нам понадобятся кнопки снаружи и внутри. Поверх каждой кнопки поставьте триггер. Сделать это можно в F3 -> N (СКМ -> New Entity) и выбрать тип trigger (это будет сферический триггер), либо превратить в триггер любой готовый Mesh: F5, Ctrl+D (СКМ –> Make Selection Trigger). Триггеры бывают нескольких типов – в зависимости от способа активации (понятного по названию). Пояснения требует разве что LookAt – взгляд игрока направлен в Pivot Point триггера. Можно указать вид анимации; если Макс выламывает дверь, логично поставить use_kick_door вместо default.
Теперь настал черед непосредственно самих скриптов. Выделяйте дверь и нажимайте B (СКМ -> Edit FSM).
Этот скриншот демонстрирует все разделы скриптов. На самом деле он склеен из трех разных, т.к. объект не может быть одновременно и DO, и AI и Trigger. Пойдем по порядку (поскольку MP2 использует синтаксис языка Си, я буду говорить не «команды», а «функции»)
Custom Events – в этом блоке функции выполняются в зависимости от состояния объекта…
States - … которое описано в блоке States. Начальное состояние выделено жирным шрифтом, можно выбирать, какое именно считать начальным..
Timers – всевозможные задержки.
DO – события, характерные для динамических объектов…
AI – и для персонажей.
Trigger/T_Activate – блок, выполняемый при активации триггера.
Generic/Startup – блок, выполняемый при загрузке уровня.
Animation – скрипты, привязанные к кейфреймам.
Итак, у нашей двери будет два состояния – «открыто» и «закрыто». Состояния не обязательно совпадают с кейфреймами, но у нас это именно так. Добавьте в States состояния Open и Closed (ПКМ -> Add State Specific Handler), Closed сделайте начальным (ПКМ -> Set Default).
Что происходит при активации триггеров? Во-первых оба они отключаются до окончания анимации, чтобы игрок не активировал их снова раньше времени – это нарушит наши планы. Во-вторых, начинается сама анимация открытия или закрытия двери. В-третьих, слышен звук движущегося механизма. Приступим к воплощению задуманного. Добавьте два события в Custom Events – Action_Start и Action_End.
Рассмотрим Action_Start. В блоках Custom Events функция выполняется либо всегда (зеленый и красный блоки), либо только когда объект находится в определенном состоянии (желтые блоки). Напишите следующее:
**%% Closed:**
this->DO_Animate(Opening);

**%% Open:**
this->DO_Animate(Closing);

**%% Send Always After:**
::Backyard::Trigger_Outside->T_Enable(0);
::Hangar::Trigger_Inside->T_Enable(0);
this->A_Play3DSound(generic, elevator_moving_loop, "");

Обращение к объектам.

Ваши комнаты и объекты могут называться по другому. Но обращение к ним идет аналогично - ::Комната::Объект::Подобъекты (если есть)-> Команда ( Параметры )
Это абсолютный путь. Иногда удобнее применять относительные пути:
this – текущий объект
parent – объект, которому принадлежит текущий.
activator – персонаж, активировавший блок. Как правило, это блок Trigger -> T_Activate.


То есть, если дверь закрыта, то мы ее открываем, а если открыта, то наоборот. И в любом случае мы выключаем оба триггера и играем звук. Теперь обратимся к Action_End.
**%% Closed:**
this->FSM_Switch(Open);

**%% Open:**
this->FSM_Switch(Closed);

**%% Send Always After:**
::Backyard::Trigger_Outside->T_Enable(1);
::Hangar::Trigger_Inside->T_Enable(1);
this->A_StopAll3DSounds("");
# Если не остановить сейчас звуки двери, то дверь так и продолжит шуметь.
# Значок «#» в начале строки позволяет писать комментарии
this->A_Play3DSound(generic, elevator_moving_end, "");
Обращаю ваше внимание на то, что я пишу не true или false, а 1 или 0 - в Си любое ненулевое значение является true. Стало быть, как дверь «узнает», что настал Action_End? Из анимации. В блоке Animation в обеих анимациях пропишите:
**%% Reaching 2nd keyframe:**
this->FSM_Send(Action_End);
Отлично, теперь в обоих триггерах заполните блок Trigger -> T_Activate. Здесь и далее я подразумеваю подблок SendAlwaysAfter. Вообще, я еще не сталкивался со случаем, когда нужно записывать в SendAlwaysBefore.
this->A_Play3DSound(generic, beep, "");
::Hangar::Door->FSM_Send(Action_Start);
Дверь готова к работе. Наверняка Вы попробовали поставить Макса под закрывающуюся дверь. Он (да и любой другой персонаж) останавливает анимацию. Если вы хотите, чтобы его придавило, перейдите в блок DO_MovedToInvalidPosition:
**%% Open:**
activator->C_SetHealth(0);
# или activator->C_CauseDamage(например 30); если только слегка поцарапать 
Вообще, старайтесь не разбрасывать функции по разным объектам. Несмотря на то, что FSM есть почти у всех, и из каждого можно обратиться к каждому, рекомендую держать основную массу скрипта в одном месте. В данном случае я весь код запихнул в Door, а в триггерах оставил лишь самое необходимое.
Вы, наверное, уже заметили, что еще при написании функции открывается список возможных вариантов, а в строке состояния пишется тип параметра. Настоятельно рекомендую пользоваться этими подсказками. Переключаться между возможными вариантами можно с помощью Tab.


Views: 6 218

Iron Fred #1 - 8 years ago 1
Голосов: +1 / -0
Очень ценная статейка.