Macroquad egui DevTools: Rust Game Debugging UI

Rodney Lab - May 2 - - Dev Community

🎮 Macroquad egui DevTools

In this post on Macroquad egui DevTools, I talk about a demo project I put together to add a developer-focussed debugging view to a Macroquad game. Macroquad is a Rust game development library built for fast prototyping and egui is a Rust immediate mode GUI. Interestingly, both were inspired by libraries from the C/C++ world. Macroquad was inspired by the C, raylib library, while Dear ImGui inspired the creation of egui.

We shall see the idea of the DevTools is to create a debugging interface, rather than something intended for end-user access. The inspiration here is the DevTools found in web browsers like Chrome and Firefox to help web developers debug sites they are working on. Using egui we shall see how you can both display data on the current Macroquad game state and also mutate the state. The latter could be handy for tweaking gameplay or even just the game aesthetic, working on real-world project.

Macroquad does have its own UI, though I preferred to use egui here partly because it allows more flexibility. It saves me having to learn another UI interface, and also make the UI more portable if I later want to port the game to Bevy, Godot or Tauri for example. egui enjoys wide support in the Rust Game Dev ecosystem.

Anyway, if this all sounds interesting to you, then let’s press on, starting by taking a look at what I created.

🧱 What I Built

I built a minimal proof-of-concept “game” using Macroquad and egui. The Macroquad base code renders a carrot coloured ball, which bounces off the window edges. Using egui widgets, I then added a dev panel which has readouts of the ball current position and velocity. Taking things a step further, another widget lets the developer change the size of the ball in real-time, just by using an egui UI slider.

Macroquad egui Dev Tools: a carrot-coloured ball floats in the middle of a window on a screen capture.  The window has a gunmetal coloured background.  In the top left corner of the window sits a panel title "Developer Tools"; it has two sections.  The Ball Physics section gives the current x and y values of the ball position and velocity.  The lower Ball Shape section has a slider for controlling the ball radius.

🤔 A Little more about egui

I mentioned egui is an immediate mode GUI. Immediate mode is a design pattern where code for handling user interface side effects appears alongside the code for the UI itself. For example, there is no callback function attached to our slider for adjusting the ball radius. We just pass a mutable reference to the ball radius into the slider widget UI method, and let the UI widget update the radius directly.

That immediate mode pattern makes coding an interface far simpler. It is not all milk and honey, though! Re-rendering unchanged elements on every loop might be inefficient. Another drawback is that precise layout can be tricky with immediate mode. Because the library draws widgets as it encounters them, centring a widget horizontally, as an example, can be tricky. This is because we might not know how wide the widget or container are until we have drawn them. That said, for a game debugging tool, immediate mode is a pretty good match.

If you want to see a more general introduction to egui or immediate mode in Rust, see a recent post where we looked at creating a Cistercian clock using Rust with egui.

⚙️ Project Setup

The macroquad-egui crate is probably the easiest way to get egui working with Macroquad. At the time of writing, macroquad-egui was not working well with the latest version of Macroquad, though I found these versions do work well together:

[package]
name = "macroquad-egui"
version = "0.1.0"
edition = "2021"

[dependencies]
egui = "0.21.0"
egui-macroquad = "0.15"
macroquad = { version="0.3.26", default-features=false }
Enter fullscreen mode Exit fullscreen mode

You can find some skeleton code in the egui-macroquad repo to test your setup:

// Source: https://github.com/optozorax/egui-macroquad#usage
use macroquad::prelude::*;

#[macroquad::main("egui with macroquad")]
async fn main() {
    loop {
        clear_background(WHITE);

        // Process keys, mouse etc.

        egui_macroquad::ui(|egui_ctx| {
            egui::Window::new("egui ❤ macroquad")
                .show(egui_ctx, |ui| {
                    ui.label("Test");
                });
        });

        // Draw things before egui

        egui_macroquad::draw();

        // Draw things after egui

        next_frame().await;
    }
}
Enter fullscreen mode Exit fullscreen mode

🖱️ Adding egui to Macroquad

Probably the hardest part, if you are new to egui, is to work out how to display the widgets you want. The egui demo site is quite handy in this regard. It features the egui widgets, and has GitHub links to the Rust code used to make each widget. This will help you replicate them in your own project.

I include the egui code I wrote here, in case it is useful for you as a starting point:

egui_macroquad::ui(|egui_ctx| {
    egui_ctx.set_pixels_per_point(4.0);
    egui::Window::new("Developer Tools").show(egui_ctx, |ui| {
        CollapsingHeader::new("Ball Physics")
            .default_open(false)
            .show(ui, |ui| {
                ui.horizontal(|ui| {
                    ui.label("Position");
                    ui.label(format!(
                        "x: {:.2} y: {:.2}",
                        ball.position.x, ball.position.y
                    ));
                });
                ui.horizontal(|ui| {
                    ui.label("Velocity");
                    ui.label(format!("x: {} y: {}", ball.velocity.x, ball.velocity.y));
                });
            });
        CollapsingHeader::new("Ball Shape")
            .default_open(false)
            .show(ui, |ui| {
                ui.horizontal(|ui| {
                    ui.label("Radius");
                    ui.add(egui::Slider::new(&mut ball.radius, 1.0..=100.0));
                });
            });
    });
});
Enter fullscreen mode Exit fullscreen mode

Some interesting points;

  • set_pixels_per_point in line 128 scales the whole user interface. I set 4.0 so the elements were large enough for capturing screenshots, and this might be a little high for general use.
  • The CollapsingHeader element in lines 130-144 creates the folding tree structure you see in the demo.
  • Notice in line 150, we just pass in a mutable reference to ball.radius to the slider. No need for callbacks; it directly updates the same variable which Macroquad uses for rendering.

Hopefully the other parts of the code will make sense, probably more so if you have already used Dear ImGui. That said, please let me know if some lines would benefit from further clarification. I have added a link, further down, to the entire project code.

🏁 What Next for the Macroquad egui DevTools Interface?

I mentioned this is just a basic proof-of-concept. I have tried a similar game debugging interface in C++ using raylib and Dear ImGui and would like to bring some features across such as:

  • adding game state and pausing play in the Dev Tools;
  • having separate regular and debugging DevTools modes; and
  • the ability to move the dev tool tab so it does not overlap the main window.

🙌🏽 Macroquad egui DevTools: Wrapping Up

In this Macroquad egui DevTools post, we got an introduction to working with Macroquad and egui. In particular, we saw:

  • which versions of Macroquad and egui work with egui-macroquad;
  • how you might use egui to update game state from a debugging panel; and
  • some resources for getting started with egui.

I hope you found this useful. As promised, you can get the full project code on the Rodney Lab GitHub repo. I would love to hear from you, if you are also new to Rust game development. Do you have alternative resources you found useful? How will you use this code in your own projects?

🙏🏽 Macroquad egui DevTools: Feedback

If you have found this post useful, see links below for further related content on this site. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on X, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on X (previously Twitter) and also, join the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Game Dev as well as Rust and C++ (among other topics). Also, subscribe to the newsletter to keep up-to-date with our latest projects.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .