Macro fps_gameloop

Source
macro_rules! fps_gameloop {
    ($logic:block, $render:block, $fps:expr) => { ... };
    ($logic:block, $render:block, $fps:expr, $handle_elapsed:expr) => { ... };
}
Expand description

You can use the fps_gameloop! macro to avoid writing a lot of boilerplate code. Take this block of code from a program written using the gemini-engine crate, for example:

viewport.objects.push(Mesh3D::default_cube());
let FPS = 30.0;

let mut frame_skip = false;
loop {
    let now = Instant::now();

    // Logic
    let cube_transform = &mut viewport.objects[0].transform;
    *cube_transform = cube_transform
        .mul_mat4(&Transform3D::from_rotation_y(-0.05));

    // Rendering
    if !frame_skip {
        view.clear();
        view.draw(&viewport);
        view.display_render().unwrap();
    }

    let elapsed = now.elapsed();
    frame_skip = gameloop::sleep_fps(FPS, Some(elapsed));
}

There’s a lot of boilerplate code here. That’s where this macro comes in. Here is the same block of code, rewritten with fps_gameloop!:

viewport.objects.push(Mesh3D::default_cube());
let FPS = 30.0;

fps_gameloop!(
    {
        let cube_transform = &mut viewport.objects[0].transform;
        *cube_transform = cube_transform
            .mul_mat4(&Transform3D::from_rotation_y(-0.05));
    },
    {
        view.clear();
        view.draw(&viewport);
        view.display_render().unwrap();
    },
    FPS
);

The code is now a lot less cluttered. This macro accepts three fragments (and an optional fourth fragment):

  • A logic block fragment for code that should run every single frame
  • A render block fragment for code related to displaying to the terminal (all plots, draws and renders). This will not run if the previous frame took too long
  • An f32 fragment representing the desired frames per second.
  • Optionally, a function of type Fn(Duration, bool). The passed duration will be the time taken to render everything, and the passed bool indicates whether the last frame was skipped or not. It can be used to, say, print debug info. Here’s an example:
fps_gameloop!(
    // -- other fields --
    |elapsed: Duration, frame_skip: bool| {
        println!(
            "Elapsed: {:.2?}µs | Frame skip: {}",
            elapsed.as_micros(),
            frame_skip
        );
    }
);