Game Loop
The most basic use of le2d
is to create a window and use Dear ImGui in a game(-like) loop.
while (context->is_running()) {
context->next_frame();
ImGui::ShowDemoWindow();
context->present();
}
static auto create(CreateInfo const &create_info={}) -> std::unique_ptr< Context >
Window Customization
le::Context
takes a (default constructed) le::ContextCreateInfo
argument, using which the window parameters (among other things) can be customized. Use le::FullscreenInfo
for a (borderless) fullscreen window instead.
};
auto context = le::IContext::create(context_create_info_v);
Context creation parameters.
Definition context.hpp:76
WindowCreateInfo window
Window creation parameters.
Definition context.hpp:80
Windowed window parameters.
Definition context.hpp:61
glm::ivec2 size
Definition context.hpp:62
Drawing Shapes
Drawing a quad is fairly straightforward: create a le::drawable::Quad
instance outside the game loop so that its properties persist across frames (it is also lightweight enough to be created/destroyed every frame), start the primary render pass each frame, and issue the draw call. ImGui will be drawn in a separate post-pass, the order of the primary / custom passes vs ImGui::XYZ()
commands does not matter.
quad.create({100.0f, 100.0f});
while (context->is_running()) {
context->next_frame();
auto& renderer = context->begin_render();
quad.draw(renderer);
context->present();
}
Screenshot of quad
Handling Events
Events are encoded as a variant in le::Event
. le::Context
maintains an event queue which should be iterated over every frame.
context->next_frame();
for (auto const& event : context->event_queue()) {
if (auto const* key_event = std::get_if<le::event::Key>(&event)) {
if (key_event->key == GLFW_KEY_ESCAPE && key_event->action == GLFW_PRESS) {
context->set_window_close();
}
}
}
Note that le::Context::set_window_close()
doesn't immediately close the window, it just sets the close flag so that le::Context::is_running()
returns false
in the next game loop iteration. The same flag is set when the user clicks X / requests the window to be closed, and can thus be unset, eg for quit confirmation.
Delta Time
kvf::DeltaTime
can be used to track frame time in a central place:
auto context = le::IContext::create(context_create_info_v);
auto delta_time = kvf::DeltaTime{};
while (context->is_running()) {
auto const dt = delta_time.tick();
}
Using Assets
Assume the following file structure in the working directory:
assets
├── audio
│ └── explode.wav
├── fonts
│ └── Vera.ttf
└── images
└── awesomeface.png
A le::FileDataLoader
instance can be used to load binary data from files in assets/
, and an le::AssetLoader
instance to load asset types using any data loader:
auto const asset_loader = context->create_asset_loader(&data_loader);
auto const waiter = context->create_waiter();
Concrete IDataLoader that uses the filesystem.
Definition file_data_loader.hpp:6
Drawing a Textured Quad
Load the texture, then simply assign its pointer to quad.texture
. It should be evident that textures must outlive drawables that use them:
auto const texture = asset_loader.load_texture("images/awesomeface.png");
if (!texture) { throw std::runtime_error{"Failed to load Texture."}; }
quad.texture = texture.get();
Screenshot of textured quad
Drawing Text
Text can be drawn via le::drawable::Text
. Naturally, it requires a loaded le::Font
. First make room by pushing the quad up, and set its texture to the loaded one:
auto const font = asset_loader.load_font("fonts/Vera.ttf");
if (!font) { throw std::runtime_error{"Failed to load Font."}; }
quad.transform.position.y -= 30.0f;
quad.texture = &texture;
text.set_string(*font, "hello from le2d!");
text.tint = kvf::yellow_v;
Text Draw Primitive.
Definition text.hpp:36
Transform transform
Definition render_instance.hpp:8
Add the text instance to the draw list:
quad.draw(renderer);
text.draw(renderer);
Screenshot of text and textured quad
Playing Sounds
Audio is loaded as capo::Buffer
assets. One-shot sound effects can be fired-and-forgotten through le::IAudio::play_sfx()
, whereas for music you would want to control playback, volume, etc by owning an Audio Source returned by le::IAudio::create_source()
.
auto const audio_buffer = asset_loader.load_audio_buffer("audio/explode.wav");
if (!audio_buffer) { throw std::runtime_error{"Failed to load Audio Buffer."}; }
auto audio_wait = kvf::Seconds{2.0f};
auto audio_played = false;
audio_wait -= dt;
if (audio_wait < 0s && !audio_played) {
context->get_audio_mixer().play_sfx(audio_buffer.get());
audio_played = true;
}
Needless to say, audio buffers must outlive playback.