Ru/HUD Elements
Часто в играх очень важным является передача информации игроку используя HUD (навигационный дисплей). Часто здесь отображается здоровье игрока, количество боеприпасов, или сообщение о текущей задаче. Обычно HUD содержит несколько различных элементов. Здесь будет описано как создавать эти элементы и использовать их для отображения информации пользователю.
Введение
HUD элементы используют библиотеку VGUI2 для рендеринга их состояния. Это позволяет им не только выглядеть и быть похожими на остальные VGUI элементам в игре, но также позволяет им использовать скриптовые, анимированные компоненты, значительно увеличивая их визуальное качество. Это требует наличия двух компонент на стороне клиента: объявление в коде и скриптовые файлы которые доступны клиенту для выполнения, скрипты - это файлы не входящие в код клиента.
Классы HUD элементов наследуются от базового класса CHudElement. Этот базовый класс работает с обновлением, отрисовкой и скрытием HUD элементов основанных на определенном игровом состоянии (HUD элемент может быть установлен или исчезнуть когда игрок умирает). Этот файл также интерпретирует файл HudLayout.res в папке /scripts вашего мода для определения позиций и поведений элементов. Большинсво элементов наследуются от базового класса vgui::Panel. Он предоставляет им примитивные панели на которых можно отрисовывать текст, формы или текстуры. Для более детальной информации по классу vgui::Panel, обратитесь к документации по VGUI2 предоставленной в данном SDK.
HUD Сообщения
Элементы используют простую систему для захвата сообщений отправленных из сервера. HUD элементы объявляют callback-функцию и связывают с ней соответствующее сообщение. Как только сообщение получено, callback-функция обычно читает какоето количество информации из сообщения и отображают или обновляют их визуальное состояние. К примеру - сообщение Damage отсылается сервером когда игрок получает повреждение. Сообщение кодируется количеством повреждения и расположением. HUD элемент Health получает сообщение и проигрывает анимации также выдавая ее числовое значение для отображения нового количества здоровья игрока.
Обработчик сообщения объявляется в HUD элементе следующим макросом:
DECLARE_HUD_MESSAGE( CMyHUDClass, MyHUDMessage );
Первый параметр указываает класс объявляющий обработчик сообщения, второй это данное сообщение. Макрос приводит имя сообщения к функции которая должна быть описана в классе HUD элемента. Поэтому, создаваемая callback-функция должна быть такой:
void MsgFunc_MyHUDMessage( bf_read &msg );
Прототип и тело функции должны содержать данное объявление. Класс bf_read это буффер данных с различными методами запросов. Он используется для передачи неформатированных данных между сервером и клиентом. Сервер использует класс bf_write для передачи данных.
HUD элемент должен также включать определение макроса HOOK_HUD_MESSAGEобычно вызываемый функцией Init() HUD элемента. Продолжая наш пример описанный выше, объявление будет такое:
HOOK_HUD_MESSAGE( CMyHUDClass, MyHUDMessage );
Данный макрос регистрирует сообщение и связывает его с callback-функцией описанной раньше. Ошибочное включение данного объявления приведет к ошибке когда сервером будет отослано сообщение.
Отправка сообщения из сервера
Для правильного приема сообщений клиентом, они должны быть объявлены и отправлены сервером. Это совершает функция Register() в синглетон-классе usermessages.
void CUserMessages::Register( const char *name, int size )
|
| Эта функция создает описание сообщения и удерживает его на протяжении существования сессии. Когда сообщение отправляется, имя (name) указанное сдесь использется для идентификации сообщения как такого что было отправлено клиенту.
name
size
|
Все сообщения должны быть объявлены в глобальной функции RegisterUserMessages(). Эта функция вызывается во время создания синглетон-класса. Не объявленные сообщения не будут удачно приняты клиентом и могут вызвать ошибку в случае попытки такой отправки.
Если были соблюдены все шаги, HUD элемент должен иметь работающую инфраструктуру для отправки и приема сообщений от сервера к клиенту. Для отправки сообщения, используются функции UserMessageBegin(), MessageEnd(), и другие поддерживаемые макросы описанные ниже.
Функция UserMessageBegin() определена как:
void UserMessageBegin( IRecipientFilter& filter, const char *messagename )
|
Эта функция создает пользовательское сообщение данного типа по имени (name), и приготавливается к получению данных от пользователя. Фильтр (filter) может быть любого типа IRecipientFilter type (CSingleUserRecipientFilter, CBroadcastRecipientFilter, и т.д.) как описано в ../dlls/recipientfilter.h.
filter
messagename
|
Следующие макросы предоставляют функциональность для записи данных в поток отправляемый клиенту. Они должны быть приняты и обработаны в порядке отправления. Макросы записываются так:
... WRITE_BYTE( m_uchMyByte ); WRITE_VEC3COORD( m_vecMyOrigin ); WRITE_BOOL( m_bMyState ); ...
Ниже приводится описание всех доступных макросов для отправки данных в поток:
WRITE_BYTE |
Один байт |
WRITE_CHAR |
Один символ |
WRITE_SHORT |
Одно короткое целое (short) |
WRITE_WORD |
Одно слово (два байта) |
WRITE_LONG |
Одно длинное целое |
WRITE_FLOAT |
Одно с плавающей запятой |
WRITE_ANGLE |
Беззнаковй 8-битный угол |
WRITE_COORD |
Сжатое значение координат |
WRITE_VEC3COORD |
Сжатое значение координат из типа Vector |
WRITE_VEC3NORMAL |
Сжатое значение нормали из типа Vector |
WRITE_ANGLES |
Сжатое угловое значение из типа Vector |
WRITE_STRING |
Строка символов |
WRITE_ENTITY |
Индекс энтити (короткое целое, short) |
WRITE_BOOL |
Один бит (булевое значение) |
WRITE_UBITLONG |
Значение беззнаковый длинный бит |
WRITE_SBITLONG |
Значение знаковый длинный бит |
WRITE_BITS |
Несколько битовых значений, которое передаются как параметр |
За WRITE_ макросом, сообщение должно быть завершено функцией MessageEnd().
Получение сообщения клиентом
Как только сообщение было отправлено сервером, клиент принимает его через callback-функцию закрепленной за этим сообщением. Принимающей callback-функции передается переменная класса bf_read которая содержит данные полученные из сервера. Класс содержит специальные функции для чтения форматированных данных из потока. И опять же, данные должны быть считаны в том порядке в котором отправлялись.
Теперь, когда данные отправлены и получены между клиентом и сервером, функции отрисовки (как описано в документе по VGUI2) могут быть использованы для отрисовки любой информации которая будет нужна на основании полученных данных.
Чтобы поймать это сообщение на клиентской стороне вы должны обработать его в нем, добавление обработки сообщения требует чтобы экземпляр вашего класса не был уничтожен.
class CHudThingy : public CHudElement , public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CHudThingy, vgui::Panel ); // ЭТО ВАЖНО
....
void MsgFunc_SayText(bf_read &msg) { // Делаем что-нибудь с сообщением};
}
DECLARE_HUD_MESSAGE( CHudThingy, SayText );
// Инициализиреум дополнительный чат
void CHudAdvancedChat::Init( void )
{
...
HOOK_HUD_MESSAGE( CHudThingy, SayText );
}
Код примера
Пример HUD элемента может быть найден в следующих файлах, поставляемых с примером приложения:
../cl_dll/sdk/sdk_hud_message.cpp
../dlls/sdk/sdk_env_message.cpp
../game_shared/sdk/sdk_usermessages.cpp
Отображение пользовательских сообщений в игре (кодирование серверных плугинов)
В этом случае эта функция может быть добавлена в ваш проект, она полностью снабжена коментариями.
void YourPlugin::SayTextMsg(int PlayerIndexN, char const *Message)
{
MRecipientFilter filter;
if(PlayerIndexN == 0)
{
filter.AddAllPlayers(MaxClients); // мы бирем maxclients из ServerActivate Void
}
else
{
filter.AddRecipient(PlayerIndexN);//добавления игрока
}
bf_write *pWrite=engine->UserMessageBegin(&filter, 3);//3 для Say_Text
if( !pWrite )
{
//TODO: Действие производимое когда что-то идет нетак
}
else
{
pWrite->WriteByte(PlayerIndexN);// Индекс игроков, чтобы отправить глобальное сообщение серверу сделайте его 0
pWrite->WriteString(Message);//само сообщение
pWrite->WriteByte(0);//0 для фразы, 1 для игнорирования
engine->MessageEnd();//закончим
}
}
Использование:
void YourPlugin::ClientCommand(edict_t *pEntity)
{
const char *pcmd = m_Engine->Cmd_Argv(0);
if ( !pEntity || pEntity->IsFree() )
{
//TODO
}
if ( FStrEq( pcmd, "ClientMsg" ) )
{
SayTextMsg( engine->IndexOfEdict(pEntity), "Hello World!")// только человек кто ввел команду видит это!
}
else if ( FStrEq( pcmd, "SayServer" ) )
{
SayTextMsg( 0, "Hello World!")// все в игре видят это:)
}
...
У вас должен быть модифицированный RecipientFilter, пожалуйста смотрите здесь для дополнительной информации.