Tutorial: Start Here

This tutorial is intended to introduce the concepts used in Nyx–especially to clear up differences from most graphics libraries. I promise, the API for Nyx is simple to use once you learn it, but there is a learning curve as there are features added that make programs run much faster, but also are not typically seen.

To start, #include "nyx/nyx.hpp" at the top of your source file. This should bring in all the normal parts of Nyx that you may need to use.

Basics

Nyx is centered around an Engine which controls everything and handles resource management for you. Isn’t that nice?

Creating one is simple:

auto engine = nyx::Engine();

Engine does not take any parameters, it simply sets up the base to start building a program off of.

Once you have an Engine created, you’re going to need a window. After all, a graphical program isn’t very useful if you can’t see what you’re doing.

To make a Window, you request one from the Engine:

auto window = engine.create_window("Tutorial", 800, 600);

The create_window method takes up to 4 arguments: a caption, width, height, and optionally, a boolean to enable/disable vsync. By default, vsync is enabled, thought you may wish to disable it if you are trying to take a performance benchmark.

Note

For now, only one Window object is supported at a time, though there are plans in the future to implement more than one.

The final piece we need to get up and running is at least one Context. On their own, Windows are not capable of doing any drawing, and the only thing you can do is clear it with a color. While that isn’t nothing, it’ll be much cooler if we can actually draw something. Contexts belong to Windows, and they are created like this:

auto ctx = window->create_context();

The create_context method does not take any arguments.

You can have more than once Context, and we’ll talk about why you might want to do that in a later tutorial.

Each of the objects we just created did a lot of work in the background to set up an environment to start putting content on the screen, but that is all (mostly) hidden from the user. This is by design to keep the library as simple as possible. In the future, there may be more options for customizability, but the defaults for now are sensible and should be completely fine for the majority of cases.

Main loop

Now that we have our framework, we can talk about the main loop of all Nyx programs. You are free to create a loop however you like, but there are two things that must happen every time a frame is rendered. For simplicity, let’s assume we have the objects we created before, then this would be a standard loop:

do {
  engine.update();

  window->clear();

  engine.show();
} while (engine.is_running);

There is a lot going on in only a few lines here, so let’s break it down.

Engine::update() is the backbone of all event handling in Nyx. It polls all the events like keypresses, mouse clicks, mouse position, window closing, etc, and passes them where they need to go. This must be called at the beginning of the loop for it to function correctly. It also updates the internal timers, but we’ll get to that later.

Window::clear() does exactly what it sounds like it does–it clears the window. It takes an optional argument of the form of a nyx::Color which will determine which color to clear it with.

Engine::show() must go at the bottom of the loop and is what tells Nyx to draw something onto the screen. Before show is called, any graphical changes to the window are buffered but not pushed to the GPU. Once show is called, those changes are submitted and the window will update.

Engine::is_running is true so long as there is at least one active window. For now, since there is only one window allowed, this becomes false as soon as the window is closed.

Drawing

You may have noticed that there are no actual drawing commands in the main loop, and now it’s time to fix that. If the program was run at this point, a window would pop up on the screen, but would be very boring. Let’s add a line using that context we created earlier.

The method for adding a line takes 5 parameters: an (x1, y1) pair for the first point, an (x2, y2) pair for the second point, and a color. Let’s draw a line from (0, 0) to (window->w, window->h) in red:

do {
  engine.update();

  window->clear();

  ctx->clear();
  ctx->line(0, 0, window->w, window->h, nyx::Color(0xff0000ff));

  engine.show();
} while (engine.is_running);

window->w and window->h simply get the width and height of the window.

Running the program now, you should see a red line that crosses the window diagonally from the top left to the bottom right. Congrats, you’ve drawn something!

There is something important happening here with the Context. Notice that we are calling ctx->clear() before adding the line. This is very important, as if we didn’t, the Context would simply fill up drawing the same line one more time every frame. This would gradually make the framerate worse and worse! Contexts keep their state between frames unless a clear is called, and this is one of the big features of Nyx. If all you wanted was a static line drawn on the screen, the more idiomatic way in Nyx would look like this:

ctx->line(0, 0, window->w, window->h, nyx::Color(0xff0000ff));

do {
  engine.update();

  window->clear();

  engine.show();
} while (engine.is_running);

For drawing a single line, this won’t make much of a performance difference, but when drawing large numbers of objects, taking advantage of the persistent state of a Context can massively improve drawing performance–which in a game can be the difference between being playable or not. The less time rendering is taking up, the more time you have for game logic.

Review

We created an Engine, used it to create a Window, and then used that to create a drawing Context. From there, we created a loop to draw onto the screen, and drew a single line in two different ways. For reference, here is the completed source for the program:

#include "nyx/nyx.hpp"

int main(int, char *[]) {
  auto engine = nyx::Engine();
  auto window = engine.create_window("Tutorial", 800, 600);
  auto ctx = window->create_context();

  ctx->line(0, 0, window->w, window->h, nyx::Color(0xff0000ff));

  do {
    engine.update();

    window->clear();

    engine.show();
  } while (engine.is_running);

  return 0;
}