Введение в Simple DirectMedia Layer
Что это такое?
SDL - бесплатный кроссплатформенный мультимедийный программный интерфейс приложения
- http://www.libsdl.org/
Используется для создания игр, библиотек для игр, демонстрационных программ, эмуляторов, MPEG плэйеров и других программ.
[Назад] Содержание [Дальше]
Что она может делать?
Вы можете установить строку заголовка вашего приложения и иконку, используя функции SDL_WM_SetCaption() и SDL_WM_SetIcon() соответственно.
Используйте SDL_PeepEvents() для поиска событий конкретного типа в очереди событий.
Используйте функции SDL_LockAudio()
и SDL_UnlockAudio() для синхронизации доступа к данным, разделяемым между звуковой функцией обратного вызова и остальной частью программы.
Если вы передадите NULL в качестве дескриптора CD-ROM в любую функцию CD-ROM API, это будет действовать как последний открытый CD-ROM.
Не используйте функции библиотеки C касающиеся ввода/вывода и управления памятью из потоков, если вы можете избежать их– они блокируют ресурсы, используемые другими потоками.
Вы можете спокойно заменить Win32 GetTickCount() на SDL_GetTicks().
При чтении ваших файлов данных вам может потребоваться перестановка байтов в 16-битной графике.
[Назад] Содержание [Дальше]
Инициализация библиотеки
SDL_INIT_AUDIO
SDL_INIT_VIDEO
SDL_INIT_CDROM
SDL_INIT_TIMER
Используйте SDL_Quit() для выгрузки библиотеки и освобождения ресурсов по окончанию работы.
SDL динамически загружает свои библиотеки из стандартного расположения системных библиотек. Используйте функцию SDL_SetLibraryPath() для использования альтернативного размещения динамических библиотек, поставляемых с вашим приложением.
#include
[Назад] Содержание [Дальше]
Использование CD-ROM audio
Во можете узнать количество CD-Rom устройств в системе вызвав функцию SDL_CDNumDrives() и затем открыть один из них, вызвав SDL_CDOpen().
Основной (по умолчанию) CD-ROM всегда 0. Заметьте, что CD-ROM устройство может быть открыто, даже если в дисковод не вставлен диск.
Для определения текущего состояния устройства вы можете воспользоваться функцией SDL_CDStatus(). По завершении использования CD-ROM устройства, закройте его функцией SDL_CDClose().
Вы можете определить системное имя CD-ROM дисковода используя SDL_CDName() функцию.
{ SDL_CD *cdrom; if ( SDL_CDNumDrives() > 0 ) { cdrom = SDL_CDOpen(0); if ( cdrom == NULL ) { fprintf(stderr, "Не могу открыть CD-ROM по умолчанию: %s\n" SDL_GetError()); return; } ... SDL_CDClose(cdrom); }}
CD-ROM диски используют время в MSF формате (mins/secs/frames = минуты/секунды/кадры) или непосредственно в кадрах. Кард - это стандартная единица времени для CD, равная 1/75 секунды. SDL использует кадры вместо MFS формата когда определяет длину трека и текущее положение, но вы всегда можете конвертировать один формат в другой с помощью макросов FRAMES_TO_MSF() и MSF_TO_FRAMES().
SDL не обновляет автоматически информацию с структуре SDL_CD пока вы не вызовете SDL_CDStatus(), таким образом вы должны вызывать SDL_CDStatus каждый раз, когда вам надо узнать, что за диск вставлен в дисковод и какие треки на нем доступны. Заметьте, что первый трек имеет индекс 0.
SDL имеет две функции для воспроизведения CD-ROM. Вы можете как играть определенный трек, используя функцию SDL_CDPlayTracks(), так и устанавливать смещение от начала всего диска, используя SDL_CDPlay().
SDL не предусматривает автоматическое оповещение при вставке CD или при завершении воспроизведения. Для отслеживания этих ситуаций вы должны периодически производить опрос состояния устройства с помощью SDL_CDStatus().
Чтобы понять, какие треки - аудио треки, а какие - треки данных, вы можете прочитать cdrom->tracks[track].type, и сравнить с SDL_AUDIO_TRACK и SDL_DATA_TRACK.
void PlayTrack(SDL_CD *cdrom, int track){ if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) { SDL_CDPlayTracks(cdrom, track, 0, track+1, 0); } while ( SDL_CDStatus(cdrom) == CD_PLAYING ) { SDL_Delay(1000); }}
[Назад] Содержание[Дальше]
Использование таймеров
SDL_GetTicks() говорит, сколько миллисекунд прошло с произвольно выбранного момента.
В основном, когда пишутся игры, оптимальным вариантом является движение объектов, основанное на времени (чем на количестве кадров). То есть, при очередном проходе по циклу следует смещать объект не на какое-то постоянное значение, а на значение, пропорциональное прошедшему с последнего прохода по циклу времени. Это позволит держаться игре с постоянной скоростью как на мощных, так и на слабых машинах.
#define TICK_INTERVAL 30 Uint32 TimeLeft(void){ static Uint32 next_time = 0; Uint32 now; now = SDL_GetTicks(); if ( next_time <= now ) { next_time = now+TICK_INTERVAL; return(0); } return(next_time-now);}
Функция SDL_Delay() позволяет установить задержку на какое-то количество миллисекунд.
Поскольку ОС, поддерживающие SDL, в большинстве своем многозадачные, вызов это функции не дает гарантии, что программа задержится ровно на данное время! Чаще эта функция просто вызывается для небольшой задержки.
Большинство ОС имеют планировщик временных участков с интервалом около 10 миллисекунд. Вы можете использовать SDL_Delay(1) как способ оставить CPU текущий временной участок, позволив выполниться остальным потокам. Так чаще всего делают, когда необходимо выпонять свой поток в быстром непрерывном цикле, но также необходимо, чтобы и другие потоки (например, аудио) тоже выполнялись.
{ while ( game_running ) { UpdateGameState(); SDL_Delay(TimeLeft()); }}
[Назад] Содержание [Дальше]
На каких платформах выполняется?
Вы можете получить часть скрытого интерфейса драйвера SDL через функцию SDL_GetWMInfo(). Это позволит вам делать вещи наподобие удаления оформления окна и задавать иконку вашего приложения.
Вы должны периодически вызывать функции обработки событий SDL из вашего главного потока для очистки очереди сообщений Windows, позволяя приложению отзываться на системные сообщения.
Linux и BeOS поддерживают флаг SDL_INIT_EVENTTHREAD, который при передачи в SDL_Init() запрашивает запуск обработки очереди сообщений в отдельном потоке. Это полезно для окраски курсора, реагируя, таким образом, когда приложение занято.
[Назад] Содержание [Далее]
Независимость от порядка байтов
Препроцессор C заменяет SDL_BYTEORDER на SDL_LIL_ENDIAN (младше-конечные) или SDL_BIG_ENDIAN (старше-конечные), в зависимости от порядка байтов в текущей системе.
Младше-конечные системы это те, которые пишут данные на диск размещая так:
[младшие байты] [старшие байты]
Старше-конечные системы пишут данные на диск так:
[старшие байты] [младшие байты]
В системах x86 младший байт-первый (little-endian), PPC наоборот (big-endian).
#include "SDL_endian.h" #if SDL_BYTEORDER == SDL_LIL_ENDIAN#define SWAP16(X) (X)#define SWAP32(X) (X)#else#define SWAP16(X) SDL_Swap16(X)#define SWAP32(X) SDL_Swap32(X)#endif
SDL предоставляет совокупность быстродействующих макросов в SDL_endian.h, SDL_Swap16() и SDL_Swap32(), которые производят обмен данными с указаным вами порядком байтов. Также определены макросы для обмена данными со специфичным для данной системы порядком байтов.
Если вам нужно знать порядок байтов в текущей системе, но не нужны все перестановочные функции, подключите SDL_byteorder.h вместо SDL_endian.h
#include "SDL_endian.h" void ReadScanline16(FILE *file, Uint16 *scanline, int length){ fread(scanline, length, sizeof(Uint16), file); if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) { int i; for ( i=length-1; i >= 0; --i ) scanline[i] = SDL_SwapLE16(scanline[i]); }}
[Назад] Содержание
О разработчике:
Sam Lantinga, Инженер по программному обеспечению в Blizzard Entertainment.Фоновая деятельность:
Связь:
Перевод на русский язык:
mailto:shd@bk.ru
http://morgeyz.narod.ru/
mailto:sashnov@ngs.ru
http://sashnov.chat.ru/
[Назад] Содержание ] [Далее]
Потоки
Создание потока делается через вызов функции SDL_CreateThread(). После успешного возврата из функции ваша функция теперь запущена одновременно с основным приложением в своем контексте задачи (стек, регистры и прочее) и может получать доступ к памяти и открытым файлам используемыми в основном приложении.
Второй аргумент для SDL_CreateThread() передается как параметр для порожденного потока. Вы можете использовать его для передачи значений в стек или только как указатель на данные, которые будут использоваться в потоке.
#include "SDL_thread.h" int global_data = 0; int thread_func(void *unused){ int last_value = 0; while ( global_data != -1 ) { if ( global_data != last_value ) { printf("Data value changed to %d\n", global_data); last_value = global_data; } SDL_Delay(100); } printf("Thread quitting\n"); return(0);} { SDL_Thread *thread; int i; thread = SDL_CreateThread(thread_func, NULL); if ( thread == NULL ) { fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError()); return; } for ( i=0; i<5; ++i ) { printf("Changing value to %d\n", i); global_data = i; SDL_Delay(1000); } printf("Signaling thread to quit\n"); global_data = -1; SDL_WaitThread(thread, NULL);}
|
Вы можете предотвращать доступ к ресурсу более чем из одного потока с помощью создания семафора и получать доступ к ресурсу, окружая доступ вызовом блокировки (SDL_mutexP()) и разблокировки (SDL_mutexV()). |
Совет: Любые данные, которые могут быть доступны более чем одному потоку одновременно, должны быть защищены семафорами. |
|
Пример: #include "SDL_thread.h"#include "SDL_mutex.h" int potty = 0;int gotta_go; int thread_func(void *data){ SDL_mutex *lock = (SDL_mutex *)data; int times_went; times_went = 0; while ( gotta_go ) { SDL_mutexP(lock); /* Lock the potty */ ++potty; printf("Thread %d using the potty\n", SDL_ThreadID()); if ( potty > 1 ) { printf("Uh oh, somebody else is using the potty!\n"); } --potty; SDL_mutexV(lock); ++times_went; } printf("Yep\n"); return(times_went);} { const int progeny = 5; SDL_Thread *kids[progeny]; SDL_mutex *lock; int i, lots; /* Create the synchronization lock */ lock = SDL_CreateMutex(); gotta_go = 1; for ( i=0; i |
[Назад] Содержание [Дальше]
Для ожидания события воспользуйтесь функцией
|
Для ожидания события воспользуйтесь функцией SDL_WaitEvent(). |
Совет: SDL имеет международную поддержку клавиатуры, трансляцию событий клавиатуры и помещение эквивалента UNICODE в event.key.keysym.unicode. Так как это требует дополнительной обработки, это должно быть разрешено функцией SDL_EnableUNICODE(). |
|
Пример: { SDL_Event event; SDL_WaitEvent(&event); switch (event.type) { case SDL_KEYDOWN: printf("The %s key was pressed!\n", SDL_GetKeyName(event.key.keysym.sym)); break; case SDL_QUIT: exit(0); }} |
|
Для опроса событий используйте SDL_PollEvent(). |
Совет: Вы можете считывать события из очереди без их удаления оттуда, используя параметр SDL_PEEKEVENT в функции SDL_PeepEvents(). |
|
Пример: { SDL_Event event; while ( SDL_PollEvent(&event) ) { switch (event.type) { case SDL_MOUSEMOTION: printf("Mouse moved by %d,%d to (%d,%d)\n", event.motion.xrel, event.motion.yrel, event.motion.x, event.motion.y); break; case SDL_MOUSEBUTTONDOWN: printf("Mouse button %d pressed at (%d,%d)\n", event.button.button, event.button.x, event.button.y); break; case SDL_QUIT: exit(0); } }} |
|
В дополнение к обработке событий непосредственно, каждый тип события имеет функцию, которая позволяет проверить состояние приложения. Если вы используете только эту функцию, вы должны игнорировать все события от функции SDL_EventState() и периодически вызывать SDL_PumpEvents() для обработки приложением событий. |
Совет: Вы можете скрывать и показывать системный курсор мыши используя SDL_ShowCursor(). |
|
Пример: { SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);} void CheckMouseHover(void){ int mouse_x, mouse_y; SDL_PumpEvents(); SDL_GetMouseState(&mouse_x, &mouse_y); if ( (mouse_x < 32) && (mouse_y < 32) ) { printf("Mouse in upper left hand corner!\n"); }} |
Видео
Просто выберите свое любимое разрешение и глубину цвета и установите его!
Вы можете узнать оптимальную глубину, поддерживаемую аппаратурой, используя функцию SDL_GetVideoInfo().
Совет #2:
Вы можете получить список всех поддерживаемых разрешений и соответствующих им глубин цвета, используя функцию SDL_ListModes().
{ SDL_Surface *screen; screen = SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE); if ( screen == NULL ) {// Если установить разрешение не удалось fprintf(stderr, "Невозможно установить разрешение 640x480: %s\n", SDL_GetError()); exit(1); }}
Рисование точек сводится к прямой записи в видеобуфер и вызову функции обновления содержимого экрана.
Если вам предстоит рисовать много объектов, лучше заблокировать (lock) экран единожды перед рисованием, вывести все объекты на экран, сохраняя список областей, требующих обновления, и разблокировать (unlock) экран вновь перед обновлением экрана.
Рисование точки на экране необходимым цветом
void DrawPixel(SDL_Surface *screen, Uint8 R, Uint8 G, Uint8 B){ Uint32 color = SDL_MapRGB(screen->format, R, G, B); if ( SDL_MUSTLOCK(screen) ) { if ( SDL_LockSurface(screen) < 0 ) { return; } } // Смотрим, сколькими байтами кодируется каждый пиксель (bytes per pixel, bpp) switch (screen->format->BytesPerPixel) { case 1: { /* Если 8-bpp */ Uint8 *bufp; bufp = (Uint8 *)screen->pixels + y*screen->pitch + x; *bufp = color; } break; case 2: { /* Если 15-bpp или 16-bpp */ Uint16 *bufp; bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; *bufp = color; } break; case 3: { /* Медленный 24-bpp режим, обычно не используется */ Uint8 *bufp; bufp = (Uint8 *)screen->pixels + y*screen->pitch + x; *(bufp+screen->format->Rshift/8) = R; *(bufp+screen->format->Gshift/8) = G; *(bufp+screen->format->Bshift/8) = B; } break; case 4: { /* Наверное, 32-bpp */ Uint32 *bufp; bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x; *bufp = color; } break; } if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } SDL_UpdateRect(screen, x, y, 1, 1);}
|
Для вашего удобства, SDL предоставляет единственную функцию для загрузки изображений, SDL_LoadBMP(). Библиотека для загрузки изображений может быть найдена в архиве с демонстрациями SDL (SDL demos archive). Вы можете отобразить загруженную картинку, используя SDL_BlitSurface() для копирования ее в видеобуфер. SDL_BlitSurface() автоматически усекает копируемую область, которая должна быть передана SDL_UpdateRects() для обновления изменившейся части экрана. |
Совет #1: Если вам необходимо показывать некоторую картинку много раз, вы можете ускорить вывод, конвертировав ее в формат дисплея. Для этого используется функция SDL_DisplayFormat() Совет #2: Большинство изображений спрайтов имеют прозрачный фон. Вы можете разрешит копирование с прозрачностью (colorkey blit) функцией SDL_SetColorKey(). |
|
Пример: void ShowBMP(char *file, SDL_Surface *screen, int x, int y){ SDL_Surface *image; SDL_Rect dest; /* Загрузим BMP файл на поверхность */ image = SDL_LoadBMP(file); if ( image == NULL ) { fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError()); return; } /* Блитируем (копируем) на экранную поверхность. Поверхность не должна быть заблокирована (locked). */ dest.x = x; dest.y = y; dest.w = image->w; dest.h = image->h; SDL_BlitSurface(image, NULL, screen, &dest); /* Обновим измененную часть экрана */ SDL_UpdateRects(screen, 1, &dest);} |
Звук
Вам необходима callback-функция, которая будет смешивать ваши аудио данные и помещать их в аудио поток. Затем необходимо выбрать желательный аудио формат и частоту, и открыть аудио устройство.
Воспроизведение не начнется до тех пор, пока вы не вызовете SDL_PauseAudio(0). Эта функция позволяет выполнить действия, которые необходимо выполнить до вызова CallBack функции. Как только вы завершите использование звука, закройте его функцией SDL_CloseAudio().
Если ваша программа оперирует с различными звуковыми форматами, передайте вторым параметром указатель на SDL_AudioSpec в SDL_OpenAudio(), чтобы получить текущий аппаратный аудио формат. Если же вы оставите второй указатель равным NULL, аудио данные будут конвертированы в аппаратный аудио формат во время выполнения.
#include "SDL.h"#include "SDL_audio.h"{ extern void mixaudio(void *unused, Uint8 *stream, int len); SDL_AudioSpec fmt; /* Установить 16бит стерео с 22Khz */ fmt.freq = 22050; fmt.format = AUDIO_S16; fmt.channels = 2; fmt.samples = 512; /* Хорошее значение для игр */ fmt.callback = mixaudio; fmt.userdata = NULL; /* открыть аудио усройство и начать воспроизведение! */ if ( SDL_OpenAudio(&fmt, NULL) < 0 ) { fprintf(stderr, "Не могу открыть аудио: %s\n", SDL_GetError()); exit(1); } SDL_PauseAudio(0); ... SDL_CloseAudio();}
Для вашего удобства, SDL предоставляет единственную функцию для загрузки звука, SDL_LoadWAV(). После того, как вы загрузили звук, вы должны преобразовать его к звуковому формату выходного потока с помощью функции SDL_ConvertAudio(), и сделать его доступным для вашей функции микширования.
Устройства SDL audio предназначены для низкоуровневого программного аудио микшера. Хорошую реализацию такого микшера, доступную по LGPL лицензии, можно найти в архиве SDL demos.
Пример: #define NUM_SOUNDS 2struct sample { Uint8 *data; Uint32 dpos; Uint32 dlen;} sounds[NUM_SOUNDS]; void mixaudio(void *unused, Uint8 *stream, int len){ int i; Uint32 amount; for ( i=0; i |
Инновации: Менеджмент - Моделирование - Софт
- Инновационный менеджмент
- Разработка программного обеспечения
- Россия и инновации
- Управление инновационным менеджментом
- Моделирование
- Финансовое моделирование
- Системы моделирования
- Виды моделирования
- Практическое моделирование
- Пакет Mechanical Desktop
- Моделирование программ
- Софт для моделирования
- Пакет Simulink
- Моделирование в IBM
- MSC Nastran Моделирование -
- AutoCAD
- Unigraphics
- P-CAD