305
Пример кода
Вы уже видели код. Он не идеален, но в нем правиль-
но реализована базовая функциональность — нужный
нам публичный API и правильные низкоуровневые вы-
зовы аудиосистемы. Осталось просто исправить некото-
рые проблемы.
Первая — это
блокировки
нашим API. Когда одна
часть кода воспроизводит звук, другие части не могут
делать ничего, пока метод
playSound()
не закончит за-
грузку ресурсов и не начнет издавать звуки через динамик.
Мы хотим отложить эту работу, чтобы
playSound()
быстрее возвращал управление. Нам нужно абстракт-
ный запрос
материализовать
. Нам потребуется неболь-
шая структура, которая будет хранить детали ожидаю-
щего обработки запроса, чтобы мы могли использовать
их позже:
struct PlayMessage
{
SoundId id;
int volume;
};
Затем нам нужно выделить
Audio
немного места
для хранения информации об ожидающем обработки
запросе на воспроизведение. И тут ваш преподаватель
по программированию посоветовал бы вам использо-
вать какую-нибудь существующую структуру данных,
например фибоначчиеву кучу, или список с пропусками,
или даже, черт побери,
связанный
список. Но на практи-
ке старый добрый массив лучше всего подходит для хра-
нения однородных данных.
• Никакого динамического выделения памяти.
• Никаких накладных расходов на хранение допол-
нительной информации или указателей.
• Удобное для кэша использование памяти.
Людям, изучающим
алгоритмы, платят
за анализ новых струк-
тур данных. Они не за-
интересованы в том,
чтобы придерживаться
классики.
306
Очередь событий (Event Queue) —
Паттерны программирования игр
Давайте сделаем это:
class Audio
{
public:
static void init() {numPending_ = 0;}
// 6 …
private:
static const int MAX_PENDING = 16;
static PlayMessage pending_[MAX_PENDING];
static int numPending_;
};
Мы можем задать размер массива для худшего из воз-
можных случаев. Чтобы воспроизвести звук, мы просто
помещаем новое сообщение в конце:
void Audio::playSound(SoundId id, int volume)
{
assert(numPending_ < MAX_PENDING);
pending_[numPending_].id = id;
pending_[numPending_].volume = volume;
numPending_++;
}
Это позволит
playSound()
вернуть управление по-
чти мгновенно, но нам все еще нужно воспроизвести
звук. Код нужно где-то разместить — конечно же, в ме-
тоде
update()
:
class Audio
{
public:
static void update()
{
for (int i = 0; i < numPending_; i++)
{
ResourceId resource = loadSound(
pending_[i].id);
int channel = ndOpenChannel();
Больше о том, что зна-
чит «удобный для кэша»,
вы найдете в главе «Ло-
кальность данных (Data
Locality)» (с. 343).
Как подразумевает на-
звание, это паттерн Ме-
тод Update (с. 181)
Do'stlaringiz bilan baham: |