Nuklear + (reads "Nuklear cross", means "cross-platform Nuklear") is an add-on to the Nuklear GUI library, which allows you to abstract from the output driver and interaction with the operating system. You need to write one simple code, and then it will be able to compile for all supported platforms.
I already wrote on Habr article " Nuklear - the perfect GUI for micro-projects? ". Then the task was simple - to make a small cross-platform utility with a GUI, which would look about the same in Windows and Linux. But ever since I have not been let go of the question, is it possible to do something more or less complicated on Nuklear? Is it possible to make a real project entirely on it that will be used?
That is why my next game, Wordlase , I did on pure Nuklear. And without any OpenGL there. Even the background pictures of my type are nk_image
. Ultimately, this made it possible to select a rendering driver, right down to pure X11 or GDI +.
Even in my last article, I laid the foundations of Nuklear + - a library designed to hide all the "dirt" from the programmer and allow him to focus on creating the interface. The library can download fonts, images, create an operating system window and the rendering context.
The full code example is in the Readme on GitHub . There you can see that the code is pretty simple. I also transferred my dxBin2h and nuklear-webdemo projects to Nuklear +. And it was very easy to do this - all initialization is replaced with one call to nkc_init
, events are handled by nkc_poll_events
, nkc_render
function nkc_render
, and nkc_shutdown
is called as the destructor.
But back to Wordlase, on the example of which this publication was built. Recently, the game has a web demo . I did not write any specific web code for the game - this is a pure C89 application compiled with Emscripten . And if you completely follow the example from the Readme Nuklear + (namely, use nkc_set_main_loop
), then the web version of the application will be obtained absolutely for free, without any extra costs.
The most interesting part of Nuklear + are supported frontends and backends. In this case, the frontend is the part responsible for interacting with the OS and drawing the window. Those. directly what the user sees. Implementations are in the nkc_frontend folder. Currently supported: SDL, GLFW, X11, GDI +. They are not equivalent. For example, GDI + uses WinAPI even for rendering fonts and loading images, i.e. Get exactly the same picture in other operating systems will be problematic. The implementation is also not the same everywhere. For example, the X11 implementation is not yet able to change the screen resolution in full screen mode (I will be glad to see Pull Request)
Selecting a frontend for your application is simple - you need to set the preprocessor variable NKCD=NKC_x
, where x
is one of: SDL, GLFW, XLIB, GDIP. For example: gcc -DNKCD=NKC_GLFW main.c
The backend in this case directly draws. The implementation is in the nuklear_drivers folder. Drawing with any version of OpenGL produces approximately the same image on all operating systems and frontends. After all, stb_image is always used to load images there, and the font is rendered using standard Nuklear tools (also based on stb). At the same time, the pure X11 driver does not even know how to download fonts. So do not forget to test your application for the selected pair of backend + frontend.
For example: Wordlase, GLFW3, OpenGL 2, Windows
Or: Wordlase, SDL2, OpenGL ES, Linux
The default backend is OpenGL2, if available. You can set NKC_USE_OPENGL=3
for OpenGL 3, and NKC_USE_OPENGL=NGL_ES2
for OpenGL ES 2.0. To use the pure X11 paint constant, the NKC_USE_OPENGL
constant is not NKC_USE_OPENGL
. Also, OpenGL options do not affect GDI + - there is always drawing by its own means.
Here is a screenshot with GDI +: Wordlase, GDI +, without OpenGL, Windows
This backend fully supports translucent images, the picture is close to the original. The difference in the font: hinting, anti-aliasing, and even the size (I will also be happy to pull Request for automatic adjustment of the size of the GDI + font to the size of stb_ttf).
And the worst case is the clean X11 renderer, which until my pull request didn't even know how to upload pictures. Wordlase, X11, without OpenGL, Linux :
There are already quite a few differences: the logo, the sun's rays, the sharper edge of the girl, the font. Why? The background in the game on the fly is made up of several translucent PNGs. But pure X11 only supports bit transparency, just like GIF. Also, the X11 renderer works very slowly on large images with transparency. And if you turn off transparency in the engine, the picture becomes even worse. Wordlase, X11, without OpenGL, without transparency :
So why do we need the GDI + and X11 renderers, if they are so ugly? Because they are bad only for large images with transparency. And if you make a small utility, where pictures are used only as icons for the user interface, then these renderers become a good option at all, because have a minimum amount of dependencies. I also used pure X11 on a weak system, where OpenGL is only software. In this case, X11 is faster than OpenGL. Hint: if instead of a heap of translucent PNGs you use one large JPEG, then X11 will work quickly and correctly.
An example of a good use of a clean X11 backend is the main Wordlase game window. There are almost no large pictures there, but there are several interface icons that are quite correctly displayed:
Ok, the drawing tool is selected, the OS window is created. Now is the time to do the GUI!
The very first in Wordlase is the language selection screen:
Two interesting techniques are immediately visible here: a few pictures against the window and the centering of the widgets.
Placing a picture on the background of the window is quite simple:
nk_layout_space_push(ctx, nk_rect(x, y, width, height)); nk_image(ctx, img);
x
and y
- position on the screen, width
and height
- the size of the image.
Centering is more difficult because not supported by Nuklear directly. You need to calculate the position yourself:
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);
The next cool thing is the choice of themes in the settings:
Implementing is also easy:
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); }
The main thing here is to understand that the pop-up combo field is the same window as the main one. And you can have anything there.
The most difficult-looking window is the main game window:
In fact, there is also nothing complicated. On the screen only 4 rows:
nk_property_int
widget)nk_group_scrolled
)The only incomprehensible point here is setting the exact dimensions of the elements. This is done using the ratio of the series:
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
and WORD_WIDTH
- constants, measured in pixels; topWordSpace
calculated as the screen width minus the width of all other elements.
And the last difficult looking window is statistics:
The location of the elements is regulated by grouping. After all, you can always say Nuklear: "there will be 2 widgets in this row." But the group is also a widget. Those. you can simply create a group using nk_group_begin
and nk_group_end
, and then position nk_group_end
inside it as inside a regular window ( nk_layout_row
, etc.).
Nuklear is ready even for commercial games and applications. And Nuklear + can make their creation more enjoyable.
Source: https://habr.com/ru/post/338106/
All Articles