Rust Game Dev: Rapier and Macroquad #
In this post on using Rapier Physics with Macroquad, we take a look at getting started with Rapier and how you might set it up in a Macroquad project. In recent posts, we have looked at Rust game dev engines and Rust game physics engines. Here, we take one from each of those posts and create a couple of apps. The first is just a bouncing ball, based on the Rapier Getting Started Guide. Next, we level things up, and use Rapier height fields, sensors, collision events for a slightly more sophisticated simulation.
Macroquad is a fast and lightweight Rust game development library, intended to be easy to use and well-suited to game prototyping. Rapier, is probably the most established game physics engine in the Rust game dev ecosystem.
It is a collection of crates for 2D and 3D game physics with regular (f32
) and high precision (f64
) offerings. Rapier is fully-featured, and also compiles to WASM, for use on the web.
With the introductions out of the way, let’s take a closer look at what we are working on. I am new to game development in Rust, so please reach out if you have alternative ways of proceeding here, which I could try to incorporate.
🧱 What we’re Building #
We look at a couple of examples. The first, has just two physical rigid bodies: a ball and a ground surface. Porting the Rapier Getting Started guide to Macroquad, we see the ball bounce on the ground before coming to rest.
Stepping up a notch, the second example uses more Rapier features. Reversing gravity, bubbles fired in random directions float to the top of the screen, where they are caught by a Rapier height field.
⚙️ Rapier Physics with Macroquad Project Config #
To start, here is the Cargo.toml
:
-
The world is not particularly large, or fast-paced for either demo, so the
f32
version of rapier should suffice, and we will just use 2D simulation. -
The bubble demo uses
rand
andrand_distr
to generate a standard normal distribution for randomly deciding the initial ball velocity. -
We use
crossbeam
for Rapier collision event handling.
👋🏽 Rapier Physics with Macroquad Hello World #
I set the project up as a series of examples, so you can place the source code for the hello world
example in a new examples
folder as examples/hello_world.rs
. Here is the full Macroquad code for this first example (based on Rapier Rust Getting Started ):
examples/hello_world.rs
— click to expand code.
The Rapier docs are fantastic, both the User Guide and the library API docs. The Common Mistakes section of the User Guide is a good port of call for initial issues.
-
we use SI units with the Rapier model, so scale pixels (used by Macroquad for rendering) with a
factor of 50 pixels per metre (line
34
). The Common Mistakes doc (just mentioned) recommends scaling. - for colliders, we are generally working in half extents, so we give the radius of the ball and the half-thickness of the collider.
-
we set gravity to
-9.81
in line91
(we are working in SI units). Notice the Rapier Physics and Macroquad rendering vertical axes increase in opposite directions, with the rendering scale zero at the top and increasing downwards. -
we don’t need physics hooks or an event handler in this example, so set them each to the
unit type (lines
101
&102
).
Collider Properties #
Restitution is a measure of how much kinetic energy the ball retains on collision. With zero restitution, it will not bounce, while with 1.0, it will rebound with equal and opposite force. You can set other physical properties on colliders in the builder. Density, for example, is the easiest way to make a body heavier (hence adjusting movement and collision characteristics).
Running the Hello World Example #
To run the example, use the following command in the Terminal:
🫧 Levelling Up: Floating Ball Example #
Next, let’s turn to the floating ball example. In this example, we spawn floating balls that get caught at the top of the screen, by a collider. I used a standard normal distributed random variable to set the ball’s initial velocity; we do not just fire them vertically.
Example Features #
The ceiling is composed of a saw-tooth-like height field, instead of a flat cuboid. The idea here is to stop the balls sliding straight to the corners when they hit the ceiling. We look closer at the height field in a moment.
A new ball is only spawned once the existing ones have come to rest. This island manager helps here to check if they are still any active dynamic balls, before spawning a new one.
Finally, there is a sensor collider at the bottom of the window. Sensors just check a collision has occurred, and are inert. Typically, you can use them to find out when an object has entered an area. As the windows fills, eventually, there will be no more space for a freshly spawned ball, and it will bounce off other balls and hit the bottom of the window. I use an event handler so when a ball hits the sensor at the bottom of the window, I can set the simulation to end.
Bubble Example Code #
Here is the code for the example, which I saved to examples/bubbles.rs
:
examples/bubbles.rs
— click to expand code.
🥊 Placing Colliders #
You can use a translation to place a collider, like we did in the hello world example. You might also use the rotation method on the collider builder, to set the collider’s rotation in radians. If you want to set both, like we do for the left and right walls, then you can pass an isometry to the position method:
The position
method sets translation and rotation and will override
those values if you wet them individually.
🪚 Height Field #
A height field is a convenient and efficient way of creating a piecewise linear collider, like the saw-tooth-shaped collider at the top of the window. I use it there, as a kind of egg carton, to give the balls a natural resting position.
The height field has vertices at regular intervals along the x-axis, and you pass in the heights of the vertices (from the x-axis) in a vector:
Here we have a zigzag height field, which has -1.2
height for even-indexed
vertices and 0.0
for odd ones.
🏝️ Island Manager #
The island manager is a register of dynamic bodies, which we can query to get a collection of all active dynamic bodies at each step:
In this case, we want all existing balls to settle before spawning a fresh on, so check the collection of active dynamic bodies is empty.
🛫 Ground Collision Sensor & Event Handler #
We set up the event handler in lines 280
– 282
:
Then we can loop though collision events to check if any collisions involved a sensor:
Since the ground is the only sensor, there is no need to check collision handles, we can just go
ahead and pause the simulation. As well as CollisionEvent::Started
, there is CollisionEvent::Stopped
, which might be useful in
other scenarios.
Running the Bubbles Example #
To run the example, use the following command in the Terminal:
🏁 Rapier Physics with Macroquad Simulation: Next Steps #
I was impressed how quickly I could get going using Rapier physics with Macroquad, that said, it is worth spending a little more time to remove some rough corners. I might develop this into a game, like the Bubble Trouble arcade game. For that it would be worth:
- nailing down the physics, so the balls do not roll when they hit the ceiling,
- making the balls “stickier”, so other balls cannot knock settled balls out of position; and
- letting the ceiling drop over time, perhaps by applying an impulse, to make the game a little harder.
I might also preserve a version, as is. I find it so relaxing! It is more satisfying than doom-scrolling social feeds 😄.
Interested to know if you might play around with the demo a little and what improvements you decide on.
🗳 Poll #
🙌🏽 Rapier Physics with Macroquad: Wrapping Up #
In this Rapier Physics with Macroquad post, we got an introduction to working with Macroquad and Rapier. In particular, we saw:
- how to configure Rapier with Macroquad;
- porting the Rapier Getting Started code to Macroquad; and
- more advanced Rapier features such as height fields, event handlers and the Island Manager .
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?
🙏🏽 Rapier Physics with Macroquad: 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.
Just dropped a new blog post on how you can use Rapier with Macroquad for fast game prototyping.
— Rodney (@askRodney) May 1, 2024
We start with a hello world ball drop, then level up to a floating ball example with a Rapier height field.
Hope you find it useful!
https://t.co/kJ3fHi7Gjo #askRodney
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.