Ru/Authoring a Model Entity: Difference between revisions
m (Nesciuse moved page Authoring a Model Entity/ru to Ru/Authoring a Model Entity over redirect: Language prefixes) |
m (-Using L template for links) |
||
Line 1: | Line 1: | ||
{{langsp}} | {{langsp}} | ||
''Предполагается что вы прочли и полностью поняли статью | ''Предполагается что вы прочли и полностью поняли статью {{L|Authoring a Logical Entity}}.'' | ||
В этой статье мы создадим ''объект'' (''объект'' = entity), который может двигаться, сталкиваться с другими объектами, и который отображается в игре (в виде модели). Мы заставим наш объект случайным образом перемещаться по миру. | В этой статье мы создадим ''объект'' (''объект'' = entity), который может двигаться, сталкиваться с другими объектами, и который отображается в игре (в виде модели). Мы заставим наш объект случайным образом перемещаться по миру. | ||
Line 37: | Line 37: | ||
}; | }; | ||
Заметьте что на этот раз мы наследуем наш класс от <code> | Заметьте что на этот раз мы наследуем наш класс от <code>{{L|CBaseAnimating}}</code>, и на этот раз у нас появилось несколько новых функций. В закрытой (private) части класса содержатся две переменные: <code>bool</code> - это тип переменной которая может принимать только значения правда/ложь (true/false) (1/0), а переменные типа float могут хранить в себе числовые значения с десятичной точкой. | ||
{{note|Наш ''объект'' не начнёт двигаться пока не получит соответствующую команду от <code>InputToggle()</code>. Заставить ''объект'' двигаться можно только вызовом функции <code>InputToggle()</code> (а не простой установкой значения <code>m_bActive</code> в единицу) - это станет неплохой практикой после того как вы прочтёте статью.}} | {{note|Наш ''объект'' не начнёт двигаться пока не получит соответствующую команду от <code>InputToggle()</code>. Заставить ''объект'' двигаться можно только вызовом функции <code>InputToggle()</code> (а не простой установкой значения <code>m_bActive</code> в единицу) - это станет неплохой практикой после того как вы прочтёте статью.}} | ||
Line 60: | Line 60: | ||
END_DATADESC() | END_DATADESC() | ||
Самое большое отличие от созданного нами ранее логического объекта в наличии макроса <code>DEFINE_THINKFUNC</code>, который даёт движку Source знать о, так называемой | Самое большое отличие от созданного нами ранее логического объекта в наличии макроса <code>DEFINE_THINKFUNC</code>, который даёт движку Source знать о, так называемой {{L|Think|"мыслительной"}} функции. | ||
== Определение модели == | == Определение модели == | ||
Line 67: | Line 67: | ||
<span style="color:blue;">#define</span> ENTITY_MODEL <span style="color:brown;">"models/gibs/airboat_broken_engine.mdl"</span> | <span style="color:blue;">#define</span> ENTITY_MODEL <span style="color:brown;">"models/gibs/airboat_broken_engine.mdl"</span> | ||
Этот код жёстко задаёт | Этот код жёстко задаёт {{L|model|модель}}, которую наш ''объект'' будет отображать в игре. Это статический кусок информации - не переменная: это значение не может меняться после компиляции кода. Путь к .mdl-файлу задаётся относительно директории игры (например <code>hl2/</code>). | ||
{{note|Мы создали эту строку только для того чтобы упростить поиск и замену этого значения в будущем. Она не влияет на работу нашего кода.}} | {{note|Мы создали эту строку только для того чтобы упростить поиск и замену этого значения в будущем. Она не влияет на работу нашего кода.}} | ||
Line 73: | Line 73: | ||
== Функция Precache() == | == Функция Precache() == | ||
Мы добрались до первой функции. Функция <code> | Мы добрались до первой функции. Функция <code>{{L|Precache()}}</code> вызывается при появлении объекта на карте и гарантирует загрузку всего, что в ней перечислено до того как появится игрок (т.е. до того как он увидит мир и получит контроль над игрой). Смотрите {{L|Precaching Assets|рекомендации по прекэшированию}} для получения дополнительной информации. | ||
Прекэширование получило отдельное имя потому что существуют другие типы " | Прекэширование получило отдельное имя потому что существуют другие типы "{{L|asynchronous|асинхронной}}" загрузки, которые могут выполняться ''после'' появления игрока. | ||
<span style="color:green;">//----------------------------------------------------------------------------- | <span style="color:green;">//----------------------------------------------------------------------------- | ||
Line 87: | Line 87: | ||
} | } | ||
Для данного ''объекта'' мы прекэшируем модель, которую будем использовать, а затем вызываем функцию прекэширования нашего базового класса (которая в случае класса <code>CBaseAnimating</code> обеспечивает загрузку системы частиц для огня, т.к. каждый объект с моделью в игре может загореться). Другие команды прекэширования: <code> | Для данного ''объекта'' мы прекэшируем модель, которую будем использовать, а затем вызываем функцию прекэширования нашего базового класса (которая в случае класса <code>CBaseAnimating</code> обеспечивает загрузку системы частиц для огня, т.к. каждый объект с моделью в игре может загореться). Другие команды прекэширования: <code>{{L|PrecacheParticleSystem()}}</code> и <code>{{L|PrecacheScriptSound()}}</code> | ||
== Функция Spawn() == | == Функция Spawn() == | ||
Функция Valve <code> | Функция Valve <code>{{L|Spawn()}}</code> вызывается всякий раз когда создаётся новый экземпляр ''объекта'', примерно так же, как и конструктор. Вообще можно использовать функцию <code>Spawn()</code> вместо конструктора, но из соображений управляемости рекомендуется использовать и конструктор чтобы отделять инициализацию переменных от остального кода. | ||
<span style="color:green;">//----------------------------------------------------------------------------- | <span style="color:green;">//----------------------------------------------------------------------------- | ||
Line 105: | Line 105: | ||
} | } | ||
Сначала мы вызываем функцию <code>Precache()</code>, а затем идут вызовы различных функций класса <code>CBaseAnimating</code>. Функция <code> | Сначала мы вызываем функцию <code>Precache()</code>, а затем идут вызовы различных функций класса <code>CBaseAnimating</code>. Функция <code>{{L|SetModel()}}</code> задаёт модель объекта. В скобках ей передаётся [[#Defining the model|определённый нами ранее]] путь к модели объекта. Функция <code>{{L|SetSolid()}}</code> требует более подробных разъяснений. Она определяет форму, которую наш объект будет использовать для проверки на столкновение с другими объектами. Использование формы самой модели было бы очень, очень {{L|expensive|дорогим}} по отношению к ресурсам компьютера, поэтому движок Source предлагает несколько компромиссов: | ||
;<code>SOLID_NONE</code> | ;<code>SOLID_NONE</code> | ||
Line 112: | Line 112: | ||
:Используется ориентированный по осям ограничивающий короб. | :Используется ориентированный по осям ограничивающий короб. | ||
;<code>SOLID_BSP</code> | ;<code>SOLID_BSP</code> | ||
:Используется BSP-дерево для определения твёрдости (используется в | :Используется BSP-дерево для определения твёрдости (используется в {{L|brush|брашах}}). | ||
;<code>SOLID_CUSTOM</code> | ;<code>SOLID_CUSTOM</code> | ||
:''Объект'' использует свои собственные функции для проверки на столкновения. | :''Объект'' использует свои собственные функции для проверки на столкновения. | ||
;<code>SOLID_VPHYSICS</code> | ;<code>SOLID_VPHYSICS</code> | ||
:Для определения точных столкновений используется встроенная в модель | :Для определения точных столкновений используется встроенная в модель {{L|collision model|модель столкновений}}. | ||
Эти пункты выбираются на уровне движка и авторы модов не могут добавлять или менять их. Мы используем параметр <code> | Эти пункты выбираются на уровне движка и авторы модов не могут добавлять или менять их. Мы используем параметр <code>{{L|SOLID_BBOX}}</code>, который генерирует "{{L|bounding box|ограничивающий короб}}", размер которого движок определяет так, чтобы он охватывал всю модель. Более "дорогой" параметр <code>{{L|SOLID_VPHYSICS}}</code>, который использует встроенную в модель {{L|collision model|модель столкновений}}, не поддерживает низкоуровневые функции перемещения, которые мы будем использовать для нашего объекта, поэтому мы его не применяем. | ||
Вызов <code> | Вызов <code>{{L|UTIL_SetSize()}}</code> нужен для того, чтобы сделать наш ограничивающий короб кубическим. Это сделано потому, что каким бы странным это не казалось, ''ограничивающий короб не может вращаться''. Вам понадобится vphysics-обработка столкновений если вы захотите заставить модель вращаться. А по описанным выше причинам мы её не используем. | ||
{{warning|<code>Spawn()</code> вызывается '''незамедлительно''' после создания объекта. Если это случится сразу же после создания карты нет гарантии того, что на карте уже появились другие объекты. Поэтому любой код, который предполагает что объект будет связываться с другими игровыми объектами становится ненадежным. В таких случаях используйте функцию <code> | {{warning|<code>Spawn()</code> вызывается '''незамедлительно''' после создания объекта. Если это случится сразу же после создания карты нет гарантии того, что на карте уже появились другие объекты. Поэтому любой код, который предполагает что объект будет связываться с другими игровыми объектами становится ненадежным. В таких случаях используйте функцию <code>{{L|Activate()}}</code>, которая всегда вызывается после появления всех объектов.}} | ||
== Функция MoveThink() == | == Функция MoveThink() == | ||
{{L|Think()|"Мыслительная" функция}} (to think - думать) позволяет ''объекту'' "принимать решения" без побуждения со стороны внешнего кода. Вспомните про наш ''логический объект'' - он делал что-нибудь только при задействовании его входной функции; это не подходит для ''объектов'', которые должны определенным образом перемещаться по миру. Мыслительная функция, если она присутствует, обычно является центральной функцией в коде ''объекта''. | |||
Сейчас мы создаём мыслительную функцию, которая будет вызываться движком с периодичностью 20 раз в секунду. Это может показаться большим числом, однако современные процессоры очень быстрые, а наш ''объект'' очень простой. Вы сможете добавить очень большое кол-во объектов <code>CMyModelEntity</code> на карту без большой потери производительности. | Сейчас мы создаём мыслительную функцию, которая будет вызываться движком с периодичностью 20 раз в секунду. Это может показаться большим числом, однако современные процессоры очень быстрые, а наш ''объект'' очень простой. Вы сможете добавить очень большое кол-во объектов <code>CMyModelEntity</code> на карту без большой потери производительности. | ||
Line 160: | Line 160: | ||
Дополнительная информация: | Дополнительная информация: | ||
*<code> | *<code>{{L|gpGlobals}}->curtime</code> возвращает время, в которое исполняется текущий код, как значение с плавающей точкой. | ||
*<code>[[Wikipedia:Vector (spatial)|Вектор]]</code> - это набор переменных, используемых для определения движения, которые содержат в себе данные о направлении и скорости. Функция <code> | *<code>[[Wikipedia:Vector (spatial)|Вектор]]</code> - это набор переменных, используемых для определения движения, которые содержат в себе данные о направлении и скорости. Функция <code>{{L|SetAbsVelocity()}}</code> задаёт абсолютную скорость, | ||
*<code> | *<code>{{L|QAngle}}</code> это просто угол - т.е. то же самое что вектор только без данных о скорости. <code>{{L|QAngle}}</code> задаёт направление. | ||
*Функция <code> | *Функция <code>{{L|VectorAngles()}}</code> преобразует вектор (<code>velFacing</code>) в угол (<code>angFacing</code>). Не забывайте, что С++ очень строг в отношении типов: вам понадобятся утилитарные функции, такие как <code>VectorAngles()</code>, чтобы осуществлять преобразования между двумя типами. | ||
В конце мы вызываем функцию <code> | В конце мы вызываем функцию <code>{{L|SetNextThink()}}</code>, которая указывает когда в следующий раз необходимо вызвать мыслительную функцию. Здесь мы задаём новую обработку через 0.05 секунд (1/20 часть), однако это число может быть различным для разных объектов. Важно понимать, что если не вызвать функцию SetNextThink() объект перестанет "мыслить". | ||
Вы могли отметить, что здесь мы определяли новые переменные. Так же как и переменные, определенные внутри класса, переменные, определенные внутри функции, принадлежат данной функции. Они создаются каждый раз когда вызывается функция и уничтожаются по её завершении. | Вы могли отметить, что здесь мы определяли новые переменные. Так же как и переменные, определенные внутри класса, переменные, определенные внутри функции, принадлежат данной функции. Они создаются каждый раз когда вызывается функция и уничтожаются по её завершении. | ||
Line 212: | Line 212: | ||
Здесь всё очень прямолинейно. Мы используем оператор <code>if</code> чтобы определить текущее состояние флага <code>m_bActive</code>. Знак восклицания означает "не": "Если <code>m_bActive</code> не равно true (истина), то сделать следующее...". Позже мы используем оператор <code>else</code> чтобы определить что мы будем делать в противоположном случае. | Здесь всё очень прямолинейно. Мы используем оператор <code>if</code> чтобы определить текущее состояние флага <code>m_bActive</code>. Знак восклицания означает "не": "Если <code>m_bActive</code> не равно true (истина), то сделать следующее...". Позже мы используем оператор <code>else</code> чтобы определить что мы будем делать в противоположном случае. | ||
При активировании объекта мы начинаем его мыслительный процесс указывая движку Source какую мыслительную функцию использовать (по умолчанию это функция <code> | При активировании объекта мы начинаем его мыслительный процесс указывая движку Source какую мыслительную функцию использовать (по умолчанию это функция <code>{{L|Think()}}</code>, однако она у нас отсутствует). Отметьте наличие знака & и пробелов около каждой скобки в аргументе. Затем мы сообщаем движку, что наш объект будет перемещаться по миру летая, хотя это скорее не полёт а "плавание", т.к. в движке Source отсутствует имитация полёта (возможно вы сможете её добавить?). И, конечно, в конце мы устанавливаем <code>m_bActive</code> в true (истина) - само по себе это значение не поменяется! | ||
После команды <code>else</code> мы останавливаем объект. Мы сбрасываем мыслительную функцию в ноль (<code>NULL</code>) чтобы остановить мыслительный процесс. Функция <code> | После команды <code>else</code> мы останавливаем объект. Мы сбрасываем мыслительную функцию в ноль (<code>NULL</code>) чтобы остановить мыслительный процесс. Функция <code>{{L|SetAbsVelocity()}}</code> устанавливает объект в его {{L|origin|начало}} - используется вектор с нулевой скоростью. Тип передвижения устанавливается в <code>{{L|MOVETYPE_NONE}}</code>, чтобы предотвратить любое перемещение, команда на которое может поступить извне. И в конце <code>m_bActive</code> устанавливается в false (ложь). | ||
== Запись FGD == | == Запись FGD == | ||
Line 237: | Line 237: | ||
== Смотрите также == | == Смотрите также == | ||
* | * {{L|Authoring a Model Entity/Code|Полный код статьи}} | ||
* | * {{L|Your First Entity|Ваш первый игровой объект}} |
Revision as of 08:48, 11 July 2024
Предполагается что вы прочли и полностью поняли статью Authoring a Logical Entity .
В этой статье мы создадим объект (объект = entity), который может двигаться, сталкиваться с другими объектами, и который отображается в игре (в виде модели). Мы заставим наш объект случайным образом перемещаться по миру.
Создайте файл sdk_modelentity.cpp
в вашей персональной папке в серверном проекте и мы начнем.
Включение заголовочного файла и описание класса
Сейчас вы должны понимать следующий код:
#include "cbase.h" class CMyModelEntity : public CBaseAnimating { public: DECLARE_CLASS( CMyModelEntity, CBaseAnimating ); DECLARE_DATADESC(); CMyModelEntity() { m_bActive = false; } void Spawn( void ); void Precache( void ); void MoveThink( void ); // Входная функция void InputToggle( inputdata_t &inputData ); private: bool m_bActive; float m_flNextChangeTime; };
Заметьте что на этот раз мы наследуем наш класс от CBaseAnimating
, и на этот раз у нас появилось несколько новых функций. В закрытой (private) части класса содержатся две переменные: bool
- это тип переменной которая может принимать только значения правда/ложь (true/false) (1/0), а переменные типа float могут хранить в себе числовые значения с десятичной точкой.

InputToggle()
. Заставить объект двигаться можно только вызовом функции InputToggle()
(а не простой установкой значения m_bActive
в единицу) - это станет неплохой практикой после того как вы прочтёте статью.Имя объекта и таблица описания данных
LINK_ENTITY_TO_CLASS( my_model_entity, CMyModelEntity ); // Начало описания данных класса. BEGIN_DATADESC( CMyModelEntity ) // Сохранение/загрузка состояния объекта. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_flNextChangeTime, FIELD_TIME ), // Присвоение нашей входной функции имя в Hammer'e. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), // Объявление "мыслительной" (think) функции. DEFINE_THINKFUNC( MoveThink ), END_DATADESC()
Самое большое отличие от созданного нами ранее логического объекта в наличии макроса DEFINE_THINKFUNC
, который даёт движку Source знать о, так называемой "мыслительной" функции.
Определение модели
// Имя модели нашего объекта. #define ENTITY_MODEL "models/gibs/airboat_broken_engine.mdl"
Этот код жёстко задаёт модель , которую наш объект будет отображать в игре. Это статический кусок информации - не переменная: это значение не может меняться после компиляции кода. Путь к .mdl-файлу задаётся относительно директории игры (например hl2/
).

Функция Precache()
Мы добрались до первой функции. Функция Precache()
вызывается при появлении объекта на карте и гарантирует загрузку всего, что в ней перечислено до того как появится игрок (т.е. до того как он увидит мир и получит контроль над игрой). Смотрите рекомендации по прекэшированию для получения дополнительной информации.
Прекэширование получило отдельное имя потому что существуют другие типы "асинхронной " загрузки, которые могут выполняться после появления игрока.
//----------------------------------------------------------------------------- // Назначение: Прекэширование необходимых объекту ресурсов. //----------------------------------------------------------------------------- void CMyModelEntity::Precache( void ) { PrecacheModel( ENTITY_MODEL ); BaseClass::Precache(); }
Для данного объекта мы прекэшируем модель, которую будем использовать, а затем вызываем функцию прекэширования нашего базового класса (которая в случае класса CBaseAnimating
обеспечивает загрузку системы частиц для огня, т.к. каждый объект с моделью в игре может загореться). Другие команды прекэширования: PrecacheParticleSystem()
и PrecacheScriptSound()
Функция Spawn()
Функция Valve Spawn()
вызывается всякий раз когда создаётся новый экземпляр объекта, примерно так же, как и конструктор. Вообще можно использовать функцию Spawn()
вместо конструктора, но из соображений управляемости рекомендуется использовать и конструктор чтобы отделять инициализацию переменных от остального кода.
//----------------------------------------------------------------------------- // Назначение: Настраивает начальное состояние объекта. //----------------------------------------------------------------------------- void CMyModelEntity::Spawn( void ) { Precache(); SetModel( ENTITY_MODEL ); SetSolid( SOLID_BBOX ); UTIL_SetSize( this, -Vector(20,20,20), Vector(20,20,20) ); }
Сначала мы вызываем функцию Precache()
, а затем идут вызовы различных функций класса CBaseAnimating
. Функция SetModel()
задаёт модель объекта. В скобках ей передаётся определённый нами ранее путь к модели объекта. Функция SetSolid()
требует более подробных разъяснений. Она определяет форму, которую наш объект будет использовать для проверки на столкновение с другими объектами. Использование формы самой модели было бы очень, очень дорогим по отношению к ресурсам компьютера, поэтому движок Source предлагает несколько компромиссов:
SOLID_NONE
- "Прозрачный" (не твердый) объект.
SOLID_BBOX
- Используется ориентированный по осям ограничивающий короб.
SOLID_BSP
- Используется BSP-дерево для определения твёрдости (используется в брашах ).
SOLID_CUSTOM
- Объект использует свои собственные функции для проверки на столкновения.
SOLID_VPHYSICS
- Для определения точных столкновений используется встроенная в модель модель столкновений .
Эти пункты выбираются на уровне движка и авторы модов не могут добавлять или менять их. Мы используем параметр SOLID_BBOX
, который генерирует "ограничивающий короб ", размер которого движок определяет так, чтобы он охватывал всю модель. Более "дорогой" параметр SOLID_VPHYSICS
, который использует встроенную в модель модель столкновений , не поддерживает низкоуровневые функции перемещения, которые мы будем использовать для нашего объекта, поэтому мы его не применяем.
Вызов UTIL_SetSize()
нужен для того, чтобы сделать наш ограничивающий короб кубическим. Это сделано потому, что каким бы странным это не казалось, ограничивающий короб не может вращаться. Вам понадобится vphysics-обработка столкновений если вы захотите заставить модель вращаться. А по описанным выше причинам мы её не используем.

Spawn()
вызывается незамедлительно после создания объекта. Если это случится сразу же после создания карты нет гарантии того, что на карте уже появились другие объекты. Поэтому любой код, который предполагает что объект будет связываться с другими игровыми объектами становится ненадежным. В таких случаях используйте функцию Activate()
, которая всегда вызывается после появления всех объектов.Функция MoveThink()
"Мыслительная" функция (to think - думать) позволяет объекту "принимать решения" без побуждения со стороны внешнего кода. Вспомните про наш логический объект - он делал что-нибудь только при задействовании его входной функции; это не подходит для объектов, которые должны определенным образом перемещаться по миру. Мыслительная функция, если она присутствует, обычно является центральной функцией в коде объекта.
Сейчас мы создаём мыслительную функцию, которая будет вызываться движком с периодичностью 20 раз в секунду. Это может показаться большим числом, однако современные процессоры очень быстрые, а наш объект очень простой. Вы сможете добавить очень большое кол-во объектов CMyModelEntity
на карту без большой потери производительности.
//----------------------------------------------------------------------------- // Назначение: Мыслительная функция для случайного перемещения объекта по карте. //----------------------------------------------------------------------------- void CMyModelEntity::MoveThink( void ) { // Смотрим, должны ли мы снова поменять направление. if ( m_flNextChangeTime < gpGlobals->curtime ) { // Задаём случайное направление и скорость. Vector vecNewVelocity = RandomVector( -64.0f, 64.0f ); SetAbsVelocity( vecNewVelocity ); // Сменить направление снова через 1-3 секунды. m_flNextChangeTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 3.0f ); } // Поворот объекта туда, куда идет движение. Vector velFacing = GetAbsVelocity(); QAngle angFacing; VectorAngles( velFacing, angFacing ); SetAbsAngles( angFacing ); // Запускать эту функцию с частотой 20Hz SetNextThink( gpGlobals->curtime + 0.05f ); }
Несмотря на то, что в этой функции довольно много кода, она довольно простая. Когда случайный интервал времени подходит к концу объект выбирает новые случайные направление и скорость для продолжения движения. Также меняются углы положения объекта в пространстве, чтобы они соответствовали новому направлению движения. Это происходит для трех измерений.
Дополнительная информация:
gpGlobals ->curtime
возвращает время, в которое исполняется текущий код, как значение с плавающей точкой.Вектор
- это набор переменных, используемых для определения движения, которые содержат в себе данные о направлении и скорости. ФункцияSetAbsVelocity()
задаёт абсолютную скорость,QAngle
это просто угол - т.е. то же самое что вектор только без данных о скорости.QAngle
задаёт направление.- Функция
VectorAngles()
преобразует вектор (velFacing
) в угол (angFacing
). Не забывайте, что С++ очень строг в отношении типов: вам понадобятся утилитарные функции, такие какVectorAngles()
, чтобы осуществлять преобразования между двумя типами.
В конце мы вызываем функцию SetNextThink()
, которая указывает когда в следующий раз необходимо вызвать мыслительную функцию. Здесь мы задаём новую обработку через 0.05 секунд (1/20 часть), однако это число может быть различным для разных объектов. Важно понимать, что если не вызвать функцию SetNextThink() объект перестанет "мыслить".
Вы могли отметить, что здесь мы определяли новые переменные. Так же как и переменные, определенные внутри класса, переменные, определенные внутри функции, принадлежат данной функции. Они создаются каждый раз когда вызывается функция и уничтожаются по её завершении.

vecNewVelocity
. Попробуйте передать значение в функцию SetAbsVelocity()
без создания лишней переменной. Вспомните почему мы добавляем void
перед всеми нашими функциями.InputToggle()
Мы добрались до нашей последней функции. Это входная функция, которая будет включать и выключать передвижение объекта.
//----------------------------------------------------------------------------- // Назначение: Включать/выключать движение объекта. //----------------------------------------------------------------------------- void CMyModelEntity::InputToggle( inputdata_t &inputData ) { // Переключение состояния активности. if ( !m_bActive ) { // Начало мыслительного процесса. SetThink( &CMyModelEntity::MoveThink ); SetNextThink( gpGlobals->curtime + 0.05f ); // Начать летать. SetMoveType( MOVETYPE_FLY ); // Установка времени для смены направления и скорости. m_flNextChangeTime = gpGlobals->curtime; // Изменение переменной m_bActive для отображения текущего состояния. m_bActive = true; } else { // Перестать мыслить. SetThink( NULL ); // Перестать двигаться. SetAbsVelocity( vec3_origin ); SetMoveType( MOVETYPE_NONE ); m_bActive = false; } }
Здесь всё очень прямолинейно. Мы используем оператор if
чтобы определить текущее состояние флага m_bActive
. Знак восклицания означает "не": "Если m_bActive
не равно true (истина), то сделать следующее...". Позже мы используем оператор else
чтобы определить что мы будем делать в противоположном случае.
При активировании объекта мы начинаем его мыслительный процесс указывая движку Source какую мыслительную функцию использовать (по умолчанию это функция Think()
, однако она у нас отсутствует). Отметьте наличие знака & и пробелов около каждой скобки в аргументе. Затем мы сообщаем движку, что наш объект будет перемещаться по миру летая, хотя это скорее не полёт а "плавание", т.к. в движке Source отсутствует имитация полёта (возможно вы сможете её добавить?). И, конечно, в конце мы устанавливаем m_bActive
в true (истина) - само по себе это значение не поменяется!
После команды else
мы останавливаем объект. Мы сбрасываем мыслительную функцию в ноль (NULL
) чтобы остановить мыслительный процесс. Функция SetAbsVelocity()
устанавливает объект в его начало - используется вектор с нулевой скоростью. Тип передвижения устанавливается в MOVETYPE_NONE
, чтобы предотвратить любое перемещение, команда на которое может поступить извне. И в конце m_bActive
устанавливается в false (ложь).
Запись FGD
Данная FGD запись позволяет Hammer'у отображать модель и позволяет вам задать имя объекта и послать ему входное сообщение "Toggle".
@PointClass base(Targetname) studio("models/gibs/airboat_broken_engine.mdl")= my_model_entity : "Tutorial model entity." [ input Toggle(void) : "Toggle movement." ]
Работающий объект
Поэкспериментируйте со своим объектом. Вы можете использовать консольную команду ent_fire my_model_entity toggle
чтобы заставить его двигаться без вмешательства со стороны системы ввода/вывода карты. Вы можете отметить несколько особенностей:
- Объект не сталкивается с другими физическими объектами.
- Функции семейства
SetAbs*
не контактируют с физическими объектами. Если вы хотите симулировать физические столкновения вам придётся использовать другой метод перемещения. - Объект удаляется от меня каждый раз, когда я к нему подхожу.
- Не совсем понятно почему это происходит. Возможно
CBaseAnimating
считает что путь объекта блокирован?