Вопросы по созданию приборов на С++ — Панели и приборы

seyco

 

Вот выдрал из одного своего проекта.

 

DXUTsound.cpp + DXUTsound.h — классы оболчки для работы с DirectSound

 

play_sounds.cpp + play_sounds.h — определение проигрываемых звуков, общие функции управления звуками и функции уже прогрывания некоторых конкретных звуков. Последние, естественно, менять на свои. Или Удалить. Для проигрывания любого звука используется функция

SoundFilePlay(int sound_type, BOOL flag_looping, DWORD pos)

 

Первый параметр — номер звука из массива структур. Для прямого доступа лучше использовать именованные константы, определяемые в play_sounds.h в enum ids_sounds

Второй — флаг проигрывания по кругу

Третий — позиция с которой будет проигрываться звук, по умолчанию — 0

 

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

 

естественно enum нужно привести в соответствие с массивом структур звуков.

 

В массиве структур — sounds описываются все звуки:

-Первое поле — обязательно NULL

-Второе — 0

-Третье — имя wav вайла. После к нему будет прибавлен путь до папки со звуками

-Четвертое — имя параметра в конфиге, для регулирования уровня данного звука. Можно иметь один параметр, но обязательно строку с его именем тогда задать для всех звуков.

-В последнем поле задается уровень от 0 до 100% по умолчанию.

 

В play_sounds.cpp

в стоках

sprintf_s(path_to_gau_cfg, «%s%s», path_to_sim, «gauges\…\….cfg»);

…

sprintf_s(path_to_sound, MAX_PATH, «%s%s», path_to_sim, «gauges\…\…\»);

там где … обязательно задать имена папок и файлов.

 

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

Хранить все свое стоит в папке с моделью, т.е. где-то в ..Aircraft{папка самолета}{например, в папке cfg_and_res}

Но тут, тогда нужно будет обязательно считать имя самолета из Aircraft и папку с самолетом называть именно так.

Другой вариант — хранить в определенной подпапке самолета и gau и уже получив, через функции API, полный путь к gau файлу, отсчитывать от него путь к конфигам и звукам.

 

Функция initialization_play_sounds() — должна обязательно вызываться в инициализации gau, т.е. в module_init

Функция deinitialization_play_sounds() — в module_deinit

 

Последние функции становятся доступными, если раскрыть макрос — GAUGE_TABLE_BEGIN()

 

В архиве есть папка libs

В ней заголовочный файл и lib из DirectX SDK 2007 для работы с DirectSound. (dxguid.lib — кажется не нужна)

После DirectX SDK 2007 уже DirectSound нет. Там уже другой, более крутой механизм проигрывания звуков.

Для сима вполне достаточно возможностей, которые предоставляет DirectSound из DirectX SDK 2007.

PlaySounds.zip


Edited by serg_p

Я целей и задач не знаю, но может быть что-то попроще можно использовать? Например, winmm какойнить…

http://msdn.microsof…0(v=vs.85).aspx

PlaySoynd проигрывает файл, если я правильно понял. Мне нужно чтобы при загрузке самолета все используемые файлы писались в буферы, и там хранились до выхода из сима(самолета). Моё мнение это должно повысить производительность. Может я не прав, поправте меня, я ведь только учусь.

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

Вот выдрал из одного своего проекта.

 

 

 

Спасибо большое, очень выручили, я как раз и не хотел .dll загружать WAVE файлами. Я даже понять не смог в моем вареанте WAVE файл компилируется или просто считывается каждый раз при запуске самолета из директории. Теперь буду спокойно разбираться.


Edited by seyco

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

 

Я уже понял, спабибо еще раз

Мысли вслух:

1. А SetCooperativeLevel(NULL,) реально работает?

2. Не вижу ничего плохого в хранении звука как ресурса. Придется только написать собственную разборку RIFFа для получения WAVEFORMATEX и остального. Некоторые мысли по этому поводу есть в DX SDK

3. У меня в June 2010 DX SDK (Latest вроде как) DSound вполне наличествует

4. Если пишем звук под FSX — GUIDы надо брать из fsx.cfg

5. Опять же, если пишем под FSX — неплохо бы отработать кнопочку Mute

… 1. А SetCooperativeLevel(NULL,) реально работает? …

Это ты, я так понял, про код seyco?

HWND hWnd = NULL;
...
if (FAILED(hRes = pDSound->SetCooperativeLevel(hWnd, DSSCL_NORMAL)))
return hRes;

Тут да — нехорошо.

 

…

2. Не вижу ничего плохого в хранении звука как ресурса. Придется только написать собственную разборку RIFFа для получения WAVEFORMATEX и остального. Некоторые мысли по этому поводу есть в DX SDK …

Если нужно заменить звук (на другой, ну мало ли, доработали вдруг), то что, будем компилить заново gau (dll)? Случай с reshack не рассматриваем, ибо если размер нового звука больше, воспользоваться reshack-ом нельзя. Да и думаю вообще, хранить данные и ресурсы в теле модуля — зло.

 

… 3. У меня в June 2010 DX SDK (Latest вроде как) DSound вполне наличествует …

Точно помню, когда хотел найти dsound.h — не нашел его в June 2010 DX SDK. А в доке было написано, что XAudio2 заменил Direct Sound с , точно не помню, кажется с версии DX SDK 2008. Сейчас некогда ставить June 2010 DX SDK, но вот, например, сейчас наткнулся на статью:

 

Программирование звука с использованием XAudio2

Программирование звука с использованием XAudio2.

…

Введение

…

Как заявляет сама Microsoft (смотреть тут), она больше не поддерживает DirectSound, и новые проекты, в зависимости от типа, должны использовать новое программное обеспечение. Для игр это XAudio2 и X3DAudio, для всего остального — Windows Core Audio.

…


Edited by serg_p

Игорь, а где ты это увидел?

Или ты про код seyco?

Именно там

 

Если нужно заменить звук (на другой, ну мало ли, доработали вдруг), то что, будем компилить заново gau (dll)? Случай с reshack не рассматриваем, ибо если размер нового звука больше, воспользоваться reshack-ом нельзя. Да и думаю вообще, хранить данные и ресурсы в теле модуля — зло.

Насчет замены — согласен, насчет зла — нет :)

 

 

Точно помню, когда хотел найти dsound.h — не нашел его в June 2010 DX SDK.

Вот сейчас посмотрел в свой DXSDK/Include — вот он, dsound.h, размером 113801 байт…

Ну значит я ошибся.

Хотя, MS точно не рекомендовала использовать его. Этот текст на англицком я точно видел. Сейчас некогда искать.

Спасибо, Сергей, за ответ, но может кто из знающих все же подскажет=)

Определение статической переменной:

ID id_agr72_sprite_y = -1;

 

В инициализации (лучше всего в module_init()):

// проверяем есть ли L:переменная, если нет - создаем
if((id_agr72_sprite_y = check_named_variable("agr72_sprite_y")) == -1)
   id_agr72_sprite_y = register_named_variable("agr72_sprite_y");

 

Чтение:

agr72_sprite_y_cbvar = get_named_variable_value(id_agr72_sprite_y);

 

Запись:

set_named_variable_value(id_agr72_sprite_y, agr72_sprite_y_cbvar);

 

// id_agr72_sprite_y — числовой идентификатор данной переменной

// agr72_sprite_y_cbvar — некая переменная C.

 

В деинициализации (лучше в module_deinit()):

unregister_all_named_vars ();

 

Лучше всего все ID и нэймы L:переменных запихнуть в массив структур (который инициализировать сразу же при определении, так удобней) и уже оперировать с ним (тут уже многие операции можно будет производить в циклах).

Можно сделать связанный список структур или класс, например, LVars, содержащий связанный список экземпляров классов, например, LVar.


Edited by serg_p

Спасибо Сергей! Приятно, когда есть отзывчивые люди.

serg_p

Спасибо!

Был вопрос про SimConnect в отдельной ветке.

 

Вот небольшой пример.

Мож пригодится.

Это для FSX.

 

Определены классы для работы с L:переменными, класс-родитель всех систем, пример топливной системы, пример системы управления центровкой.

 

Все файлы cpp — отдельно-компилируемые модули (далее по тексту просто модули).

 

Модуль sim (sim.h и sim.cpp) — все дела связанные с симконнектом. Там подробные комментарии (в файлах sim.h и sim.cpp). SimConnect.dll загружается явным образом из предопределенного места. Соответственно все вызовы функций SimConnect-а — через указатели, которые получаются через вызов функции API — GetProcAddress(). Прибегнуть к такому варианту зугрузки SimConnrect-а заставили постоянные траблы у пользователей с симконнектом.

SimConnect.dll от FSX+SP1 лежит в fsx_gaufor_simpanelSimConnectSP1

 

Уровень логики полностью отвязан от приборов визуализации. Но для того, что бы произошла инициализация — необходимо прописать в Panel.cfg хотя бы один прибор из dll. В примере это невидимый прибор — LogicLevel. Определен в главном модуле (main.cpp).

 

В main.rc определны свойсва dll.

Там все «NO NAME» и «No copyright».

Я специально все убрал.

 

Проект сделан в VC++ 2008.

 

Все из SDK для FSX + SP1

 

В проекте используется только одна конфигурация — Release. Debug я удалил. Но я изменил параметры компиляции так, что при компиляции и сборке генерится отладочная информация. Вся она записывается в файл fsx_gau.pdb. В fsx_gau.dll прописан только путь к fsx_gau.pdb. Такой вариант, по моему мнению, более удобен по сравнению с работой в двух конфигурациях проекта. Всегда есть релизная dll и всегда есть возможность ее отладить. Для отладки нужен только pdb файл. Естественно пользователям его не даем. Даем только dll. Но здесь есть минус. Пришлось отключиь оптимизацию. Помню читал статейку про то, что оптимизация — зло. Для меня было достаточно убедительно. Правда ссылку потерял. Это конечно все спорно, но я выбрал этот вариант работы и все gau делаю именно так, с генерацией отладочной информации и отключенной оптимизацией.


Edited by serg_p

Уровень логики полностью отвязан от приборов визуализации. Но для того, что бы произошла инициализация — необходимо прописать в Panel.cfg хотя бы один прибор из dll. В примере это невидимый прибор — LogicLevel. Определен в главном модуле (main.cpp).

 

Серёга, до сих пор? Можно же сделать как в кишках девятки с инициализацие в модуле и вклиниванием в общую цепь обновления гауг. Вроде даже обсуждали это уже. Я точно помню, что Денис говорил, что в десятке симконектом это можно сделать штатно.

Свой модуль, в FSX, ты должен класть инсталлером в определенное место и еще обязательно прописать его в xml файле, где прописываются внешние, по отношению к симу, модули. Т.е. твой инсталлер должен будет корректно модифицировать этот xml файл. Ну шо бы сим знал, что ету dll надо загружать при старте. Плюс твой модуль будет висеть всегда в памяти, нужен он, ненужен, он будет там пока не умрет сим.

 

Для меня все это зло!

 

Мне легче dll положить в папку Panel (ну или в предопределенную папку внутри папки модели) и прописать одну строчку с фейковым прибором в Panel.cfg (если в dll нет ни одного видимого прибора) и все, уровень логики заработал.

 

Тут принцип простой. Если в dll есть хотя бы один прибор, пусть даже невидимый, причем не важно, где в Panel.cfg он прописан, то этого достаточно, что бы сим загрузил эту dll и выполни ее инициализацию. После загрузки срабатывает module_init(), инициализируется SimConnect, происходит подписка на системное событие Frame — все, моя логика заработала и заработала совершенно независимо от уровня представления.

 

Т.е. достаточно просто добавить одну строку в Panel.cfg, не важно куда, и моя dll будет работать, как надо и загружаться только с моей моделью. Более того, повторюсь, если в dll есть хотя бы один видимый прибор, то фейковый, невидимый прибор уже не нужен.

 

 

Так, кстати, у меня сейчас работает и тушка в FS9. 5 gau (это сложилось исторически). Фейковые приборы тут даже и не нужны, я их все поубивал, ибо в каждой из gau куча видимых приборов. Т.е. загрузка и инициализация моих dll (gau) обязательно произойдет, а при их инициализации и запуск моего уровня логики (вклинивание в цепочку). Естественно все это ща в тушке работает не через симконнект, а сам знаешь чрез что. Более того, если я захочу все причесать и свести весь уровень логики в одну dll уровня логики, то и в этом случае я не буду ее делать как модуль. Мне не трудно прописать одну строку в Panel.cfg и при этом получить независимый уровень логики загружающийся вместе с моей моделью.

 

Ну и скажи, накой мне городить в тушке модуль?

 

Если конечно у меня не возникнет необходимость «ожить» до загрузки моей модели. Тут да — без модуля не обойтись!

 

Правда в связи с FSX смущает немного одно обсоятельство. Если у тебя, например, 3 dll, то в этом случае будут открыты три сессисии SimConnect-а. Но я думаю (я точно не проверял), что это совсем не накладно. Так шо особо-то причиной городить модуль,это обстоятельство тоже не может быть.

 

 

Ну а вообще, лучше все пробовать самому, а не говорить, что вот Денис говорил, а ты до сих пор. Т.е. попробуй сам и тогда ты точно будешь знать ценость любого совета для себя и соответственно для конкретного контекста.


Edited by serg_p

Ну а вообще, лучше все пробовать самому, а не говорить, что вот Денис говорил, а ты до сих пор. Т.е. попробуй сам и тогда ты точно будешь знать ценость любого совета для себя и соответственно для конкретного контекста.

 

Нуууу, не ругайся. Про части, называемые модуль в понятии сима, ты прав. Но я так обычную гаугу обозвал и попробовал на девятке такой «модуль». Грузится на ура только с тем самолётом, где используется, выгражуется тоже вместе с самолётом и иницилизации не надо через невидимый прибор. А как твой способ при загрузке полёта сразу ВК? Пойми мне правильно, я противник подходов «загрузи самолёт, вылези из него, залезь в цессну». Я не думаю, что эйсы в десятке сделали другое что-то, просто сделали доступ к этой цепочке документированым и всё. Но опять же, могу быть не прав, десятку в книжках видел.

Если уровень логики крутится не CalBack-ом прибора, а обработчиком события (что в FS9, что в FSX) — логика систем работает вне зависимости от того работают ли приборы или нет. Т.е. это означает, что вне зависимости от того, где ты загрузился в 2D или в ВК, твоя топливная система, гидросистема, курсовая система и т.д. — будут работать, тока надо потыкать тумблера. Т.е. здесь не получится так, что загрузившись в ВК, тебе обязательно надо показать главную панель, что бы все закрутилось.

 

Так у меня в примере.

 

Так сейчас у меня в тушке.

За исключение прибора ККП и озвучки экипажа. Думаю, даст Бог — доделаю. Сейчас делаю сохранение/восстановление состояния систем. Данные сначала запихиваю в map (контейнер map из STL, а затем сбрасываю в файл. При загрузке — из файла в map. Переменная в map-е ищется по ключу, по своему найму (поиск очень быстрый, в одноразовом событии загрузки полета — вполне все к месту). Далее извлекается Value и присваивается соответствующему полю, переменной.

 

STL — обалденная вещь. Жалею, что поздно ее для себя открыл.


Edited by serg_p

Ах вот почему у меня логика да и вообще все начинает работать только после переключения в 2D.

Вовремя прочитал, теперь надо думать как пользоваться обработчиком событий…


Edited by seyco

Если уровень логики крутится не CalBack-ом прибора, а обработчиком события (что в FS9, что в FSX) — логика систем работает вне зависимости от того работают ли приборы или нет. Т.е. это означает, что вне зависимости от того, где ты загрузился в 2D или в ВК, твоя топливная система, гидросистема, курсовая система и т.д. — будут работать, тока надо потыкать тумблера. Т.е. здесь не получится так, что загрузившись в ВК, тебе обязательно надо показать главную панель, что бы все закрутилось.

 

 

А у меня так и получилось, пока все 2D панели с тумблерами не откроешь (тумблеры уже включены из памяти cfg-а), приборы не хотят работать, даже после того, как всю логику работы перенес из одного прибора в логику симконнекта.

 

…

 

Проблему решил просто — открыл, закрыл все панели (кроме 1-й) в инициализации.


Edited by seyco

Это не решение.

 

Собственно это решение первое, что приходит в голову. Но это — не решение.

 

Тогда уж вот так:

пост #56

 

Но это тоже не решение.

Единственно правильное решение «вклится в цепочу».

Как?

Для FSX все показано и рассказано.

Здесь.

Для FS9 решение есть, но это через личку.

Причем, за неимением времени — передача библиотеки и небольшой пример.

Если уровень логики крутится не CalBack-ом прибора, а обработчиком события (что в FS9, что в FSX) — логика систем работает вне зависимости от того работают ли приборы или нет. Т.е. это означает, что вне зависимости от того, где ты загрузился в 2D или в ВК, твоя топливная система, гидросистема, курсовая система и т.д. — будут работать, тока надо потыкать тумблера. Т.е. здесь не получится так, что загрузившись в ВК, тебе обязательно надо показать главную панель, что бы все закрутилось.

 

Я вернусь к этой теме, когда перелезу на десятку. Как в девятке знаю, давно уже всё отвязали, всё крутится вне зависимости от способа загрузки самолёта и т.п.

 

STL — обалденная вещь. Жалею, что поздно ее для себя открыл.

 

STL — удобная вещь, не более. А скажи-ка мне, в какой файл ты сохраняешь это всё? В файл полёта или в отдельный? И на каком этапе происходит загрузка? Спрашиваю так ехидно, потому что сам малость посидел над этой темой (хотя и девятке).

… STL — удобная вещь, не более. А скажи-ка мне, в какой файл ты сохраняешь это всё? В файл полёта или в отдельный? И на каком этапе происходит загрузка? Спрашиваю так ехидно, потому что сам малость посидел над этой темой (хотя и девятке).

Я тоже это сейчас делаю в девятке (в ПТ Ту-154М). Узнаю имя полета, используя недокументированные возможности, ну, ту, классную библиотечку (причем, получаю полное имя файла). А дальше к этому полному имени просто прибавляю расширение .pt154msave и соответственно сбрасываю туда map при сохранении полета. При загрузке читаю свой файл (имя которого отличается от имени файла полета расширением) и закидываю в map.

 

Здесь собственно все прозрачно.

Единственное, есть две трудности, которые непреодолимы при использовании штатного SDK:

1. Получить имя файла текущего полета;

2. Сесть на событие сохранение полета.

Но все это позволяет преодолеть, как говорилось выше — «золотая библиотека» (для FS9).


Edited by serg_p

А у меня так и получилось, пока все 2D панели с тумблерами не откроешь (тумблеры уже включены из памяти cfg-а), приборы не хотят работать, даже после того, как всю логику работы перенес из одного прибора в логику симконнекта.

…

 

Проблему решил просто — открыл, закрыл все панели (кроме 1-й) в инициализации.

 

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

 

Но появился еще вопрос, на который сам пока не могу найти ответ.

В отрисовки приборов есть такая строка:

 

PELEMENT_HEADER tank_2_list_2[] = {&tank_2_03.header, NULL};

 

еще можно писать так:

 

PELEMENT_HEADER tank_2_list_2[] = {(PELEMENT_HEADER)&tank_2_03, NULL};

 

Объясните пожалуйста, в чем разница?

 

В СДК есть пример:

 

 

PELEMENT_HEADER cs_sliders_list[] = {&cs_slider_trim.header, &cs_slider_ailerons.header, &cs_slider_elevator.header, &cs_slider_rudder.header, NULL};

 

Если я правильно понял, это обозначение последовательности отрисовки деталей или нет?

… PELEMENT_HEADER tank_2_list_2[] = {&tank_2_03.header, NULL};

 

еще можно писать так:

 

PELEMENT_HEADER tank_2_list_2[] = {(PELEMENT_HEADER)&tank_2_03, NULL};

 

Объясните пожалуйста, в чем разница? …

PELEMENT_HEADER tank_2_list_2[] = {&tank_2_03.header, NULL};

Это определение статической переменной, которая тут же и инициализируется. Т.к. tank_2_list_2[] — это массив, то инициализация здесь производится не одним значением, а списком значений. NULL — это тоже значение. По этому значению симулятор определяет, что это конец массива. Стандартная практика в C.

 

{(PELEMENT_HEADER)&tank_2_03, NULL};

и

{&tank_2_03.header, NULL};

Это одно и тоже, за исключением того, что в верхней строке — явное приведение типа указателя, это хорошая практика, приведение типов осуществлять самому, т.е. явно. & — операция взятия адреса.

 

seyco

Это последний раз, когда я разъясняю элементарщину. И других программеров, жаждущих Вам объяснять элементарные вещи, Вы здесь не увидите. По этому еще раз настоятельно рекомендую Вам пройти, какую-нибудь книгу, прорешав все примеры, где поэтапно разжевываются все элементарные понятия. Без этого Вы будете постоянно спотыкаться.

 

Вот например

C++. Бархатный путь

 

На эту книгу я наткнулся, введя сейчас в гугле: «книга C++ элементарные понятия».

 

Про нет времени — не аргумент.

Тут либо делать на XML, на котором мало чего можно сделать, причем все делается через задницу, или использовать инструмент профессионалов, т.е. C/C++. В случае с C/C++ — все сложно, но других путей MS нам не дал. Было бы все по-другому, если бы было так, например: приборы описывались бы в XML, а логику их работы можно было бы реализовать в lua, это бы было бы здорово. Но в FS9/FSX — не так.


Edited by serg_p