После создания логической энтити в предыдущем примере, теперь создадим
энтить которая может двигаться, создавать коллизии с остальнымио
бъектами, и которая имеет визуальную компоненту (в данном случае,
модель). В этом примере мы создадим энтить которая отображается
используя модель, и перемещаются по миру случайным образом.
1) Создаем файл используя код из ..dllssdksdk_modelentity.cpp и добавляем его к проекту server
2) Добавляем определение класса
class CMyModelEntity : public CBaseAnimating
{
public:
DECLARE_CLASS( CMyModelEntity, CBaseAnimating );
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void MoveThink( void );
// Input function
void InputToggle( inputdata_t &inputData );
private:
bool m_bActive;
float m_flNextChangeTime;
};
Мы наследуем нашу энтить от класса
CBaseAnimating. Это позволяет использовать модели и анимацию. Также новыми для этой энтити являются функции
Spawn() и
Precache().
3) Определяем описание данных для данного класса
LINK_ENTITY_TO_CLASS( my_model_entity, CMyModelEntity );
// Start of our data description for the class
BEGIN_DATADESC( CMyModelEntity )
// Save/restore our active state
DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flNextChangeTime, FIELD_TIME ),
// Links our input name from Hammer to our input member function
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
// Declare our think function
DEFINE_THINKFUNC( MoveThink ),
END_DATADESC()
Подобно нашей логической энтити, мы должны объявить переменные используемые энтитью так чтоб движок знал их концепции.
Важно отметить что функция
MoveThink() должна быть объявлена как энтить с think-функцией таблице описания данных используя макрос
DEFINE_THINKFUNC.
4) Создаем Precache() функцию
#define ENTITY_MODEL "models/gibs/airboat_broken_engine.mdl"
void CMyModelEntity::Precache( void )
{
PrecacheModel( ENTITY_MODEL );
}
Precache() функция выполняет предзагрузку всех нужных файлов. Здесь мы также описываем модель которая представляет нашу энтить в мире.
В данном примере мы вызываем
PrecacheModel()
для предзагрузки модели. Без этого шага, модель энтити не появиться в
мире и движок будет жаловаться на отсутствующее предкэширование.
5)
Создаем Spawn() функцию
void CMyModelEntity::Spawn( void )
{
Precache();
SetModel( ENTITY_MODEL );
SetSolid( SOLID_BBOX );
UTIL_SetSize( this, -Vector(20,20,20), Vector(20,20,20) );
m_bActive = false;
}
Функция
Spawn() вызывается после создания
энтити. Эта функция может считаться игровым конструктором энтити.
Здесь энтить может установить ее начальное состояние, включая то что
модель использует, ее способы движения и твердость. Важно отметить что
функция
Spawn() вызывается немедленно после
размещения энтити в памяти и поскольку это происходит в самом начале
карты, нет гарантии что все остальные энтити к этому моменту были
успешно созданы. В связи с этим, любой код связывающий данную энтить с
поиском или связью с другими именованными энтитями должен производиться
в функции
Activate() данной энтити. Функция
Activate() вызывается когда все энтити создались и произвели запуски их
Spawn() функций. Поиск энтитей перед функцией
Activate() зависит от порядка создания энтитей и является ненадежным.
В приведенном ниже примере, сначала вызывается функция
Precache() для того чтобы удостовериться что все файлы будут корректоно прекэшированы. После чего, используется функция
SetModel() для установки модели определенной до этого.
Далее, устанавливается твердость энтити с помощью функции
SetSolid(). Здесь доступны несколько допустимых типов твердости:
SOLID_NOT
Отсутствует.
SOLID_BSP
Использует BSP дерево для определения твердости (используется для браш-моделей)
SOLID_BBOX
Использует ограничительный бокс углового центрирования.
SOLID_CUSTOM
Энтить определяет ее сеобственные функции для проверки колизий.
SOLID_VPHYSICS
Использует объект vcollide для проверки коллизий.
В данном примере, мы создаем энтить использующую ограничительный бокс. Функция
UTIL_SetSize() позволяет установить размер ограничительного бокса. Здесь мы устанавливаем куб размером 40x40x40.
6) Создаем the MoveThink() функцию
Энтити способны обновлять внутренее состояние и принимать решения с помощью функции
think,
которая вызывается с частотой указанной энтитью. Ниже мы создаем
think-функцию которая вызывается 20 раз в секунду. Эта функции
используется для случайного обновления передвижения и направления в
мире.
void CMyModelEntity::MoveThink( void )
{
// See if we should change direction again
if ( m_flNextChangeTime < gpGlobals->curtime )
{
// Randomly take a new direction and speed
Vector vecNewVelocity = RandomVector( -64.0f, 64.0f );
SetAbsVelocity( vecNewVelocity );
// Randomly change it again within one to three seconds
m_flNextChangeTime = gpGlobals->curtime + random->RandomFloat(1.0f,3.0f);
}
// Snap our facing to where we are heading
Vector velFacing = GetAbsVelocity();
QAngle angFacing;
VectorAngles( velFacing, angFacing );
SetAbsAngles( angFacing );
// Think every 20Hz
SetNextThink( gpGlobals->curtime + 0.05f );
}
While a lot of code is packed into this function, its outcome is fairly
simple: once a random time interval has elapsed, the entity will choose a
new, random direction and speed to travel at. It will also update its
angles to face towards this direction of travel.
The call to
SetNextThink() is important in
this function, because it tells the entity when next to think. Here it
is set to think again 1/20th of a second in the future. Most entities
will only need to think at a rate of 1/10th of a second, depending on
their behaviors. It’s important to note that failure to update the
next think time of the entity will cause it to stop thinking (which is
sometimes desired).
7)
Создаем ToggleInput() функцию
Для данной энтити мы используем ввод(input) для переключения ее
передвижения как включенного/выключенного. Для этого, мы объявляем
input-функцию подобной из предыдущего урока.
void CMyModelEntity::InputToggle( inputdata_t &inputData )
{
// Toggle our active state
if ( !m_bActive )
{
// Start thinking
SetThink( MoveThink );
SetNextThink( gpGlobals->curtime + 0.05f );
// Start flying
SetMoveType( MOVETYPE_FLY );
// Set our next time for changing our speed and direction
m_flNextChangeTime = gpGlobals->curtime;
m_bActive = true;
}
else
{
// Stop thinking
SetThink( NULL );
// Stop moving
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
m_bActive = false;
}
}
Для начала "работы" энтити используется функция
SetThink() в соединении с функцией
SetNextThink(). Она сообщает энтити использовать функцию
MoveThink() и вызывается через 1/20th секунды. Важно отметить что энтить может иметь любое количество think-функций и использовать
SetThink()
функцию для выбора между ними. Каждая энтить может иметь несколько
think-функций выполняющихся в одно и то же время используя
SetContextThink() скрытый в другом файле.
Также мы определяем тип движения энтити
MOVETYPE_FLY. Это позволяет энтити двигаться в заданном направлении без гравитации.
Во второй части энтити этой функции мы останавливаем энтить от движения. think-функция устанавливается в
NULL для остановки think-процесса. Ее тип движения также устанавлтвается
MOVETYPE_NONE to keep it from moving.
8)
Компиляция и запуск
Загрузите карту
sdk_entity_model. Вы можете
видеть двигатель летающий в пространтсве перед вами во время старта.
Станьте на платформу прямо расположеной по направлению движению от места
старта. Установить одну мз них для прекражения работы двигателя.
Попробуйте изменить модель использущуюся энтитью или изменить диапазg
частоты ее срабатывания. Также вы можете добавлять различные
обработчики behavior для ее
MoveThink() функции.