Мы научились открывать окно. Теперь выведем на него рисунок.
В оригинальном уроке автор теперь будет размещать только главные части кода.
Сам урок тут - http://lazyfoo.net/tutorials/S... /index.php
Архив с исходниками тут - http://lazyfoo.net/tutorials/S... screen.zip
Открывайте сразу код, он на 120 строк, и давайте изучать.
В первом уроке всё помещено в main функцию, так как программа маленькая. В реальных программах код разбивается на модули. Это позволит создавать отдельные части, каждую из которых можно независимо исправлять и изменять.
Создадим отдельные функции для инициализации, для загрузки изображения, и для закрытия sdl. Объявляем в верху исходника.
| C | 1
2
3
| bool init();
bool loadMedia();
void close(); |
|
Теперь объявляем глобальные переменные. Обычно стараются избегать глобальных, но мы делаем программу как можно проще, всё находится в одном файле, поэтому оставим так.
Новый тип переменной - SDL_Surface - это просто тип данных изображения, в котором содержится информация о пикселях. SDL_Surface использует программный рендер. Это значит что для рендера будут использоваться ресурсы процессора (CPU). Можно рендерить используя аппаратные средства, но это чуть сложнее и будет рассмотрено позже на примере с использованием графической карты (GPU).
Изображение будет загружаться из файла и отображаться в окне.
Обратите внимание, что это SDL_Surface* указатель на поверхность. Для чего так сделано?- Динамическое использование памяти для загрузки изображения
- Удобное обращение к изображению в памяти. Например, для отрисовки нескольких кирпичей. Намного проще держать в памяти одно изображение и использовать его несколько раз снова и снова.
И не забывайте после объявления сразу инициализировать значением NULL:
| C | 1
2
3
| SDL_Window* gWindow = NULL;
SDL_Surface* gScreenSurface = NULL;
SDL_Surface* gHelloWorld = NULL; |
|
Функцию инициализации и создания окна выносим в отдельную функцию. Мы вызываем SDL_GetWindowSurface, чтоб получить сёрфейс нашего окна. Именно туда мы будем выводить изображение.
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| bool init()
{
bool success = true;
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
success = false;
}
else
{
gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( gWindow == NULL )
{
printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
success = false;
}
else
{
gScreenSurface = SDL_GetWindowSurface( gWindow );
}
}
return success;
} |
|
В функции загрузки изображения загружаются файлы используя SDL_LoadBMP. По указанному адресу берётся .bmp файл и помещается в указанную поверхность. Возвращает указатель на эту поверхность. Если вернуло NULL значит произошла ошибка и текст ошибки выведется на консоль.
| C | 1
2
3
4
5
6
7
8
9
10
11
| bool loadMedia()
{
bool success = true;
gHelloWorld = SDL_LoadBMP( "02_getting_an_image_on_the_screen/hello_world.bmp" );
if( gHelloWorld == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError() );
success = false;
}
return success;
} |
|
В данном примере в папке проекта, в папке "02_getting_an_image_on_the_screen" ищется файл с именем "hello_world.bmp". Если программа не может найти изображение, проверяйте внимательнее адреса.
В завершающей функции нужно закрыть окно и выйти из SDL. Но перед этим нужно освободить память от поверхностей SDL_FreeSurface. Поверхность окна освободится при функции SDL_DestroyWindow.
Возьмите в привычку присваивать указателям значение NULL, когда они пока ни куда не указывают.
| C | 1
2
3
4
5
6
7
8
9
10
| void close()
{
SDL_FreeSurface( gHelloWorld );
gHelloWorld = NULL;
SDL_DestroyWindow( gWindow );
gWindow = NULL;
SDL_Quit();
} |
|
Теперь main функция. В ней вызываем инициализацию sdl и загрузку изображения. Если всё пройдёт успешно, то изображение из загруженной поверхности выведется на оконную поверхность используя SDL_BlitSurface.
Что делает блиттинг (blitting)? Он берёт исходную поверхность (source surface) и переносит на целевую поверхность (destination surface). Первый аргумент функции SDL_BlitSurface - поверхность источник, третий аргумент - конечная поверхность. Второй и четвертый будут рассмотрены позже.
Начало main функции:
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| int main( int argc, char* args[] )
{
if( !init() )
{
printf( "Failed to initialize!\n" );
}
else
{
if( !loadMedia() )
{
printf( "Failed to load media!\n" );
}
else
{
SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL ); |
|
В нашем примере всего одна функция для рисования. Но изображения ещё не видно. Нужно сделать ещё шаг.
| C | 1
| SDL_UpdateWindowSurface( gWindow ); |
|
Обычно, пока вы последовательно прорисовываете то, что нужно в данном кадре, вы этого не видите. Вам нужно обновить экран используя SDL_UpdateWindowSurface. Большинство систем рендеринга используют двойную буферизацию.
Когда вы рисуете используя SDL_BlitSurface, то рендер происходит в теневом буфере. На экране в этот момент показывается то, что находится в "переднем" буфере. Так делается, потому что в игре нужно нарисовать множество объектов. Если рисовать только во фронтальном буфере, то всё будет мигать, пока не нарисуется полный кадр. Поэтому сначала всё рисуется в теневом буфере, а затем мгновенно буферы меняются местами и человек видит полностью нарисованный кадр.
Не нужно вызывать SDL_UpdateWindowSurface после каждого блиттинга. Только после всех процедур рисования и блиттинга достаточно вызвать обновление, один раз для каждого кадра.
Вот мы всё нарисовали. Делаем задержку в 2 секунд, после чего закрываем программу
| C | 1
2
3
4
5
6
7
8
9
10
| //Wait two seconds
SDL_Delay( 2000 );
}
}
//Free resources and close SDL
close();
return 0;
} |
|
В лучших традициях "всё правильно, но не работает" у меня не работает.
Почему? Потому что код написан в стиле c++, а компилятор думает что код написан на чистом c. Соответственно выхода два. Или код изменить под обычный си, или кодеблоксу объяснить, что надо компилировать в плюсах.
Сейчас я переделаю код, а в следующих уроках мы перейдём на c++. Кроме того, в коде будут изменения. Я попытаюсь нарисовать футбольный мячик на зеленом поле:
Кликните здесь для просмотра всего текста
| C | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
| #include <SDL.h>
#include <stdio.h>
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
int init(); // для инициализации. вместо типа bool пришлось поставить int
int loadMedia(); // для загрузки изображений
void close(); // для закрытия
SDL_Window* gWindow = NULL;
SDL_Surface* gScreenSurface = NULL;
SDL_Surface* gBall = NULL; // имя переделали для мяча
int init()
{
int success = 1; // вместо bool пришлось использовать int и придавать числовые значения
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
success = 0;
}
else
{
gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( gWindow == NULL )
{
printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
success = 0;
}
else
{
gScreenSurface = SDL_GetWindowSurface( gWindow ); // получаем поверхность окна
}
}
return success;
}
int loadMedia()
{
int success = 1;
gBall = SDL_LoadBMP( "ball100.bmp" ); // размер нашего рисунка мяча 100*100 точек
if( gBall == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", "ball100.bmp", SDL_GetError() );
success = 0;
}
return success;
}
void close()
{
SDL_FreeSurface( gBall ); // высвобождаем поверхности
gBall = NULL;
SDL_DestroyWindow( gWindow );
gWindow = NULL;
SDL_Quit();
}
int main( int argc, char* args[] )
{
if( !init() )
{
printf( "Failed to initialize!\n" );
}
else
{
if( !loadMedia() )
{
printf( "Failed to load media!\n" );
}
else
{
// заливаем поле зеленым
SDL_FillRect( gScreenSurface, NULL, SDL_MapRGB( gScreenSurface->format, 0x00, 0x88, 0x00 ) );
// рисуем мяч на поверхности окна
SDL_BlitSurface( gBall, NULL, gScreenSurface, NULL );
// обновляем окно
SDL_UpdateWindowSurface( gWindow );
SDL_Delay( 5000 );
}
}
SDL_DestroyWindow( gWindow );
SDL_Quit();
return 0;
} |
|
Вот что получилось. Как видите, по углам остались белые уголки. В следующих уроках мы научимся использовать прозрачность для исправления этого, и отлавливать нажатия кнопок.
|