- PVSM.RU - https://www.pvsm.ru -
Nuklear+ [1] (читается как "Nuklear cross", значит "кроссплатформенный Nuklear") — это надстройка над GUI библиотекой Nuklear [2], которая позволяет абстрагироваться от драйвера вывода и взаимодействия с операционной системой. Нужно написать один простой код, а он потом уже сможет скомпилироваться под все поддерживаемые платформы.
Я уже писал на хабре статью "Nuklear — идеальный GUI для микро-проектов? [3]". Тогда задача была простой — сделать маленькую кроссплатформенную утилиту с GUI, которая будет примерно одинаково выглядеть в Windows и Linux. Но с тех самых пор меня не отпускал вопрос, а можно ли на Nuklear сделать что-то более-менее сложное? Можно ли целиком на нём сделать какой-нибудь реальный проект, которым будут пользоваться?
Именно поэтому следующую свою игру, Wordlase [5], я делал на чистом Nuklear. И без всякого там OpenGL. Даже фоновые картинки у меня имеют тип nk_image
. В конечном итоге это дало возможность выбора драйвера отрисовки, вплоть до чистого X11 или GDI+.
Ещё в прошлой своей статье [3] я заложил основы Nuklear+ [1] — библиотеки, призванной спрятать всю "грязь" от программиста и дать ему сфокусироваться на создании интерфейса. Библиотека умеет загружать шрифты, картинки, создавать окно операционной системы и контекст отрисовки.
Полный пример кода есть в Readme на GitHub [1]. Там можно увидеть, что код получается довольно простой. Также я перенёс на Nuklear+ свои проекты dxBin2h [6] и nuklear-webdemo [7]. И сделать это было очень просто — вся инициализация заменяется на один вызов nkc_init
, события обрабатываются nkc_poll_events
, отрисовка функцией nkc_render
, а в качестве деструктора вызывается nkc_shutdown
.
Но вернёмся к Wordlase, на примере которой и построена данная публикация. С недавних пор у игры есть веб-демо [4]. Я не писал какого-то специфичного веб-кода для игры — это чистое С89 приложение, скомпилированное с помощью Emscripten [9]. И если полностью следовать примеру из Readme Nuklear+ (а именно, использовать nkc_set_main_loop
), то веб-версия приложения будет получена абсолютно на халяву, без особых лишних затрат.
Самой интересной частью Nuklear+ являются поддерживаемые фронтэнды и бэкэнды. В данном случае под фронтэндом понимается часть, ответственная за взаимодействие с ОС и отрисовку окна. Т.е. непосредственно то, что видит пользователь. Реализации лежат в папке nkc_frontend [10]. Сейчас поддерживаются: SDL, GLFW, X11, GDI+. Они не равносильны. Например, GDI+ использует WinAPI даже для рендера шрифтов и загрузки изображений, т.е. получить ровно такую же картинку в других ОС будет проблематично. Реализация так же не везде одинакова. Например, реализация Х11 пока не умеет изменять разрешение экрана в полноэкранном режиме (буду рад видеть Pull Request)
Выбрать фронтэнд для своего приложения просто — нужно установить переменную препроцессора NKCD=NKC_x
, где x
это одно из: SDL, GLFW, XLIB, GDIP. Например: gcc -DNKCD=NKC_GLFW main.c
Бэкэнд в данном случае выполняет непосредственно отрисовку. Реализация в папке nuklear_drivers [11]. Отрисовка средствами любой версии OpenGL выдаёт примерно одинаковую картинку на всех ОС и фронтэндах. Ведь для загрузки изображений там всегда используется stb_image [12], а шрифт рендерится стандартными средствами Nuklear (тоже основано на stb). В то же время чистый Х11 драйвер даже не умеет загружать шрифты. Так что не забывайте тестировать своё приложение для выбранной пары бэкэнд+фронтэнд.
Например: Wordlase, GLFW3, OpenGL 2, Windows
[13]
Или: Wordlase, SDL2, OpenGL ES, Linux
[14]
В качестве бэкэнда по умолчанию выбран OpenGL2, если доступен. Можно задать NKC_USE_OPENGL=3
для OpenGL 3, и NKC_USE_OPENGL=NGL_ES2
для OpenGL ES 2.0. Для использования чистого Х11 отрисовщика константу NKC_USE_OPENGL
указывать не надо. Также OpenGL опции не влияют на GDI+ — там отрисовка всегда идёт своими средствами.
Вот скриншот с GDI+: Wordlase, GDI+, без OpenGL, Windows
Этот бэкэнд полноценно поддерживает полупрозрачные изображения, картинка близка к оригиналу. Разница в шрифте: хинтинг, сглаживание, да даже размер (также буду рад Pull Request'у для автоматической подстройки размера GDI+ шрифта под размер stb_ttf).
И самый ужасный случай — чистый Х11 отрисовщик, который до моего pull request [16] даже не умел загружать картинки. Wordlase, X11, без OpenGL, Linux:
Вот здесь уже довольно много отличий: логотип, солнечные лучи, более острый край девушки, шрифт. Почему? Фон в игре на лету собирается из нескольких полупрозрачных PNG. Но чистый Х11 поддерживает только битовую прозрачность, прямо как GIF. Также отрисовщик Х11 очень медленно работает на больших изображениях с прозрачностью. А если в движке отключить прозрачность, то картинка становится ещё хуже. Wordlase, X11, без OpenGL, без прозрачности:
Так зачем вообще нужны отрисовщики GDI+ и Х11, если они так уродливы? Потому, что они плохи только для больших изображений с прозрачностью. А если делать маленькую утилиту, где картинки используются только как иконки пользовательского интерфейса, то эти отрисовщики становятся вовсе неплохим вариантом, т.к. имеют минимальное количество зависимостей. Также я пользовался чистым Х11 отрисовщиком на слабых системах, где OpenGL только программный. В таком случае Х11 работает быстрее OpenGL. Подсказка: если вместо кучи полупрозрачных PNG использовать один большой JPEG, то Х11 будет работать быстро и корректно.
Пример хорошего использования чистого Х11 бэкэнда — главное игровое окно Wordlase. Больших картинок там почти нет, зато есть несколько интерфейсных иконок, которые вполне корректно отображаются:
Отлично, отрисовщик выбран, окно ОС создаётся. Теперь самое время заняться GUI!
Самым первым в Wordlase [4] показывается экран выбора языка:
Здесь видны сразу 2 интересных техники: несколько картинок на фоне окна и центрирование виджетов.
Поместить картинку на фон окна достаточно просто:
nk_layout_space_push(ctx, nk_rect(x, y, width, height));
nk_image(ctx, img);
x
и y
— позиция на экране, width
и height
— размеры изображения.
Центрирование является более сложной задачей, т.к. не поддерживается Nuklear напрямую. Нужно вычислять положение самостоятельно:
if ( nk_begin(ctx, WIN_TITLE,
nk_rect(0, 0, winWidth, winHeight), NK_WINDOW_NO_SCROLLBAR)
) {
int i;
/* 0.2 are a space skip on button's left and right, 0.6 - button */
static const float ratio[] = {0.2f, 0.6f, 0.2f}; /* 0.2+0.6+0.2=1 */
/* Just make vertical skip with calculated height of static row */
nk_layout_row_static(ctx,
(winHeight - (BUTTON_HEIGHT+VSPACE_SKIP)*langCount )/2, 15, 1
);
nk_layout_row(ctx, NK_DYNAMIC, BUTTON_HEIGHT, 3, ratio);
for(i=0; i<langCount; i++){
nk_spacing(ctx, 1); /* skip 0.2 left */
if( nk_button_image_label(ctx, image, caption, NK_TEXT_CENTERED)
){
loadLang(nkcHandle, ctx, i);
}
nk_spacing(ctx, 1); /* skip 0.2 right */
}
}
nk_end(ctx);
Следующая прикольная штучка — выбор темы оформления в настройках:
Реализуется тоже просто:
if (nk_combo_begin_color(ctx, themeColors[s.curTheme],
nk_vec2(nk_widget_width(ctx), (LINE_HEIGHT+5)*WTHEME_COUNT) )
){
int i;
nk_layout_row_dynamic(ctx, LINE_HEIGHT, 1);
for(i=0; i<WTHEME_COUNT; i++)
if( nk_button_color(ctx, themeColors[i]) ){
nk_combo_close(ctx);
changeGUItheme(nkcHandle, s.curTheme);
}
nk_combo_end(ctx);
}
Здесь главное понимать, что всплывающее поле combo — это такое же окно, как и главное. И располагать там можно что угодно.
Самым сложно выглядящим окном является основное игровое окно:
На самом деле, тут тоже нет ничего сложного. На экране всего 4 ряда:
nk_property_int
)nk_group_scrolled
)Единственный непонятный момент здесь — задание точных размеров элементам. Выполняется это с помощью соотношения ряда:
float ratio[] = {
(float)BUTTON_HEIGHT/winWidth, /* square button */
(float)BUTTON_HEIGHT/winWidth, /* square button */
(float)topWordSpace/winWidth,
(float)WORD_WIDTH/winWidth
};
nk_layout_row(ctx, NK_DYNAMIC, BUTTON_HEIGHT, 4, ratio);
BUTTON_HEIGHT
и WORD_WIDTH
— константы, измеряются в пикселях; topWordSpace
вычисляется как ширина экрана минус ширины всех остальных элементов.
И последнее сложно выглядящее окно — статистика:
Расположение элементов регулируется с помощью группировки. Ведь всегда можно сказать Nuklear: "в этом ряду будет 2 виджета". Но группа тоже является виджетом. Т.е. можно просто создать группу с помощью nk_group_begin
и nk_group_end
, а дальше позиционироваться внутри неё как внутри обычного окна (nk_layout_row
и пр.).
Nuklear уже готов даже для коммерческих игр и приложений. А Nuklear+ может сделать их создание более приятным.
Автор: Храбров Дмитрий
Источник [21]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/263993
Ссылки в тексте:
[1] Nuklear+: https://github.com/DeXP/nuklear_cross
[2] Nuklear: https://github.com/vurtun/nuklear
[3] Nuklear — идеальный GUI для микро-проектов?: https://habrahabr.ru/post/319106/
[4] Веб-демо Wordlase: https://wordlase.dexp.in
[5] Wordlase: http://store.steampowered.com/app/602930/Wordlase/
[6] dxBin2h: https://github.com/DeXP/dxBin2h
[7] nuklear-webdemo: https://github.com/DeXP/nuklear-webdemo
[8] Image: https://dexp.github.io/nuklear-webdemo
[9] Emscripten: https://kripken.github.io/emscripten-site/
[10] nkc_frontend: https://github.com/DeXP/nuklear_cross/tree/master/nkc_frontend
[11] nuklear_drivers: https://github.com/DeXP/nuklear_cross/tree/master/nuklear_drivers
[12] stb_image: https://github.com/nothings/stb
[13] Image: https://habrastorage.org/web/d81/f02/298/d81f02298d974dc9ad67e903926d5c0e.jpg
[14] Image: https://habrastorage.org/web/b49/776/063/b4977606303840b180c474f10d104653.jpg
[15] Image: https://habrastorage.org/web/e47/097/fd6/e47097fd606d4751be3824128e49d1c6.jpg
[16] pull request: https://github.com/vurtun/nuklear/pull/512
[17] Image: https://habrastorage.org/web/b46/a9d/cb9/b46a9dcb9a734d66893c35773ea8a299.jpg
[18] Image: https://habrastorage.org/web/e5f/54e/569/e5f54e5698d448888397db00eb48ac17.jpg
[19] Wordlase веб демо: https://wordlase.dexp.in/
[20] Эта публикация на английском: https://dexp.in/articles/nuklear-cross/
[21] Источник: https://habrahabr.ru/post/338106/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.