Pylos Game. Part N

Вот тут в предыдущей статье я написал, что реализую игру в Пилос на PascalABC.Net. Решил поделиться нюансами разработки под этот язык.

Вот уже полгода играем в эту игру с другом, но ощущается нехватка более полного погружения: отдачи от игры, банально простых звуков соударения мячика поверхностью поля. И мне сегодня захотелось добавить звук в игру, когда шарик ставится на поле. В целом задача несложная:
1) найти модуль для работы со звуком;
2) порыскать в интернете необходимый звук;
3) и добавить кусок кода для воспроизведения звука в событие добавления шара на поле.

Нашел удобный класс для запуска аудиофайлов в C#. System.Windows.Media.MediaPlayer. Да-да, именно там. Думаете я решил переписать свой проект на этот модный и востребованный язычок? Не тут-то было. Просто вот здесь, в мануале паскаля, в разделе Справочник - Модули - Библиотеки dll, написана классная вещь: "обеспечивают межъязыковое взаимодействие".

Pylos Game. Part N

У меня от этой фразы коленки дрожат от возбуждения. Помню сколько сил я в своё время потратил на диплом, пытаясь подружить код на C++ с кодом на Cи некоего института каких-то высоких технологий. При том что язык Си почти полностью является подмножеством языка С++ - т.е. любая программа написанная на Сишке, должна скомпилироваться и запуститься на Плюсах. Но это были такие страдания, что я до сих пор помню, как приходилось в каждый хедер файл добавлять директивы #ifndef, #define, #endif, потому что инженеры того института не удосужились этого сделать. Ну да ладно.

Так что же это значит? Это афигеть ни встать фича мирового уровня, означающая что всё что работает на шарпах (С#), можно подрубить и в наш востребованный на критику (а нахер нам в 2к24 паскаль) паскаль и использовать прямо из коробки. Берёшь и вызываешь те же самые методы. Я просто в неописуемом восторге. До сих пор не могу привыкнуть. Вот тут скидываю 7 строчек кода для запуска mp3шки в паскале:

{$reference PresentationCore.dll} // подключаю шарповскую библиотеку
uses GraphABC; // подключаю модуль для работы с оконными приложениями (чтобы приложение не завершилось раньше, чем закончится музыка)
begin // бедный бегин, которому так достаётся от критиканов паскаля, хотя такой информативный, уфф
var fsound := new System.Windows.Media.MediaPlayer; // класс из библиотеки System.Windows.Media - непонятно правда почему мы не её подгружаем, а PresentationCore. Однако не во всём хочется копаться, увы
fsound.Open(new System.Uri('D:\knock.mp3')); // загружаем наш аудиофайл в программу
fsound.Play; // и запускаем (по идее программа должна сразу завершиться, но GraphABC создаёт оконное приложение, которому передаёт выполнение главного потока, поэтому музыка будет играть и не останавливаться.
end. // более лаконичный напарник бегина

Проверив, что всё работает, я перешёл к поиску звука. Пришлось помучиться, чтобы найти звук, который бы меня удовлетворял бы на первых порах. Если честно, за полчаса я не нашёл звук соударения деревянных мячиков друг о друга. Но нашёл вот этот звук. Двойной стук ногой по полу. Ну что ж, нот бэд. Обрезал аудиофайлик в онлайн обрезалке, получив одинарный стук и пошёл вставлять код в свой проект.

Решил начать с того, чтобы сразу написать обёртку вокруг нашего MediaPlayer. Обожаю писать обёртачки, они классные. Создал объект, вызвал нужный метод и сидишь кайфуешь. Вся настройка, костыли и прочая магия происходит внутри. Инкапсуляция топ.

Pylos Game. Part N

Нашёл место, куда вставить проигрывание звука (m_soundPlayer имеет тип SoundPlayerT соответственно, объявил его в том же классе, где его и вызываю):

Pylos Game. Part N
Pylos Game. Part N

И словил вот такую ошибочку: Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток. Немного погуглив, я понял, что метод, в котором происходит добавления шара, вызывается в не в том потоке, в котором создался m_soundPlayer. В моей игре есть как минимум два потока: основной и обработчик событий. Объект класса GameViewT создаётся в основном (в том числе его содержимое). А вот метод в котором добавляется шар на поле уже вызывается в потоке обработчика событий (при нажатии мышкой на поле). Так что тут и происходит "конфликт интересов". Что же делать? Не хочу я создавать объект там же, где он вызывается, потому что тогда аудиофайл будет каждый раз заново подгружаться с жёсткого диска (хотя у меня ssd, всё равно, это лишняя трата времени и ресурсов, я так не хочу). Поискал в интернете примеры решения данной проблемы. Одно из решений, вынести в глобальную переменную и отовсюду ей пользоваться. "Глобальные переменные - зло" - кричит какой-то голос, но уже не так громко, как раньше, когда я только-только выпустился с универа. Поработав в разных местах, я заметил что другие уважающие себя разработчики не пренебрегают пользоваться ими, хоть и в изменённом виде: в виде синглтонов. Для меня по факту это всё та же глобальная переменная, просто в обёрточке. Так что я тоже, не брезгуя, создаю хэндлер-синглтон:

Pylos Game. Part N
Pylos Game. Part N

И вуаля:

Вроде ничего особенного, но я испытал оргазм. Как же это афигенно. Я бы не назвал себя диким любителем программирования, но такие мелочи дарят мне большие-маленькие радости в жизни.

P.S. Обманул я вас и себя: никакой там не синглтон у меня, а обычный статический класс со статическими полями и методами:)

33
Начать дискуссию