<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rodney Lab</title>
    <description>The latest articles on Forem by Rodney Lab (@askrodney).</description>
    <link>https://forem.com/askrodney</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F664822%2Ffd20117b-2cde-491a-9d71-8b3f8d4b5e10.png</url>
      <title>Forem: Rodney Lab</title>
      <link>https://forem.com/askrodney</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/askrodney"/>
    <language>en</language>
    <item>
      <title>Godot Rust CI: Handy GDScript &amp; Rust GitHub Actions</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Thu, 15 Aug 2024 07:19:50 +0000</pubDate>
      <link>https://forem.com/askrodney/godot-rust-ci-handy-gdscript-rust-github-actions-m4c</link>
      <guid>https://forem.com/askrodney/godot-rust-ci-handy-gdscript-rust-github-actions-m4c</guid>
      <description>&lt;h2&gt;
  
  
  🎬 Godot Rust GitHub Actions
&lt;/h2&gt;

&lt;p&gt;In this Godot Rust CI post, we take a quick look at some GitHub action you might want to add to your project for linting GDScript and Rust code on each commit.  &lt;/p&gt;

&lt;p&gt;The config, below, is based on the &lt;em&gt;Knights to See You&lt;/em&gt; game.  I ran through that project and some &lt;a href="https://rodneylab.com/godot-rust-gdext/" rel="noopener noreferrer"&gt;resources for getting going with Godot Rust bindings&lt;/a&gt; in a recent post.  You might want to start there if you are new to Rust in Godot.&lt;/p&gt;

&lt;p&gt;GitHub actions let you run continuous integration processes on each commit, usually by adding a YAML config file in a &lt;code&gt;.github/workflows&lt;/code&gt; directory for your project.  They can take a little trial-and-error to get right, but are worth the effort for keeping code consistent, running unit tests, finding outdated packages and even spotting potential security vulnerabilities.&lt;/p&gt;

&lt;p&gt;There is a link further down to the full project code.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 GDScript Linting and Formatting
&lt;/h2&gt;

&lt;p&gt;The project uses a mix of Rust and GDScript, and for linting the GDScript code in GitHub actions, I use &lt;a href="https://github.com/Scony/godot-gdscript-toolkit" rel="noopener noreferrer"&gt;godot-gdscript-toolkit&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rust&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read-all&lt;/span&gt;
&lt;span class="c1"&gt;# TRUNCATED...&lt;/span&gt;
  &lt;span class="na"&gt;static-checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GDScript&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Static&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;checks'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scony/godot-gdscript-toolkit@9c4fa1cd596149d71e9d867416f3bb7b3a2fed3e&lt;/span&gt; &lt;span class="c1"&gt;# 4.2.2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gdformat --check godot/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gdlint godot/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting default read-only permissions in a line &lt;code&gt;10&lt;/code&gt; is a good idea, to limit access actions have to your repo.  You can override this for any actions which require repo write access.&lt;/p&gt;

&lt;p&gt;I use commit hashes to identify the exact action I want to run, though you can just use tag, instead.&lt;/p&gt;

&lt;p&gt;The project is structured with &lt;code&gt;godot&lt;/code&gt; and &lt;code&gt;rust&lt;/code&gt; folders at the root level, and I set &lt;code&gt;gdformat&lt;/code&gt; and &lt;code&gt;gdlint&lt;/code&gt; only to run on the &lt;code&gt;godot&lt;/code&gt; directory.  &lt;code&gt;gdformat&lt;/code&gt; provides opinionated GDScript formatting, while &lt;code&gt;gdlint&lt;/code&gt; includes checks for unnecessary else statements, unused arguments and that your naming is consistent with the &lt;a href="https://docs.godotengine.org/pl/stable/tutorials/scripting/gdscript/gdscript_styleguide.html" rel="noopener noreferrer"&gt;GDScript style guide&lt;/a&gt; (snake case function names, Pascal Case classes etc) among other checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu7yddc14d27teapyemn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu7yddc14d27teapyemn.png" alt="Godot Rust C I: screen capture show log from Git Hub action running godot-g d script-toolkit.  g d format output reads 4 files would be left unchanged and g d lint output reads Success: no problems found." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🦀 Rust Linting and Formatting
&lt;/h2&gt;

&lt;p&gt;Rust is well-known for having integrated formatting, linting, testing through cargo utilities, and you can set these up to run in &lt;a href="https://github.com/dtolnay/rust-toolchain" rel="noopener noreferrer"&gt;GitHub actions by dtolnay&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rust&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read-all&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CARGO_TERM_COLOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
  &lt;span class="na"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dwarnings&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Cinstrument-coverage"&lt;/span&gt;
  &lt;span class="na"&gt;LLVM_PROFILE_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;project-%p-%m.profraw"&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dtolnay/rust-toolchain@4f366e621dc8fa63f557ca04b8f4361824a35a45&lt;/span&gt; &lt;span class="c1"&gt;# stable&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo test&lt;/span&gt;
  &lt;span class="na"&gt;fmt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rustfmt&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dtolnay/rust-toolchain@4f366e621dc8fa63f557ca04b8f4361824a35a45&lt;/span&gt; &lt;span class="c1"&gt;# stable&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rustfmt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce formatting&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo fmt --check&lt;/span&gt;
  &lt;span class="na"&gt;clippy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clippy&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dtolnay/rust-toolchain@4f366e621dc8fa63f557ca04b8f4361824a35a45&lt;/span&gt; &lt;span class="c1"&gt;# stable&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;clippy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Linting&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo clippy -- -D warnings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are probably already familiar with these tools, but let me know if the GitHub action side of things needs more explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧐 Other Handy Rust Actions
&lt;/h2&gt;

&lt;p&gt;Jon Gjengset has a &lt;a href="https://www.youtube.com/watch?v=xUH-4y92jPg" rel="noopener noreferrer"&gt;fantastic video, in which he takes you through setting up CI on a Rust GitHub repo&lt;/a&gt;.  You can clone his setup into any new Rust projects.  Essentially, that is the process I followed for &lt;em&gt;Knights to See You&lt;/em&gt;.  Besides the Rust actions above, I have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;msrv check&lt;/strong&gt; - a check that the project successfully builds using the &lt;a href="https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field" rel="noopener noreferrer"&gt;Minimum Supported Rust version I give in &lt;code&gt;Cargo.toml&lt;/code&gt;&lt;/a&gt;.  This is worth checking in CI, since locally you will, likely, be running a newer Rust version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cargo deny&lt;/strong&gt; check - this checks the licences of any crates you use in your project and dependencies of those crates.  You create your own allow list of licences, and the tool highlights any projects without matching licences.  This helps avoid surprises further down the line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;audit-check&lt;/strong&gt; - finds crates with security vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rust MSRV Check Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rust&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read-all&lt;/span&gt;
&lt;span class="c1"&gt;# TRUNCATED...&lt;/span&gt;
  &lt;span class="na"&gt;msrv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;msrv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.78.0"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu / ${{ matrix.msrv }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Linux Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo apt-get update&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install ${{ matrix.msrv }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dtolnay/rust-toolchain@4f366e621dc8fa63f557ca04b8f4361824a35a45&lt;/span&gt; &lt;span class="c1"&gt;# stable&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;toolchain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.msrv }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo +${{ matrix.msrv }} check&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo check&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cargo Deny Licence Check Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cargo Deny&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cargo-deny&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-22.04&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;advisories&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bans licenses sources&lt;/span&gt;
    &lt;span class="c1"&gt;# Prevent sudden announcement of a new advisory from failing ci:&lt;/span&gt;
    &lt;span class="na"&gt;continue-on-error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.checks == 'advisories' }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EmbarkStudios/cargo-deny-action@3f4a782664881cf5725d0ffd23969fcce89fd868&lt;/span&gt; &lt;span class="c1"&gt;# v1.6.3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check ${{ matrix.checks }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://github.com/EmbarkStudios/cargo-deny-action" rel="noopener noreferrer"&gt;cargo-deny-action repo for configuration details&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rust Security Audit Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security audit&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cargo.toml'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cargo.lock'&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security_audit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332&lt;/span&gt; &lt;span class="c1"&gt;# v4.1.7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rustsec/audit-check@dd51754d4e59da7395a4cd9b593f0ff2d61a9b95&lt;/span&gt; &lt;span class="c1"&gt;# v1.4.1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://github.com/rustsec/audit-check" rel="noopener noreferrer"&gt;audit-check repo for configuration details&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are more checks you might want to include, depending on your project, and I have only highlighted a few here.  See &lt;a href="https://github.com/jonhoo/rust-ci-conf" rel="noopener noreferrer"&gt;Jon Gjengset’s rust-ci-config repo&lt;/a&gt; for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Godot Rust CI: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this Godot Rust CI, we took a quick look through some GitHub actions you might want to add to your Godot Rust game.  In particular, we looked at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;actions for linting and formatting GDScript&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;running familiar cargo tooling in GitHub actions&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;also how to run &lt;strong&gt;security audit and licence checks&lt;/strong&gt; on your Rust crates in CI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/knights-to-see-you" rel="noopener noreferrer"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;. I would love to hear from you, if you are also new to Godot video game development.  Were there other resources you found useful? Also, let me know what kind of game you are working on!&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Godot Rust CI: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/" rel="noopener noreferrer"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875" rel="noopener noreferrer"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org" rel="noopener noreferrer"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/" rel="noopener noreferrer"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/" rel="noopener noreferrer"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/" rel="noopener noreferrer"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue" rel="noopener noreferrer"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>godot</category>
      <category>rust</category>
    </item>
    <item>
      <title>Godot Rust gdext: GDExtension Rust Game Dev Bindings</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 07 Aug 2024 17:05:20 +0000</pubDate>
      <link>https://forem.com/askrodney/godot-rust-gdext-gdextension-rust-game-dev-bindings-37i1</link>
      <guid>https://forem.com/askrodney/godot-rust-gdext-gdextension-rust-game-dev-bindings-37i1</guid>
      <description>&lt;h2&gt;
  
  
  🕹️ Godot Rust
&lt;/h2&gt;

&lt;p&gt;Following on from the recent, trying Godot 4 post, here, we look at Godot Rust gdext.  In that previous post, I mentioned how Godot not only lets you code in officially supported GDScript, but also lets you create dynamic libraries for your Godot games written in Rust, Swift, and Zig among other languages.&lt;/p&gt;

&lt;p&gt;In this post, I run through some resources for getting started with Godot Rust gdext and also highlight some tips that I benefited from.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧑🏽‍🎓 gdext Learning Resources
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gdext&lt;/code&gt; provides GDExtension bindings for Godot 4.  If you are working with Godot 4, skip over &lt;code&gt;gdnative&lt;/code&gt; resources, which relate to the older Godot 3 API.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://godot-rust.github.io/book/" rel="noopener noreferrer"&gt;best starting point is probably the godot-rust book&lt;/a&gt;, which starts by setting up with gdext and runs through a basic code example.  The book then move on to more advanced topics, these are really helpful, and you will probably want to keep the book open even if you jump to working on your own game after working through the initial chapters.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://godot-rust.github.io/docs/gdext/master/godot/" rel="noopener noreferrer"&gt;official, online Rust godot docs&lt;/a&gt; document APIs.  If you have to work offline, cargo lets you open these from a local source in a browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo doc &lt;span class="nt"&gt;--open&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt; godot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the gdext APIs mirror GDScript APIs, you will often want to &lt;a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html" rel="noopener noreferrer"&gt;check the GDScript API docs&lt;/a&gt; for additional background.&lt;/p&gt;

&lt;p&gt;Another fantastic resource is the &lt;a href="https://github.com/godot-rust/gdext/blob/master/examples/dodge-the-creeps" rel="noopener noreferrer"&gt;example game code in the gdext GitHub repo for a Dodge the Creeps game&lt;/a&gt; (which will sound familiar if you have followed the &lt;a href="https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html" rel="noopener noreferrer"&gt;official Godot, GDScript-based, tutorial&lt;/a&gt;).  You can try building it or just dip into for help to unblock if you get stuck working on your own game.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 What I Built
&lt;/h2&gt;

&lt;p&gt;After working though the gdext Hello World, I thought a good way to learn more APIs would be to start converting a game I already had from GDScript to Godot Rust gdext.  For this, I picked the &lt;em&gt;Knights to See You&lt;/em&gt; game I made by following the  &lt;a href="https://www.youtube.com/watch?v=LOhfqjmasi0&amp;amp;pp=ygUHZ29kb3QgNA%3D%3D" rel="noopener noreferrer"&gt; How to make a Video Game - Godot Beginner Tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I followed the guidelines in the godot-rust book, but made a small change, in the project file structure.  This made it slightly more convenient to work in the Terminal from the project root folder, and also simplified the project CI configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i16nyw5yvqif1g7d6g0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i16nyw5yvqif1g7d6g0.png" alt="Godot Rust gdext: a 2D platform style game view with a blue background in three bands, becoming deeper blue as you descend. The foreground features brown and grey stone platforms.  The player character has a knight sprite, and you can see coins, a bottle, a tree, and a rope bridge.  Text to the left and centre of the view reads " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📂 Folder Structure
&lt;/h2&gt;

&lt;p&gt;The change I made from the godot-rust book, which I alluded to before was to set the project up using &lt;a href="https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html" rel="noopener noreferrer"&gt;Cargo Workspaces&lt;/a&gt;.  The &lt;code&gt;rust&lt;/code&gt; folder is exactly as the book recommends, and contains a &lt;code&gt;Cargo.toml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── Cargo.toml
├── godot
│   ├── assets
│   │   ├── fonts
│   │   ├── music
│   │   ├── sounds
│   │   └── sprites
│   ├── export_presets.cfg
│   ├── knights-to-see-you.gdextension
│   ├── project.godot
│   ├── scenes
│   └── scripts
└── rust
    ├── Cargo.toml
    └── src
        ├── lib.rs
        └── player.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I, just, added another &lt;code&gt;Cargo.toml&lt;/code&gt; file to the root directory, where I created a new workspace, adding that &lt;code&gt;rust&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[workspace]&lt;/span&gt;
&lt;span class="py"&gt;members&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"rust"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;resolver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This changes the Rust output &lt;code&gt;target&lt;/code&gt; directory, moving it up a level, so I had to update the paths listed in my &lt;code&gt;godot/&amp;lt;PROJECT&amp;gt;.gdextension&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[configuration]&lt;/span&gt;
&lt;span class="py"&gt;entry_symbol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gdext_rust_init"&lt;/span&gt;
&lt;span class="py"&gt;compatibility_minimum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;4.2&lt;/span&gt;
&lt;span class="py"&gt;reloadable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="py"&gt;linux.debug.x86_64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/debug/librust.so"&lt;/span&gt;
&lt;span class="py"&gt;linux.release.x86_64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/release/librust.so"&lt;/span&gt;
&lt;span class="py"&gt;windows.debug.x86_64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/debug/librust.dll"&lt;/span&gt;
&lt;span class="py"&gt;windows.release.x86_64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/release/librust.dll"&lt;/span&gt;
&lt;span class="py"&gt;macos.debug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/debug/librust.dylib"&lt;/span&gt;
&lt;span class="py"&gt;macos.release&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/release/librust.dylib"&lt;/span&gt;
&lt;span class="py"&gt;macos.debeg.arm64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/debug/librust.dylib"&lt;/span&gt;
&lt;span class="py"&gt;macos.release.arm64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"res://../target/release/librust.dylib"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  👀 Rust Hot Reloading and Watching Files
&lt;/h2&gt;

&lt;p&gt;GDExtension for Godot 4.2 supports hot reloading out of the box.  To speed up your coding feedback cycle, use &lt;a href="https://github.com/watchexec/cargo-watch" rel="noopener noreferrer"&gt;cargo watch to recompile your Rust code automatically&lt;/a&gt; when you save a Rust source file.  Install cargo watch (if you need to):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;cargo-watch &lt;span class="nt"&gt;--locked&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo watch &lt;span class="nt"&gt;-cx&lt;/span&gt; check &lt;span class="nt"&gt;-x&lt;/span&gt; clippy &lt;span class="nt"&gt;-x&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which will clear the window, run &lt;code&gt;cargo check&lt;/code&gt;, &lt;code&gt;cargo clippy&lt;/code&gt; then, &lt;code&gt;cargo build&lt;/code&gt; each time you save the Rust source.  Now you can hit save then jump straight to Godot Engine to test play the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦀 gdext API Snippets
&lt;/h2&gt;

&lt;p&gt;I started the switch from GDScript to Godot Rust gdext with the Player scene.  In the original game, the Player Scene was a &lt;code&gt;CharacterBody2D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rust does not easily handle Object-oriented Programming inheritance, and gdext opts for a composition approach.  Following the book, you will see the recommended pattern, here, is to create a &lt;code&gt;Player&lt;/code&gt; struct in Rust with a &lt;code&gt;base&lt;/code&gt; field of type &lt;code&gt;Base&amp;lt;ICharacterBody2D&amp;gt;&lt;/code&gt;.  That &lt;code&gt;base&lt;/code&gt; field then provides access to the character properties that you are familiar with from GDScript.&lt;/p&gt;

&lt;p&gt;Here are some API snippets (with equivalent GDScript) to give you a feel for &lt;code&gt;gdext&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating Character Velocity
&lt;/h3&gt;

&lt;p&gt;Using GDScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;
&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;gdext&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.base_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.set_velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Vector2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;base_mut()&lt;/code&gt;, here, returns a mutable reference to the &lt;code&gt;base&lt;/code&gt; field on &lt;code&gt;Player&lt;/code&gt;, mentioned above.  You set and get &lt;code&gt;CharacterBody2D&lt;/code&gt; properties using &lt;code&gt;self.base()&lt;/code&gt; and &lt;code&gt;self.base_mut()&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Gravity and Project Settings
&lt;/h4&gt;

&lt;p&gt;To get project default gravity value with GDScript, you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gravity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProjectSettings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"physics/2d/default_gravity"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The equivalent in gdext is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;gravity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ProjectSettings&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.get_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"physics/2d/default_gravity"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="py"&gt;.try_to&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Setting Animations
&lt;/h4&gt;

&lt;p&gt;The Player scene has an &lt;code&gt;AnimatedSprite2D&lt;/code&gt; node.  Typically, you can reference that node in GDScript with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;onready&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;animated_sprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;AnimatedSprite2D&lt;/span&gt;


&lt;span class="c1"&gt;# TRUNCATED...&lt;/span&gt;


&lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"idle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;gdext&lt;/code&gt;, you can set the sprite like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;animated_sprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;
    &lt;span class="nf"&gt;.base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="py"&gt;.get_node_as&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AnimatedSprite2D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AnimatedSprite2D"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Play animation&lt;/span&gt;
&lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="nf"&gt;.play_ex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idle"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full Comparison
&lt;/h3&gt;

&lt;p&gt;For reference, here is the full code for the Rust &lt;code&gt;Player&lt;/code&gt; class (&lt;code&gt;rust/src/player.rs&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;godot&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AnimatedSprite2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CharacterBody2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICharacterBody2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProjectSettings&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;godot_print&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;move_toward&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nn"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WithBaseField&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;godot_api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GodotClass&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(GodotClass)]&lt;/span&gt;
&lt;span class="nd"&gt;#[class(base=CharacterBody2D)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;jump_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CharacterBody2D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;MovementDirection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Neutral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[godot_api]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ICharacterBody2D&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CharacterBody2D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;godot_print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initialise player Rust class"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;130.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;jump_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;300.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

            &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;physics_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;Vector2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;velocity_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;velocity_y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.get_velocity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// handle jump and gravity&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_velocity_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.is_on_floor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="nf"&gt;.is_action_pressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jump"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;#[allow(clippy::cast_possible_truncation)]&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.jump_velocity&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;velocity_y&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;gravity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ProjectSettings&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;.get_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"physics/2d/default_gravity"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="py"&gt;.try_to&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should be able to represent default gravity as a 32-bit float"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nd"&gt;#[allow(clippy::cast_possible_truncation)]&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;velocity_y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gravity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Get input direction&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="nf"&gt;.get_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"move_left"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"move_right"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;movement_direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nn"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EPSILON&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nn"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EPSILON&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nn"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EPSILON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Neutral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nn"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EPSILON&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;unreachable!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;animated_sprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;
            &lt;span class="nf"&gt;.base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="py"&gt;.get_node_as&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AnimatedSprite2D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AnimatedSprite2D"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Flip the sprite to match movement direction&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;movement_direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="nf"&gt;.set_flip_h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Neutral&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="nf"&gt;.set_flip_h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Play animation&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;animation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.is_on_floor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;movement_direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Neutral&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"idle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"jump"&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="nf"&gt;.play_ex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animation&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Apply movement&lt;/span&gt;
        &lt;span class="nd"&gt;#[allow(clippy::cast_possible_truncation)]&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_velocity_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;movement_direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Neutral&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;move_toward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;velocity_x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.speed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;MovementDirection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.speed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.base_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.set_velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vector2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_velocity_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_velocity_y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.base_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.move_and_slide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the link further down for the full project code.&lt;/p&gt;

&lt;p&gt;Again, for reference, here is the previous GDScript code for the Player:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;&lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CharacterBody2D&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SPEED&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;130.0&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;JUMP_VELOCITY&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;300.0&lt;/span&gt;

&lt;span class="c1"&gt;# Get the gravity from the project settings to be synced with RigidBody nodes.&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gravity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProjectSettings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"physics/2d/default_gravity"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;onready&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;animated_sprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;AnimatedSprite2D&lt;/span&gt;


&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;_physics_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Add the gravity.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_on_floor&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;gravity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;

    &lt;span class="c1"&gt;# Handle jump.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_action_just_pressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"jump"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_on_floor&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JUMP_VELOCITY&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the input direction: -1, 0 or 1&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"move_left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"move_right"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Flip the sprite&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flip_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flip_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;true&lt;/span&gt;

    &lt;span class="c1"&gt;# Play animation&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_on_floor&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"idle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;animated_sprite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"jump"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Apply movement&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;SPEED&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;move_toward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SPEED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;move_and_slide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🤖 Adding Rust Library in Godot Engine
&lt;/h2&gt;

&lt;p&gt;Once you have coded and compiled the player (or other GDExtension class) to use it in your game, you just need to change the type of its Godot Scene.&lt;/p&gt;

&lt;p&gt;Do this by right-clicking on the scene in Godot Engine (Player scene in this case) and selecting &lt;strong&gt;Change Type…&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifi3rimt6oyvp45qqitz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifi3rimt6oyvp45qqitz.png" alt="Godot Rust gdext: The screen capture shows Godot Engine.  A small window is in focus, in the middle of the screen, with the title " title="" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, you need to search for the name you gave your class in the Rust code.  My Rust struct was also called &lt;code&gt;Player&lt;/code&gt;, and I can see it in the view as a child of &lt;code&gt;CharacterBody2D&lt;/code&gt;.  I select this and I am done!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20fltmiqm9rrak3so4h9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20fltmiqm9rrak3so4h9.png" alt="Godot Rust gdext: The screen capture shows Godot Engine.  In the left-hand panel, the Scene tab is active, and the user has opened the context menu for the Player Scene. The logo beside the Player label indicates that the Player is an instance of a Character Body 2 D class.  In the context menu, towards the top of the bottom half of the listed options, " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Godot Engine now links to the dynamic shared library I created in Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Godot Rust gdext: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this  post on Godot Rust gdext, we took a look through some resources for getting started with GDExtension for Godot 4.  In particular, we looked at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;resources for getting started with GDExtension&lt;/strong&gt; and Godot Rust;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;tip for speeding up the coding feedback cycles&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;how to &lt;strong&gt;use your GDExtension dynamic library in Godot Engine&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/knights-to-see-you" rel="noopener noreferrer"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;. I would love to hear from you, if you are also new to Godot video game development.  Were there other resources you found useful? Also, let me know what kind of game you are working on!&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Godot Rust gdext: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/" rel="noopener noreferrer"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875" rel="noopener noreferrer"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org" rel="noopener noreferrer"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/" rel="noopener noreferrer"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/" rel="noopener noreferrer"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/" rel="noopener noreferrer"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue" rel="noopener noreferrer"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>godot</category>
      <category>rust</category>
    </item>
    <item>
      <title>Trying Godot 4: Free &amp; Open-source Video GameDev</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 17 Jul 2024 16:47:42 +0000</pubDate>
      <link>https://forem.com/askrodney/trying-godot-4-free-open-source-video-gamedev-47gl</link>
      <guid>https://forem.com/askrodney/trying-godot-4-free-open-source-video-gamedev-47gl</guid>
      <description>&lt;h2&gt;
  
  
  🤖 Trying Godot 4
&lt;/h2&gt;

&lt;p&gt;I have been hearing a lot of good things about Godot for game development, and thought it would be a good time to try it out and write a post on trying Godot 4.  Godot is a &lt;strong&gt;full-featured game engine&lt;/strong&gt; like Unity or Unreal.  The biggest difference it that it is free and open-source software (&lt;strong&gt;FOSS&lt;/strong&gt;).  You need to look no further than the 3D modelling and editing Blender toolset to see how community driven development creates software on a par with proprietary alternatives.&lt;/p&gt;

&lt;p&gt;FOSS advancements can track user needs. Studios adopting Godot, instead of building and maintaining their own &lt;strong&gt;in-house engine&lt;/strong&gt;, leverage on community contributions. Further, they provide Godot improvements, useful for their own games, and then share those improvements back to the community.&lt;/p&gt;

&lt;p&gt;There are already successful Godot games available on Steam, such as &lt;a href="https://store.steampowered.com/app/1942280/Brotato/?curator_clanid=41324400" rel="noopener noreferrer"&gt;Brotato&lt;/a&gt;, &lt;a href="https://store.steampowered.com/app/1249480/ExZodiac/" rel="noopener noreferrer"&gt;Ex-Zodiac&lt;/a&gt; (in early access at time of writing), and &lt;a href="https://store.steampowered.com/app/2218750/Halls_of_Torment/" rel="noopener noreferrer"&gt;Halls of Torment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/I5wDKWydhjs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In the rest of this post, we take a closer look at why you might choose Godot and also see some video and website &lt;strong&gt;resources for starting out with Godot&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Why Godot?
&lt;/h2&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Godot has &lt;strong&gt;batteries included&lt;/strong&gt;; it is a full-featured and intuitive game engine helping you churn out your game quicker,&lt;/li&gt;
&lt;li&gt;it is FOSS, so you &lt;strong&gt;do not have to pay to use Godot&lt;/strong&gt; or pay royalties on games you create with it; and&lt;/li&gt;
&lt;li&gt;it is &lt;strong&gt;popular&lt;/strong&gt; - it keeps improving with community contributions, and you can find quality learning resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My first impression was that Godot is lightweight.  I loved that you could download, install and start coding far quicker than other engines which have gigantic downloads.  Despite that, Godot is still powerful.  Not only is Godot powerful, but the user interface, is snappy and intuitive.&lt;/p&gt;

&lt;p&gt;Finally, here, I would say the flexibility in code language for the game logic is another strength.  The easiest way to code, is to use &lt;strong&gt;GDScript&lt;/strong&gt;, which looks like Python.  However, if you need &lt;strong&gt;performance&lt;/strong&gt;, you can use &lt;strong&gt;GDExtension&lt;/strong&gt; which lets you code in C, C++ or, thanks to a &lt;a href="https://godotengine.org/article/introducing-csharp-godot/" rel="noopener noreferrer"&gt;donation from Microsoft&lt;/a&gt;, C#.  Language support does not end there though; community projects also add support for Rust, Swift, Zig and &lt;a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/what_is_gdextension.html#supported-languages" rel="noopener noreferrer"&gt;Go as well as other languages&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏅 Official Godot Getting Started Guide
&lt;/h2&gt;

&lt;p&gt;The official tutorial is well-written and provides a direct route for getting you up-to-speed on the Godot engine.  If you want to get a feel for Godot before diving in though, first try the Brackeys How to make a Video Game video tutorial (mentioned further down).  You can then loop back and learn more fundamentals once you have a better idea of whether it is something that will suit you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb5ii1u2qqm4ay24xqwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb5ii1u2qqm4ay24xqwl.png" alt="Trying Godot 4: Screen capture shows a game window with a grey background.  Towards the bottom left is a the Godot icon (a pixelated robot head in blue).  The icon is tilted to you one side."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The official guide starts with an &lt;a href="https://docs.godotengine.org/en/stable/getting_started/introduction/introduction_to_godot.html" rel="noopener noreferrer"&gt;Introduction&lt;/a&gt;, giving you an overview of Godot’s capabilities.&lt;/li&gt;
&lt;li&gt;You then jump onto some &lt;a href="https://docs.godotengine.org/en/stable/getting_started/introduction/key_concepts_overview.html" rel="noopener noreferrer"&gt;Godot concepts&lt;/a&gt;, before moving onto the Instancing Tutorial, which provides a first look at Godot Code.&lt;/li&gt;
&lt;li&gt;The guide wraps up with creating a &lt;a href="https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html" rel="noopener noreferrer"&gt;2D&lt;/a&gt; and then, &lt;a href="https://docs.godotengine.org/en/stable/getting_started/first_3d_game/index.html" rel="noopener noreferrer"&gt;3D Godot game&lt;/a&gt;, before you are let loose to build that game idea you have had running through your mind for months!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏞️ Godot Design: Scenes, Nodes and Signals
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wk58rocr919eu3hkghx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wk58rocr919eu3hkghx.png" alt="Trying Godot 4: a 2D platform style game view with a blue background in three bands, becoming deeper blue as you descend. The foreground features brown and grey stone platforms.  The player character has a knight sprite, and you can see coins, a bottle, a tree, and a rope bridge.  Text to left and centre of the view reads "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Godot promotes an Object-oriented Programming (&lt;strong&gt;OOP&lt;/strong&gt;) approach to games architecture, which contrasts to the &lt;a href="https://rodneylab.com/rust-entity-component-systems/" rel="noopener noreferrer"&gt;Entity Component System (&lt;strong&gt;ECS&lt;/strong&gt;) used by Bevy&lt;/a&gt;, for example.  &lt;/p&gt;

&lt;p&gt;Godot &lt;strong&gt;Scenes&lt;/strong&gt; are game elements, such as collectable coins, a moving platform or even a player.&lt;/p&gt;

&lt;p&gt;Scenes might be composed of multiple Godot built-in &lt;strong&gt;Nodes&lt;/strong&gt;. Nodes are OOP objects providing some logic or behaviour.  For example, the collectable coin scenes have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a 2D collider node (used to detect that the player character has walked into the coin),&lt;/li&gt;
&lt;li&gt;an &lt;code&gt;AudioStreamPlayer&lt;/code&gt; node to play a sound effect when the coin is picked up; and&lt;/li&gt;
&lt;li&gt;one each of an &lt;code&gt;AnimationSprite&lt;/code&gt; and &lt;code&gt;AnimationPlayer&lt;/code&gt; node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenes are combined into a &lt;strong&gt;Scene Trees&lt;/strong&gt;: the game scene includes collectable coin and player scenes in its scene tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signals: Communicating between Remote Scenes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Signals&lt;/strong&gt; provide a convenient interface communicating between scenes at different levels of the game.  There are built-in signals, triggered when a button is pressed, but you can also create your own.  For example, you could create a lava scene with an &lt;code&gt;on_body_entered&lt;/code&gt; signal, emitted when the player falls into the lava.  Now, the score readout, life count and other independent components just need to listen for that signal to update themselves.  No need to poll on every game tick to check if they need to update.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 Composition is Still Possible
&lt;/h3&gt;

&lt;p&gt;ECS designs favour &lt;strong&gt;composition over inheritance&lt;/strong&gt;, making certain common relationships easier to code.  Godot allows for composition, adding custom components to a class.  This is a little beyond the scope of this post, though the &lt;a href="https://www.youtube.com/watch?v=74y6zWZfQKk" rel="noopener noreferrer"&gt; How You Can Easily Make Your Code Simpler in Godot 4 video provides an excellent explanation with great examples&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖥️ Coding your Godot Game
&lt;/h2&gt;

&lt;p&gt;I mentioned above, that &lt;strong&gt;GDScript&lt;/strong&gt; is a good choice if you want to get going quickly and other languages, available via &lt;strong&gt;GDExtension&lt;/strong&gt; can provide more performant alternatives where that is most important.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trying Godot 4: GDScript
&lt;/h3&gt;

&lt;p&gt;GDScript looks a lot like Python.  In fact, earlier versions of Godot supported coding in Python, though Python was replaced by GDScript to address Python for game development shortcomings.  GDScript is quite intuitive and supports static types.  Here is an example, in case you are curious to see some code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gdscript"&gt;&lt;code&gt;

&lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Node2D&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SPEED&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;onready&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ray_cast_right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;RayCastRight&lt;/span&gt;
&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;onready&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ray_cast_left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;RayCastLeft&lt;/span&gt;


&lt;span class="c1"&gt;# Called every frame. 'delta' is the elapsed time since the previous frame.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ray_cast_right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_colliding&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ray_cast_left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_colliding&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;SPEED&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This code moves a monster back and forth between two walls. Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_process&lt;/code&gt; is the in-built function run on every frame for our scene. Engine function names begin with an underscore.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const SPEED := 60&lt;/code&gt; creates the constant property, inferring its type to be integer from the value provided.  The typing is optional, and the statement could be written &lt;code&gt;const SPEED = 60&lt;/code&gt; for dynamic typing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;var direction: int = 1&lt;/code&gt; uses an explicit static type. Godot would throw up an error if we later tried to assign a string, for example, to direction.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Learning GDScript
&lt;/h4&gt;

&lt;p&gt;The Brackeys have a &lt;a href="https://www.youtube.com/watch?v=e1zJS31tr88" rel="noopener noreferrer"&gt;broad-strokes overview of GDScript, in a one-hour video&lt;/a&gt;.  This is a different video to the Godot introduction from the same creator, mentioned further down.  Some, older, content on YouTube covers GDScript for Godot 3.  Godot 4 was released over a year ago now, and is stable, so you will probably want to focus on Godot 4 for any new games you create.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trying Godot 4: GDExtension for Rust, Swift, C++, C#, Zig and more…
&lt;/h3&gt;

&lt;p&gt;When you need performance, GDExtension will probably be your best bet.  GDExtension lets you compile a shared library using third-party C bindings.  Shared libraries are compiled independently of the game, but still can be called from your game code.  Because GDExtension creates shared libraries, you can use the same library in different games, or distribute it, without having to compile with the engine’s source code.&lt;/p&gt;

&lt;p&gt;You target a particular Godot engine version when you write GDExtension.  If you target 4.2 (latest version at time of writing), you get to use the latest Godot features and your code will be compatible with future Godot Engine versions. That said, your Godot 4.2 targeted GDExtension library might not work with Godot Engine 4.1 and earlier versions.&lt;/p&gt;

&lt;p&gt;Watch the &lt;a href="https://www.youtube.com/watch?v=c2TOIGcmQ7A" rel="noopener noreferrer"&gt;6 Programming Languages in 1 Godot Game! Trying out GDExtension!&lt;/a&gt; video to get a better fell for how this works.&lt;/p&gt;

&lt;h4&gt;
  
  
  GDExtension Tutorials
&lt;/h4&gt;

&lt;p&gt;For tutorials see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Godot’s official &lt;a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/gdextension_cpp_example.html#doc-gdextension-cpp-example" rel="noopener noreferrer"&gt;Godot C++ GDExtension tutorial&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://godot-rust.github.io/book/index.html" rel="noopener noreferrer"&gt;godot-rust book&lt;/a&gt;, which takes you through setting up your first Rust GDExtension game; or&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://migueldeicaza.github.io/SwiftGodotDocs/tutorials/swiftgodot-tutorials/" rel="noopener noreferrer"&gt;SwiftGodot tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📼 Free Video Tutorials
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1j656ai4b73fw9vmdvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1j656ai4b73fw9vmdvm.png" alt="Trying Godot 4: Screen capture shows game being edited in Godot Engine.  A panel on the left shows the Godot scene tree with a root Game node and child GameManager, TileMap, Player nodes.  Right of that, occupying most of the screen is a 2D platform style game view with a blue background in three bands, becoming deeper blue as you descend. The foreground features stone platforms, trees and coins."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some free tutorials you can access on YouTube to get going with Godot.  The first two, shorter ones assume a little previous programming knowledge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Brackeys (known for Unity content) have recently started putting out Godot 4 content.  Create a 2D platform game in the &lt;a href="https://www.youtube.com/watch?v=LOhfqjmasi0&amp;amp;pp=ygUHZ29kb3QgNA%3D%3D" rel="noopener noreferrer"&gt;How to make a Video Game - Godot Beginner Tutorial&lt;/a&gt;. Around 80 minutes.&lt;/li&gt;
&lt;li&gt;Create a rogue-like shoot-em-up in the same vein as Brotato and Halls of Torrent in the &lt;a href="https://www.youtube.com/watch?v=GwCiGixlqiU" rel="noopener noreferrer"&gt;Your First 2D GAME From Zero with GODOT 4!&lt;/a&gt; video from GDQuest.  Just over two hours.&lt;/li&gt;
&lt;li&gt;Build a 3D Godot 4 Platformer in the &lt;a href="https://www.youtube.com/watch?v=CI-cVKuSD1s&amp;amp;list=PLda3VoSoc_TTp8Ng3C57spnNkOw3Hm_35" rel="noopener noreferrer"&gt;3D for coding beginners&lt;/a&gt; series.  Creator has a background in eduction and targets all levels including coding novices. 11 videos, 5 hours in total.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If plan to work offline, remember to check the video descriptions for game asset downloads needed to complete the tutorials.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Where Next?
&lt;/h2&gt;

&lt;p&gt;If you are looking for a roadmap for getting going on Godot my two cents it to try these steps in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the Brackeys How to make a Video Game - Godot Beginner Tutorial video, for an overview of Godot building your own game in under two hours without having to get too concerned with the details;&lt;/li&gt;
&lt;li&gt;the official Godot tutorial to learn some fundamentals in more detail; and&lt;/li&gt;
&lt;li&gt;the Brackeys GDScript tutorial.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From there, you can explore a path taking in Godot aspects best suited for the game you want to work on, including the other resources listed above.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Trying Godot 4: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this trying Godot 4 post, we took a look through some resources for getting started with Godot.  In particular, I talked about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why &lt;strong&gt;you might choose Godot&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Godot options for &lt;strong&gt;coding language, including GDScript, C++, C#, Rust and Swift&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;some &lt;strong&gt;Godot 4 tutorials&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  I would love to hear from you, if you are also new to Godot video game development.  Were there other resources you found useful? Also, let me know what kind of game you are working on!&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Trying Godot 4: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/" rel="noopener noreferrer"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875" rel="noopener noreferrer"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org" rel="noopener noreferrer"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/" rel="noopener noreferrer"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/" rel="noopener noreferrer"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/" rel="noopener noreferrer"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue" rel="noopener noreferrer"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>godot</category>
    </item>
    <item>
      <title>Ratatui Audio with Rodio: Sound FX for Rust Text-based UI</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 26 Jun 2024 17:47:13 +0000</pubDate>
      <link>https://forem.com/askrodney/ratatui-audio-with-rodio-sound-fx-for-rust-text-based-ui-bhd</link>
      <guid>https://forem.com/askrodney/ratatui-audio-with-rodio-sound-fx-for-rust-text-based-ui-bhd</guid>
      <description>&lt;h2&gt;
  
  
  🔊 Adding Sound FX to a Ratatui Game
&lt;/h2&gt;

&lt;p&gt;In this post, we look at Ratatui audio with Rodio.  I have been building a text-based User Interface (&lt;strong&gt;TUI&lt;/strong&gt;) game in Ratatui.  Ratatui is Rust tooling for &lt;strong&gt;Terminal&lt;/strong&gt; apps.  This is the third post in the series. I wrote the initial post once I had put together a minimal viable product, and outlined some next steps in that post.  In the last post, I added fireworks to the victory screen by painting in a Ratatui canvas widget.&lt;/p&gt;

&lt;p&gt;As well as fireworks, another enhancement that I identified (in the first post) was sound effects.  I have built Rust games with sound before, but relied on the game’s tooling to add manage loading the audio assets and playing them (&lt;a href="https://rodneylab.com/rust-for-gaming/#rust-for-gaming-macroquad" rel="noopener noreferrer"&gt;Macroquad&lt;/a&gt; and &lt;a href="https://rodneylab.com/rust-for-gaming/#rust-for-gaming-bevy" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt;, for example, make this seamless).  So, the first step was going to be finding a crate to play &lt;strong&gt;MP3 audio&lt;/strong&gt;.  I discovered Rodio, which worked well, and was quick to get going with.&lt;/p&gt;

&lt;p&gt;In the rest of this post, I talk about the Rodio integration, and some next steps for the game.  There is a link to the latest project repo, with full code further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 Ratatui Audio with Rodio: What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprsy8uxl974crx0hwkya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprsy8uxl974crx0hwkya.png" alt="Ratatui Audio with Rodio: Screen capture shows game running in the Terminal.  The main title reads “How did you do?”.  Below, text reads You nailed it. “You hit the target!”, and below that, taking up more than half the screen, are a number of colourful dots in the shape of a recently ignited firework."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn’t really want background music, just sound effects to play when the player starts the challenge, and then to provide audio feedback if their latest solution attempt was good or perfect.   Finally, I wanted to play some audio when each firework on the victory screen ignited.  I found some quite small wave files for each of these, and converted them to MP3s.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Rodio
&lt;/h2&gt;

&lt;p&gt;Rodio uses a number of lower level crates under the hood, simplifying adding audio via their single higher level API.  These, lower-level, crates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cpal&lt;/code&gt; for playback;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;symphonia&lt;/code&gt; for MP4 and AAC playback;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hound&lt;/code&gt; for WAV playback;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lewton&lt;/code&gt; for Vorbis playback; and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claxon&lt;/code&gt; for FLAC playback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I just needed MP3s, so disabled default features, and only added &lt;code&gt;symphonia-mp3&lt;/code&gt; back in the project &lt;code&gt;Cargo.toml&lt;/code&gt;, to keep the binary size in check:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;

&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"countdown-numbers"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BSD-3-Clause"&lt;/span&gt;
&lt;span class="py"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/rodneylab/countdown-numbers"&lt;/span&gt;
&lt;span class="c"&gt;# ratatui v0.26.3 requires 1.74.0 or newer&lt;/span&gt;
&lt;span class="py"&gt;rust-version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.74"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Trying Ratatui TUI 🧑🏽‍🍳 building a text-based UI number game in the Terminal 🖥️ in Rust with Ratatui immediate mode rendering."&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;num_parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.2"&lt;/span&gt;
&lt;span class="py"&gt;rand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.8.5"&lt;/span&gt;
&lt;span class="py"&gt;ratatui&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.27.0"&lt;/span&gt;
&lt;span class="py"&gt;rodio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.18.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;default-features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"symphonia-mp3"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  🐎 Adding Rodio
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.rs/rodio/latest/rodio/" rel="noopener noreferrer"&gt;Rodio docs&lt;/a&gt; give a couple of examples for getting going.  Those examples read and decode an ogg file from a local folder within the project.  This makes uses of the Rodio decoder struct, either appending it to a Rodio sink, or playing decoder output directly on an output stream.  Either way, the audio is decoded and played straight away.&lt;/p&gt;

&lt;p&gt;That setup works well for longer files, played once.  For the game, I have a few sound effects that might be played dozens of times.  It seemed a little extravagant to read from a file and decode each time I needed to play the same sound effect.  Luckily, I found a &lt;a href="https://stackoverflow.com/questions/67988070/how-to-read-a-file-from-within-a-move-fnmut-closure-that-runs-multiple-times" rel="noopener noreferrer"&gt;Stack Overflow post with an alternative approach&lt;/a&gt;, letting you buffer the decoder output.  Since the audio files were no bigger that &lt;code&gt;10 KB&lt;/code&gt;, I was happy to buffer them as the app starts up and keep them in memory.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rust Code
&lt;/h3&gt;

&lt;p&gt;I created a &lt;code&gt;SoundEffects&lt;/code&gt; struct for holding all the sound effects, as there are only five of them.  For an app with more, I would attempt a cleaner solution, but this approach keeps things simple for what I have.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rodio&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;source&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SoundEffects&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;perfect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;firework&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;buffer_sound_effect&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;AsRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Buffered&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sound_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap_or_else&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should be able to load `{}`"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.display&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Decoder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or_else&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Should be able to decode audio file `{}`"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="nf"&gt;.buffered&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Default&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SoundEffects&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SoundEffects&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buffer_sound_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./assets/start.mp3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buffer_sound_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./assets/end.mp3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;perfect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buffer_sound_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./assets/perfect.mp3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buffer_sound_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./assets/valid.mp3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;firework&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buffer_sound_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./assets/firework.mp3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The default initializer for the &lt;code&gt;SoundEffects&lt;/code&gt; struct just creates a buffered decoding for each of the effects, which can be called later from the app code.&lt;/p&gt;

&lt;p&gt;In app code, I can then clone one of these buffers and add it to a sink to play it.  For example in the main game loop:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;run_app&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Terminal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...TRUNCATED   &lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream_handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;OutputStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Sink&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;stream_handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sound_effects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SoundEffects&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nn"&gt;event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// ...TRUNCATED&lt;/span&gt;

                &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.current_screen&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="c1"&gt;// ...TRUNCATED&lt;/span&gt;
                    &lt;span class="nn"&gt;CurrentScreen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PickingNumbers&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.is_number_selection_complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.current_screen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CurrentScreen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Playing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                &lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="nf"&gt;.append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound_effects&lt;/span&gt;&lt;span class="py"&gt;.start&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// TRUNCATED...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We initialize the sink ahead of the main loop, then play the buffered sound from within it (line &lt;code&gt;19&lt;/code&gt;), by calling &lt;code&gt;sink.append()&lt;/code&gt;.  &lt;code&gt;sink.append()&lt;/code&gt; pushes the buffer into a queue and plays it immediately (if nothing is already playing), or waits until the last sound has finished before starting.  That setup works here, and if you need to play sounds simultaneously, you can create multiple sinks.  This setup also avoids borrow checker issues with consuming the &lt;code&gt;File&lt;/code&gt; struct, which might arise when decoding the &lt;code&gt;File&lt;/code&gt; within the main loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Ratatui Audio with Rodio: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this Ratatui audio with Rodio post, I briefly ran through how I added audio to the Ratatui Countdown game.  In particular, I talked about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why I &lt;strong&gt;added Rodio&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;why &lt;strong&gt;you might buffer Rodio to avoid borrow checker issues in a game loop&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;how I &lt;strong&gt;buffered MP3 sound FX with Rodio&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/countdown-numbers" rel="noopener noreferrer"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Ratatui Audio with Rodio: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/" rel="noopener noreferrer"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875" rel="noopener noreferrer"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org" rel="noopener noreferrer"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/" rel="noopener noreferrer"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/" rel="noopener noreferrer"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/" rel="noopener noreferrer"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue" rel="noopener noreferrer"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>tui</category>
    </item>
    <item>
      <title>Ratatui for Terminal Fireworks: using Rust TUI Canvas</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 19 Jun 2024 17:00:01 +0000</pubDate>
      <link>https://forem.com/askrodney/ratatui-for-terminal-fireworks-using-rust-tui-canvas-e5l</link>
      <guid>https://forem.com/askrodney/ratatui-for-terminal-fireworks-using-rust-tui-canvas-e5l</guid>
      <description>&lt;h2&gt;
  
  
  🧨 Adding Fireworks to the Ratatui Game
&lt;/h2&gt;

&lt;p&gt;In this Ratatui for Terminal Fireworks post, I talk about how I added fireworks to the Text-based User Interface (&lt;strong&gt;TUI&lt;/strong&gt;) game I created in a recent post.  The game is based on the arithmetic challenge from the UK TV quiz, Countdown.  The Ratatui game worked already, but was little more than a minimal viable product.&lt;/p&gt;

&lt;p&gt;I mentioned some rough corners worth focussing on in that previous post.  One of those rough corners was adding some &lt;strong&gt;confetti&lt;/strong&gt; or &lt;strong&gt;fireworks&lt;/strong&gt; to the results screen when the player achieves a perfect score.  In this post, I take a quick look at how I added fireworks using the Ratatui canvas.  There is a link to the latest project repo, with full code further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 Ratatui for Terminal Fireworks: What I Built
&lt;/h2&gt;

&lt;p&gt;The game stayed mostly as it was, and only the victory screen changed, adding a fireworks animation using a Ratatui canvas widget if the player got a perfect score.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprsy8uxl974crx0hwkya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprsy8uxl974crx0hwkya.png" alt="Ratatui for Terminal Fireworks: Screen capture shows game running in the Terminal.  The main title reads “How did you do?”.  Below, text reads You nailed it. “You hit the target!”, and below that, taking up more than half the screen, are a number of colourful dots in the shape of a recently ignited firework."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧑🏽‍🎓 Ratatui Examples
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://rodneylab.com/trying-ratatui-tui/" rel="noopener noreferrer"&gt;post on Trying Ratatui TUI&lt;/a&gt;, I listed some resources for getting started with Ratatui, including three official tutorials.  Those tutorials focus on text-content, and the &lt;a href="https://docs.rs/ratatui/latest/ratatui/widgets/canvas/struct.Canvas.html" rel="noopener noreferrer"&gt;Ratatui Canvas widget&lt;/a&gt; is a better match for the fireworks animation, as I would want to draw shapes to the window at arbitrary locations.&lt;/p&gt;

&lt;p&gt;Luckily, there is another invaluable resource for getting started with Ratatui: &lt;a href="https://github.com/ratatui-org/ratatui/tree/main/examples" rel="noopener noreferrer"&gt;the examples in Ratatui’s GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ewjdxcy08lqxipmsguf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ewjdxcy08lqxipmsguf.png" alt="Ratatui for Terminal Fireworks: Canvas examples screen capture show a low-resolution world map in the left half of the terminal, outlined with green dots on a black background.  The right half of the screen shows a further two demos: a pong demo up top, and a collection of rectangles, resembling a histogram at the bottom.  The pong example has a large, yellow ball close to the middle of the screen, while the other demo features red and purple rectangles."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The canvas examples within the repo and more specifically, the Pong frame, within that collection was super helpful in getting going here, as it included example code on timing in Ratatui.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎆 Fireworks Alternative
&lt;/h2&gt;

&lt;p&gt;There is an alternative for adding fireworks, worth a mention. That combines Ratatui with the &lt;a href="https://rodneylab.com/rust-for-gaming/#rust-for-gaming-bevy" rel="noopener noreferrer"&gt;Bevy Rust game engine&lt;/a&gt;, calling Ratatui from within a Bevy app.  This alternative approach uses &lt;a href="https://github.com/joshka/bevy_ratatui" rel="noopener noreferrer"&gt;bevy_ratatui&lt;/a&gt;.  This lets you take advantage of Bevy features, such as its Entity Component System (ECS) and plugin ecosystem, while still rendering to the Terminal.&lt;/p&gt;

&lt;p&gt;At the time of writing, bevy_ratatui is still experimental.  Also, I already have a Ratatui app, and wanted to avoid re-writing it with Bevy, so decided to stick with Ratatui’s canvas widget for the firework animation.  bevy_ratatui does look promising though, and I will probably try it in another project soon.  Let me know if you have already tried it and have some feedback!&lt;/p&gt;

&lt;h2&gt;
  
  
  🖥️ My Approach using Ratatui Canvas
&lt;/h2&gt;

&lt;p&gt;Creating and drawing to the canvas widget was not too complicated.  I created a Rust Vec of firework Sparks structs. Each Spark struct had a colour (selected randomly at initialization) and current position and velocity values.  I just needed to loop over that Spark Vec to display each of them on each render.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;create_result_block_canvas&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sparks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Spark&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.check_solution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;.marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;symbols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Spark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;x_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;y_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="o"&gt;..&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sparks&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="nf"&gt;.draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;y_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="nf"&gt;.x_bounds&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;.y_bounds&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ratatui uses immediate mode, so you have to redraw every element for each frame.  I updated the position elements for each spark in an &lt;code&gt;on_tick&lt;/code&gt; method, which also runs each frames, creating the appearance of moving sparks.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Ratatui for Terminal Fireworks: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this Ratatui for Terminal fireworks post, I briefly ran through how I added fireworks to the Ratatui Countdown game.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a link to &lt;strong&gt;code examples for getting started with Ratatui&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;bevy_ratatui app&lt;/strong&gt; as an alternative route to rendering in the Terminal; and&lt;/li&gt;
&lt;li&gt;some &lt;strong&gt;code snippets and design choices for my own Ratatui game&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/countdown-numbers" rel="noopener noreferrer"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Ratatui for Terminal Fireworks: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/" rel="noopener noreferrer"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875" rel="noopener noreferrer"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org" rel="noopener noreferrer"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/" rel="noopener noreferrer"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/" rel="noopener noreferrer"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/" rel="noopener noreferrer"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue" rel="noopener noreferrer"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>tui</category>
    </item>
    <item>
      <title>Trying Ratatui TUI: Rust Text-based User Interface Apps</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 12 Jun 2024 16:42:23 +0000</pubDate>
      <link>https://forem.com/askrodney/trying-ratatui-tui-rust-text-based-user-interface-apps-37b0</link>
      <guid>https://forem.com/askrodney/trying-ratatui-tui-rust-text-based-user-interface-apps-37b0</guid>
      <description>&lt;h2&gt;
  
  
  Ratatui and Text-based User Interfaces
&lt;/h2&gt;

&lt;p&gt;I have been trying Ratatui TUI, a Rust crate for building Text-based User Interface (&lt;strong&gt;TUI&lt;/strong&gt;) apps, which typically run in the Terminal.  Ratatui, along with Go-based Bubble Tea might be responsible for the recent growth in TUIs.  Some examples of TUI apps are &lt;strong&gt;Lazygit&lt;/strong&gt;, glow, &lt;strong&gt;Soft Serve&lt;/strong&gt;, &lt;strong&gt;ATAC&lt;/strong&gt; and rlt.  C++ programmers are not left out — FTXUI is a modern C++ library for building TUIs.&lt;/p&gt;

&lt;p&gt;You use the Ratatui crate to build command line apps in Rust, though it is a little different to other Rust libraries like clap and inquire.  &lt;strong&gt;clap&lt;/strong&gt; focusses on managing and parsing command line flags (its name is an acronym: Command Line Argument Parser).  &lt;strong&gt;inquire&lt;/strong&gt;, is designed for interacting with the user, prompting them for information via the command line.  Ratatui is different to both, and gives you more control over the text-based app’s user interface layout and handling interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧑🏽‍🎓 Ratatui Learning Resources
&lt;/h2&gt;

&lt;p&gt;Ratatui is a powerful library, so there is a little to learn, and the project provides three beginner tutorials to help flatten the learning curve.  These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ratatui.rs/tutorials/hello-world/"&gt;Ratatui Hello World Tutorial&lt;/a&gt;, which is a good starting point if you are quite new to the Rust ecosystem;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ratatui.rs/tutorials/counter-app/"&gt;Counter App&lt;/a&gt;, which takes things up a level and introduces immediate mode rendering; and &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ratatui.rs/tutorials/json-editor/"&gt;JSON Editor&lt;/a&gt;, which introduces more sophisticated layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this is your first Rust app, you might want to start with either &lt;a href="https://rust-cli.github.io/book/index.html"&gt;Command line app in Rust&lt;/a&gt;, or probably &lt;a href="https://doc.rust-lang.org/stable/book/"&gt;The Rust Programming Language&lt;/a&gt;, both free web books.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 What I Built
&lt;/h2&gt;

&lt;p&gt;After following the tutorials, a nice extension was to create a basic numbers game, inspired by the &lt;a href="https://en.wikipedia.org/wiki/Countdown_(game_show)#Numbers_Round"&gt;UK TV Countdown quiz&lt;/a&gt;.  For this arithmetic challenge, the player randomly picks six numbers, then using +, -, * and / operators, tries to arrive at a target.&lt;/p&gt;

&lt;p&gt;This little game provided a natural extension to the JSON editor tutorial, mentioned above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb52a73mkga9mlvd4jyo0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb52a73mkga9mlvd4jyo0.png" alt="Ratatui for Terminal Fireworks: Screen capture shows game running in the Terminal.  The main title reads “How did you do?”.  Below, text reads You nailed it. “You hit the target!”, and below that, taking up more than half the screen, are a number of colourful dots in the shape of a recently ignited firework." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Project setup
&lt;/h2&gt;

&lt;p&gt;Beyond the &lt;code&gt;ratatui&lt;/code&gt; and &lt;code&gt;crossbeam&lt;/code&gt; crates used in the JSON tutorial, I added &lt;code&gt;num_parser&lt;/code&gt; and &lt;code&gt;rand&lt;/code&gt;.  Naturally, &lt;code&gt;rand&lt;/code&gt; was useful for generating the target and helping select the six, initial numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"countdown-numbers"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BSD-3-Clause"&lt;/span&gt;
&lt;span class="py"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/rodneylab/countdown-numbers"&lt;/span&gt;
&lt;span class="py"&gt;rust-version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.74"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Trying Ratatui TUI 🧑🏽‍🍳 building a text-based UI number game in the Terminal 🖥️ in Rust with Ratatui immediate mode rendering."&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;crossterm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.27.0"&lt;/span&gt;
&lt;span class="py"&gt;num_parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.2"&lt;/span&gt;
&lt;span class="py"&gt;rand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.8.5"&lt;/span&gt;
&lt;span class="py"&gt;ratatui&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.26.3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set the game up, so the player typed in their solution attempt, with their chosen numbers, using familiar algebraic notation.  For example, let’s say the target is &lt;code&gt;326&lt;/code&gt; and the six numbers are &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;75&lt;/code&gt;, &lt;code&gt;50&lt;/code&gt; and &lt;code&gt;25&lt;/code&gt;.  Here, it is possible to reach the target exactly, and the player could enter their solution into the Ratatui app as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(2 * 2 * 75) + 25 + 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;num_parser&lt;/code&gt; crate can parse that expression and yield the result (&lt;code&gt;326&lt;/code&gt;), significantly reducing the number of lines of code I had to write.&lt;/p&gt;

&lt;p&gt;That was all I needed for dependencies: &lt;code&gt;crossterm&lt;/code&gt;, &lt;code&gt;num_parser&lt;/code&gt;, &lt;code&gt;rand&lt;/code&gt; and &lt;code&gt;ratatui&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧑🏽‍🍳 What I Liked about Ratatui
&lt;/h2&gt;

&lt;h3&gt;
  
  
  📚 Fantastic Docs
&lt;/h3&gt;

&lt;p&gt;The three tutorials are very well written, and provide a great introduction to using Ratatui, including unit testing your app.  Beyond the tutorials, I found the &lt;a href="https://docs.rs/ratatui/latest/ratatui/"&gt;official Ratatui API docs&lt;/a&gt; were detailed enough to solve all the queries I had as I built the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Leveraging crates.io
&lt;/h3&gt;

&lt;p&gt;Being able to leverage the Rust crate ecosystem was a huge help.  I mentioned &lt;code&gt;num_parser&lt;/code&gt; did a lot of heavy-lifting.  This was quick to &lt;a href="https://crates.io/crates/num_parser"&gt;find &lt;code&gt;num_parser&lt;/code&gt; using crates.io&lt;/a&gt;.  Although a similar C++ library might exist, I don’t think I could have found it so easily.  I am not familiar enough with the Go ecosystem to comment on how that would have worked out for me, drop a comment below if you are familiar, or if you are a C++ aficionado, and know an easy way to find C++ libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  🖥️ Trying Ratatui TUI: Immediate Mode
&lt;/h3&gt;

&lt;p&gt;I kept the app structure from the JSON tutorial, that is, with three main source files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/main.rs&lt;/code&gt;: contains the main loop, draws the UI and listens for UI events, immediately calling app methods;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/app.rs&lt;/code&gt;: manages app state and provides helper functions the app calls on user event; and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/ui.rs&lt;/code&gt;: generates the UI based on the current app state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The immediate mode pattern worked well, writing how the app UI should look for any given state, with Rust pattern matching helping me get going quicker, making sure I covered all possible states, with no surprises.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4tfl22zj3deoxre71f0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4tfl22zj3deoxre71f0.png" alt="Ratatui for Terminal Fireworks: Canvas examples screen capture show a low-resolution world map in the left half of the terminal, outlined with green dots on a black background.  The right half of the screen shows a further two demos: a pong demo up top, and a collection of rectangles, resembling a histogram at the bottom.  The pong example has a large, yellow ball close to the middle of the screen, while the other demo features red and purple rectangles." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an example of how this worked, we might look at the initial number picking.  The player presses [ to pick a small number and ] for a large one.  These key presses are captured in event listener code in &lt;code&gt;src/main.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.current_screen&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;CurrentScreen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Introduction&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.current_screen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CurrentScreen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PickingNumbers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;CurrentScreen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PickingNumbers&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.is_number_selection_complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.current_screen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CurrentScreen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Playing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;']'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.pick_random_large_number&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'['&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.pick_random_small_number&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// TRUNCATED...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So pressing those square bracket keys triggers an app state update (&lt;code&gt;src/app.rs&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;pick_random_large_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.random_available_large_number_index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.available_large_numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index_value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;picked_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.selected_numbers&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="nf"&gt;.is_none&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;picked_index_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;picked_index&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.selected_numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;picked_index_value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.available_large_numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index_value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the UI model is updated (&lt;code&gt;src/ui.rs&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;create_selected_numbers_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Paragraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;selected_numbers_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.selected_numbers&lt;/span&gt;&lt;span class="nf"&gt;.into_iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Numbers: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;())],&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;accum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;accum&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{value} "&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="nn"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;accum&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_ "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="n"&gt;accum&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// TRUNCATED...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hence, the current app state gets reflected on the next enumeration of the event loop.&lt;/p&gt;

&lt;p&gt;There is a link further down to the complete code for the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Trying Ratatui TUI: What Next?
&lt;/h2&gt;

&lt;p&gt;So far, I have a minimal proof of concept.  I would like to remove some rough edges.  In particular:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add some fireworks to make the victory screen feel a little more of a celebration;&lt;/li&gt;
&lt;li&gt;add sound feedback when the user hits keys; and&lt;/li&gt;
&lt;li&gt;improve gameplay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For improving gameplay, there are already different messages depending on how close to the target you got.  However, I would like to take this to the next level, adding a game timer and putting best times on a high-score board.  This definitely provides motivation for me playing &lt;a href="https://apps.apple.com/gb/app/sudoku-pi/id6467504425"&gt;Sudoku Pi (also written in Rust)&lt;/a&gt;, and I hope it will translate to this game.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Trying Ratatui TUI: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post on trying Ratatui TUI, we looked got an introduction to immediate mode user interfaces in Rust.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TUI &lt;strong&gt;alternatives to Rust’s Ratatui&lt;/strong&gt; in Go and C++;&lt;/li&gt;
&lt;li&gt;other &lt;strong&gt;Rust Terminal app tooling&lt;/strong&gt; and how Ratatui is different; and&lt;/li&gt;
&lt;li&gt;some &lt;strong&gt;learning resources for getting started with Ratatui&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/countdown-numbers"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Trying Ratatui TUI: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>tui</category>
    </item>
    <item>
      <title>Using egui for Bevy ECS Introspection with Macroquad Rendering</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 05 Jun 2024 17:19:46 +0000</pubDate>
      <link>https://forem.com/askrodney/using-egui-for-bevy-ecs-introspection-with-macroquad-rendering-1h84</link>
      <guid>https://forem.com/askrodney/using-egui-for-bevy-ecs-introspection-with-macroquad-rendering-1h84</guid>
      <description>&lt;h2&gt;
  
  
  👀 Using egui for Bevy ECS Introspection
&lt;/h2&gt;

&lt;p&gt;In this post, we look at using egui for Bevy Introspection with Macroquad rendering.  This continues the series of recent posts, which started with adding egui DevTools to Macroquad, then added Rapier physics, units of measurement and an Entity Component system (&lt;strong&gt;ECS&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Here, we bring everything together, adding DevTools-like introspection to the Bevy ECS demo from the previous post.  As before, the complete project code is on GitHub (link further down).&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 What are we Building Here?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fythoji3g090q87wkak96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fythoji3g090q87wkak96.png" alt="Using egui for Bevy ECS Introspection: A number of blue, yellow and orange bubbles are trapped at the top of the screen.  The Developer tools panel appears to contain details for all of them, with the details for just 2 or 3 scrolled into view.  The headings Entity Generation:ID 1:8 and Entity Generation:ID 1:9 are visible.  These top sections with Position, Velocity, Colour, and Radius data for their respective bubble." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo simulation, built in Rust, shows a series of coloured, floating bubbles release and float to top of the window.  The bubbles subsequently get trapped at the top of the screen; filling the window, from the top down.&lt;/p&gt;

&lt;p&gt;I employed the following tools to build the demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Macroquad&lt;/strong&gt; — a fast, quick-to-get going on Rust tool built with game prototyping in mind;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapier&lt;/strong&gt; — mature, pure Rust physics engine for game dev;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;uom&lt;/strong&gt; — a crate with applications in Aerospace for maintaining consistency in physical quantity units and helping to avoid unit errors;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bevy ECS&lt;/strong&gt; — ECS implementation from the Bevy game engine, added to this project as the stand-alone &lt;code&gt;bevy_ecs&lt;/code&gt; crate; and&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;egui&lt;/strong&gt; — an immediate Graphics User Interface (&lt;strong&gt;GUI&lt;/strong&gt;) library for Rust (inspired, in fact, by the &lt;a href="https://rodneylab.com/using-raylib-with-dear-imgui/"&gt;C++ Dear ImGui library&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I focus on the egui integration in this post, so feel free to jump back through the series to learn more about how I put the other elements together.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Crate Setup
&lt;/h2&gt;

&lt;p&gt;You might need to use slightly older (than the latest) versions of some crates to get everything working together.  Here is my &lt;code&gt;Cargo.toml&lt;/code&gt;, which you can use as a starting point for finding compatible versions of &lt;code&gt;egui&lt;/code&gt;, &lt;code&gt;egui-macroquad&lt;/code&gt; and &lt;code&gt;macroquad&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"macroquad-bevy-ecs-introspection"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BSD-3-Clause"&lt;/span&gt;
&lt;span class="py"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/rapier-example"&lt;/span&gt;
&lt;span class="c"&gt;# bevy_ecs 0.13 requires MSRV 1.76&lt;/span&gt;
&lt;span class="py"&gt;rust-version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.76"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Macroquad Rapier ECS 🦀 Rust game dev — using bevy's 🧩 Entity Component System in a Macroquad game with Rapier physics."&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;bevy_ecs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.13.2"&lt;/span&gt;
&lt;span class="py"&gt;crossbeam&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.8.4"&lt;/span&gt;
&lt;span class="py"&gt;egui&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.21.0"&lt;/span&gt;
&lt;span class="py"&gt;egui-macroquad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.15"&lt;/span&gt;
&lt;span class="py"&gt;macroquad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3.26"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;default-features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;rand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.8.5"&lt;/span&gt;
&lt;span class="py"&gt;rand_distr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.4.3"&lt;/span&gt;
&lt;span class="py"&gt;rapier2d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.19.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"simd-stable"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;uom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.36.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔄 Update egui Introspection System
&lt;/h2&gt;

&lt;p&gt;I &lt;a href="https://rodneylab.com/macroquad-rapier-ecs/"&gt;introduced Bevy ECS to the project in a previous Macroquad Rapier ECS post&lt;/a&gt;.  Continuing, we need to add a couple of ECS systems for updating and rendering the egui DevTools.  First up is the &lt;code&gt;update_dev_tools_system&lt;/code&gt; in &lt;code&gt;src/systems.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;update_dev_tools_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CircleMesh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;egui_macroquad&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="nf"&gt;.set_pixels_per_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;egui&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Developer Tools"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;ScrollArea&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;vertical&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;CollapsingHeader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bubbles"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.default_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bubble_velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;circle_mesh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nf"&gt;draw_ball_ui_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bubble_velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;circle_mesh&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bevy ECS Queries
&lt;/h3&gt;

&lt;p&gt;Typically, for Bevy ECS, this function takes a query over ECS components as its argument (line &lt;code&gt;107&lt;/code&gt;).  Here, the query generates a collection of all ECS entities that have &lt;code&gt;Position&lt;/code&gt;, &lt;code&gt;Velocity&lt;/code&gt;  and &lt;code&gt;CircleMesh&lt;/code&gt; components (essentially the bubbles). Additionally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In line &lt;code&gt;109&lt;/code&gt;, I call &lt;code&gt;set_pixels_per_point&lt;/code&gt; this scales up the UI. &lt;code&gt;4.0&lt;/code&gt; worked for screen captures, though for general use, you will probably want something a touch lower.&lt;/li&gt;
&lt;li&gt;We need a &lt;code&gt;ScrollArea&lt;/code&gt; in the UI (line &lt;code&gt;111&lt;/code&gt;) to make viewing data for the dozens of balls in the simulation more practical.&lt;/li&gt;
&lt;li&gt;You can iterate over the ECS entities satisfying the initial query, with a simple for loop (line &lt;code&gt;115&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we look at the &lt;code&gt;draw_ball_ui_data&lt;/code&gt; function called within that last loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  🫧 Individual Bubble Updates
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk4gjr4t05kuexwp6c7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk4gjr4t05kuexwp6c7u.png" alt="Using egui for Bevy ECS Introspection: An orange bubble is trapped in the top, right corner of the window.  Towards the left, is the Developer Tools Panel.  This show properties for the bubble, reading Entity Generation:ID 1:0, Position x: 26.26 m, y: -0.65m, Velocity x: -0.00 m/s, y: 0.00 m/s, Colour (orange dot is displayed), Radius 0.6m" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another advantage of using the &lt;code&gt;uom&lt;/code&gt; crate for handling physical quantities is formatting of those values for output, which we use in the &lt;code&gt;draw_ball_ui_data&lt;/code&gt; function (&lt;code&gt;src/systems&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;draw_ball_ui_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bubble_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;circle_mesh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CircleMesh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;format_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Abbreviation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;m_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;VelocityUnit&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;format_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Abbreviation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nn"&gt;CollapsingHeader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Entity Generation:ID {}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.generation&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.default_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.horizontal&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Position"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"x: {:.2}, y: {:.2}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="c1"&gt;// TRUNCATED...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quantity Formatting
&lt;/h3&gt;

&lt;p&gt;To select preferred formatting, in lines &lt;code&gt;55&lt;/code&gt; and &lt;code&gt;56&lt;/code&gt;, we add unit formatting for metre and metre per second quantities.  These uom format args incorporate type checking, so you would get a compile-time error if you tried to format a length quantity using metres per second, for example.&lt;/p&gt;

&lt;p&gt;Notice, in for example, in lines &lt;code&gt;67&lt;/code&gt;-&lt;code&gt;70&lt;/code&gt; that we can use standard Rust formatting arguments with the &lt;code&gt;uom&lt;/code&gt; format arg to specify the precision we want position output data with.  Here, the &lt;code&gt;{:.2}&lt;/code&gt; format specifiers indicates we want to round the position values to two decimal places.&lt;/p&gt;

&lt;h3&gt;
  
  
  ECS Entity Generations and IDs
&lt;/h3&gt;

&lt;p&gt;ECS entities are not a lot more than an integer ID and a generation.  The generation provides a mechanism for reusing IDs.  For example, the first bubble spawned might have ID &lt;code&gt;0&lt;/code&gt; and generation &lt;code&gt;1&lt;/code&gt;.  If we then despawned that bubble from the ECS, the next bubble can re-use ID &lt;code&gt;0&lt;/code&gt;, which is now free.  However, the ECS would give it a generation of &lt;code&gt;2&lt;/code&gt;, so it can be distinguished from the earlier one.&lt;/p&gt;

&lt;p&gt;We access, both &lt;code&gt;generation&lt;/code&gt; an &lt;code&gt;id&lt;/code&gt; from the Bevy ECS &lt;code&gt;Entity&lt;/code&gt; struct calling &lt;code&gt;.generation()&lt;/code&gt; and &lt;code&gt;.index()&lt;/code&gt; in lines &lt;code&gt;59&lt;/code&gt; and &lt;code&gt;60&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✍🏽 Draw UI System
&lt;/h2&gt;

&lt;p&gt;The system for actually drawing the DevTools panel, using &lt;code&gt;egui&lt;/code&gt; is a little simpler — we just need to call &lt;code&gt;egui_macroquad::draw&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;draw_dev_tools_system&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;egui_macroquad&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📆 Bringing it all together: ECS Schedule
&lt;/h2&gt;

&lt;p&gt;I added the two new systems to the Bevy ECS schedule, to give us some control over the order Bevy ECS runs them in (&lt;code&gt;src/main.rs&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;playing_schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Schedule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;playing_schedule&lt;/span&gt;
        &lt;span class="nf"&gt;.configure_sets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nn"&gt;ScheduleSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BeforeSimulation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nn"&gt;ScheduleSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Simulation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nn"&gt;ScheduleSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AfterSimulation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nf"&gt;.chain&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.add_systems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;create_ball_physics_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;update_dev_tools_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;draw_balls_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;draw_dev_tools_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="nf"&gt;.chain&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nf"&gt;.chain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="nf"&gt;.in_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ScheduleSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BeforeSimulation&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.add_systems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step_physics_engine_system&lt;/span&gt;&lt;span class="nf"&gt;.in_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ScheduleSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Simulation&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;.add_systems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;update_balls_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;spawn_new_ball_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;end_simulation_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nf"&gt;.in_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ScheduleSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AfterSimulation&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Bevy ECS, schedule systems grouped into a tuple will run in parallel.  Tacking &lt;code&gt;.chain()&lt;/code&gt; onto the end of the tuple tells the ECS that you want the systems to run in series, in the order of appearance.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 What Next?
&lt;/h2&gt;

&lt;p&gt;I have the basic, minimal DevTools panel working now.  In terms of extensions and where to go next, I am considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding a wireframe view mode, with toggle, to show/hide the Rapier colliders;&lt;/li&gt;
&lt;li&gt;buttons for pausing, stepping and restarting the simulation from the dev panel tool; and&lt;/li&gt;
&lt;li&gt;adding the collider properties to the panel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interested to know if you have some ideas on and also, what else might be a good direction to take this in.  Drop a comment below, or &lt;a href="https://links.rodneylab.com/"&gt;reach out on other channels&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Using egui for Bevy ECS Introspection: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post on using egui for Bevy ECS introspection, we looked at displaying  Rapier physical properties with egui and Macroquad rendering.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code for adding &lt;strong&gt;egui update and draw ECS systems&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;how you can add an &lt;strong&gt;egui ScrollArea to display large data collections&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;how you can &lt;strong&gt;format uom quantities for display, including rounding to a fixed number of decimal places&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/rapier-bevy-ecs-introspection"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Using egui for Bevy ECS Introspection: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>bevy</category>
    </item>
    <item>
      <title>Macroquad Rapier ECS: Using Bevy ECS in Macroquad Game</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Thu, 30 May 2024 08:03:14 +0000</pubDate>
      <link>https://forem.com/askrodney/macroquad-rapier-ecs-using-bevy-ecs-in-macroquad-game-f06</link>
      <guid>https://forem.com/askrodney/macroquad-rapier-ecs-using-bevy-ecs-in-macroquad-game-f06</guid>
      <description>&lt;h2&gt;
  
  
  🐣 Using Bevy Entity Component System outside of Bevy
&lt;/h2&gt;

&lt;p&gt;Not every game needs an Entity Component System  (&lt;strong&gt;ECS&lt;/strong&gt;), but for this Macroquad Rapier ECS post, I was keen to see how you might add an ECS to a Macroquad game with Rapier physics.&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://rodneylab.com/deploying-your-rust-wasm-game/"&gt;previous post, I used the Shipyard ECS with Macroquad&lt;/a&gt;.  I have been working through a build-your-own physics engine tutorial, which used the Bevy game engine.  I loved the &lt;strong&gt;ergonomics&lt;/strong&gt; of the embedded ECS, so thought, I would take it for a spin here.  &lt;strong&gt;Rapier&lt;/strong&gt; the Rust physics engine used here has a Bevy plugin, but I wanted to try using both Rapier and the stand-alone &lt;strong&gt;Bevy ECS outside of Bevy&lt;/strong&gt;, just for the challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 Macroquad Rapier ECS: What I Built
&lt;/h2&gt;

&lt;p&gt;I continued with the bubble simulation, used for a few recent posts.  It had no ECS, dozens of entities and a few other features that made it interesting to try with an ECS data model.  So that was my starting point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4to771ahbgvbewi3sdcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4to771ahbgvbewi3sdcq.png" alt="Macroquad Rapier ECS: A collection of yellow, orange, and blue balls have floated to the top of the window in a screen-capture.  They are tightly packed, though not evenly distributed, with the collection being more balls deep at the centre of the window." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simulation releases bubbles which float to the top of the screen, where they are trapped by a Rapier height field.  Once all existing bubbles are settled, the simulation spawns a fresh one.&lt;/p&gt;

&lt;p&gt;The simulation uses a random number generator to decide on the initial velocity of spawned balls, and has running and paused states.  The simulation triggers the paused state when the bubbles almost completely fill the window.  Both the random number generator and the simulation state are &lt;strong&gt;singletons&lt;/strong&gt;, and fit well into the ECS resource model.  I also put the physics engine itself into an ECS resource.  The &lt;a href="https://rodneylab.com/c++-game-dev-libraries/"&gt;C/C++ flecs ECS library&lt;/a&gt;, for example, calls resources singletons.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Simulation within the ECS Model
&lt;/h3&gt;

&lt;p&gt;As you would expect, each bubble is an entity.  If you are new to ECSs you might imagine the ECS entities as rows of a database table.  In this database world, the table columns are components.  In my case, the balls all have the same components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a circle static mesh (which encodes colour and size data needed for rendering);&lt;/li&gt;
&lt;li&gt;a circle collider (for handling physical collisions with Rapier); and &lt;/li&gt;
&lt;li&gt;position and velocity components (used for updating the simulation physics).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Systems mutate the component properties, for example, there is a ball update system, which uses Rapier, to work out what the current velocity of the bubble is, at each step, then update the velocity component.&lt;/p&gt;

&lt;p&gt;In the following sections, we look at some code snippets to help illuminate the points here, and hopefully give you an idea of how I put the simulation together, adding an ECS to Macroquad. The &lt;strong&gt;full code is on GitHub&lt;/strong&gt;, and there is a link to it further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Components
&lt;/h2&gt;

&lt;p&gt;I just mentioned that bubble entities each have a few components.  Here is some example code for spawning a new ball using the simulation ECS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_ball_entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
    &lt;span class="nf"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_ball_position&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;CircleMesh&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BALL_COLOURS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;macroquad_rand&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;gen_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BALL_COLOURS&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;())],&lt;/span&gt;
        &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BALL_RADIUS&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;CircleCollider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BALL_RADIUS&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;physics_handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_ball_velocity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new entity gets a &lt;code&gt;Position&lt;/code&gt;, &lt;code&gt;CircleMesh&lt;/code&gt;, &lt;code&gt;CircleCollider&lt;/code&gt; and &lt;code&gt;Velocity&lt;/code&gt;.  The number of components here is arbitrary, and not fixed (as it might be when calling a constructor using an object-oriented approach).  This grants flexibility as you develop the simulation or game.&lt;/p&gt;

&lt;p&gt;Also note, I follow a Rust convention in naming the &lt;code&gt;_ball_entity&lt;/code&gt; identifier.  The initial underscore (&lt;code&gt;_&lt;/code&gt;) indicates the identifier is not used anywhere else in the scope.  We do not need it later, since the systems used to query and mutate the component properties, operate on entities with specific sets of components (properties). We will see this more clearly later.&lt;/p&gt;

&lt;p&gt;In an ECS model, the entity is not much more than an integer, which Bevy ECS can use as an ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Units of Measurement
&lt;/h3&gt;

&lt;p&gt;In a previous &lt;a href="https://rodneylab.com/rapier-physics-with-units-of-measurement/"&gt;post on adding units of measurement to a Rapier game&lt;/a&gt;, I introduced the length units used in the snippet above. This pattern of using units leveraging Rusts type system, sense-checking calculations and helping to avoid unit conversion errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏷️ Macroquad Rapier ECS: Tags
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Position&lt;/code&gt; and &lt;code&gt;Velocity&lt;/code&gt; components, mentioned before, are structs with associated data.  You can also have ECS tags, which are akin to components without data.  Rapier has a type of collider that just detects a collision, and beyond that does not interact with the physics system, these are &lt;strong&gt;sensors&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0vkr33vv6180kg7ovuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0vkr33vv6180kg7ovuu.png" alt="Macroquad Rapier ECS: A collection of yellow, orange, and blue balls have floated to the top of the window in a screen-capture.  They are tightly packed, though not evenly distributed, with the ball reaching almost down to the ground in the centre.  A lone ball sits bottom centre on the floor of the window." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used a &lt;code&gt;Sensor&lt;/code&gt; tag in the ECS for colliders that are sensors, which helps to separate them out when running systems.  The only sensor in the simulation runs along the bottom of the window.  Towards the end of the simulation, when the window is almost full, a newly spawned bubble will inevitably bounce off an existing one and collide with the floor sensor.  I added a system to pause the simulation when this occurs, effectively ending the simulation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Component,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CuboidCollider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;half_extents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vector2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Isometry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Complex&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vector2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Component,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Sensor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, above, code snippet shows a &lt;code&gt;CuboidCollider&lt;/code&gt; (used for the floor sensor), which is a regular component and then the &lt;code&gt;Sensor&lt;/code&gt; tag.  The code snippet, below, initializes the floor sensor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;spawn_ground_sensor_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;collider_half_thickness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;window_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WINDOW_WIDTH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;window_height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WINDOW_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
        &lt;span class="n"&gt;CuboidCollider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;half_extents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;window_width&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;collider_half_thickness&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;window_height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;collider_half_thickness&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Isometry2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Sensor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, the &lt;code&gt;Sensor&lt;/code&gt; tag is included.  To spawn the wall colliders on either side of the window, a very similar block is used, only omitting the &lt;code&gt;Sensor&lt;/code&gt; tag.  We will see how this can be used with systems next. &lt;/p&gt;

&lt;h2&gt;
  
  
  🎺 Systems
&lt;/h2&gt;

&lt;p&gt;Systems are code blocks, which are only run on components belonging to a specified component set.  As an example, here is the system for updating bubble position and velocity within the game loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;update_balls_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CircleCollider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;physics_engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PhysicsEngine&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;circle_collider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;circle_collider&lt;/span&gt;&lt;span class="py"&gt;.physics_handle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ball_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;physics_engine&lt;/span&gt;&lt;span class="py"&gt;.rigid_body_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ball_body&lt;/span&gt;&lt;span class="nf"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ball_body&lt;/span&gt;&lt;span class="nf"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nn"&gt;VelocityUnit&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ball_body&lt;/span&gt;&lt;span class="nf"&gt;.linvel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nn"&gt;VelocityUnit&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ball_body&lt;/span&gt;&lt;span class="nf"&gt;.linvel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This system runs on any entity that satisfies the query of having &lt;code&gt;Position&lt;/code&gt;, &lt;code&gt;Velocity&lt;/code&gt; and &lt;code&gt;CircleCollider&lt;/code&gt; components.  We can be more prescriptive, choosing entities that have a set of components, and also, either do, or do not have some other component or tag.  We use &lt;code&gt;With&lt;/code&gt; when creating the floor sensor during the simulation initialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;create_cuboid_sensors_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CuboidCollider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;With&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Sensor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;physics_engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResMut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PhysicsEngine&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;collider&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_collider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nn"&gt;ColliderBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cuboid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collider&lt;/span&gt;&lt;span class="py"&gt;.half_extents.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collider&lt;/span&gt;&lt;span class="py"&gt;.half_extents.y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collider&lt;/span&gt;&lt;span class="py"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collider&lt;/span&gt;&lt;span class="py"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.sensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;physics_engine&lt;/span&gt;&lt;span class="py"&gt;.collider_set&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_collider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you might expect, the equivalent code for creating the side wall colliders uses &lt;code&gt;Without&lt;/code&gt; in its query, and omits &lt;code&gt;.sensor(true)&lt;/code&gt; in its Rapier setup code.&lt;/p&gt;

&lt;h2&gt;
  
  
  📆 Schedules
&lt;/h2&gt;

&lt;p&gt;We use ECS schedules to determine when systems run.  The simulation has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a setup schedule, run once during simulation setup,&lt;/li&gt;
&lt;li&gt;a running schedule, executed on every run through the game loop in simulate/running mode; and&lt;/li&gt;
&lt;li&gt;a paused schedule, runs when we have paused the simulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bevy ECS organizes the systems above into these schedules, which can include constraints to ensure systems run in the right order.&lt;/p&gt;

&lt;p&gt;Here is the paused schedule code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;paused_schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Schedule&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;paused_schedule&lt;/span&gt;&lt;span class="nf"&gt;.add_systems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draw_balls_system&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just needs to draw the balls (the screen is cleared in each game loop iteration, even while the simulation is paused). The running schedule features more systems.&lt;/p&gt;

&lt;p&gt;That code above is triggered in the game loop, while the simulation state is set to paused:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TRUNCATED...&lt;/span&gt;
        &lt;span class="nf"&gt;clear_background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GUNMETAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;simulation_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;
            &lt;span class="py"&gt;.get_resource&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SimulationState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected simulation state to have been initialised"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;simulation_state&lt;/span&gt;&lt;span class="py"&gt;.mode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;SimulationMode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;playing_schedule&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;SimulationMode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Paused&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;paused_schedule&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;next_frame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! We covered  all the major constituents of an ECS!&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Macroquad Rapier ECS: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post on Macroquad Rapier ECS, we got an introduction to working with an ECS in Macroquad.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the main constituents of an ECS including &lt;strong&gt;resources, schedules and tags, as well as entities, components, and systems&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;why you might want to add &lt;strong&gt;tags, and how you can use them in Bevy ECS system queries along with &lt;code&gt;With&lt;/code&gt; and &lt;code&gt;Without&lt;/code&gt;&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS schedules for different game or simulation states&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/macroquad-rapier-bevy-ecs"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Macroquad Rapier ECS: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>bevy</category>
    </item>
    <item>
      <title>Unreal Engine 5 macOS: UE5 C++ Game Dev</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 22 May 2024 16:40:00 +0000</pubDate>
      <link>https://forem.com/askrodney/unreal-engine-5-macos-ue5-c-game-dev-3co5</link>
      <guid>https://forem.com/askrodney/unreal-engine-5-macos-ue5-c-game-dev-3co5</guid>
      <description>&lt;h2&gt;
  
  
  🧑🏽‍💻 Working with UE 5.4 on macOS
&lt;/h2&gt;

&lt;p&gt;In this post, we see how you can work in Unreal Engine 5 on macOS.  When using C++ with Unreal Editor, the official UE5 editor for macOS is Xcode, though we see how you can set up VS Code to work with UE5.  This is a common developer editor choice and worth considering for UE5, if you are already familiar with it.  We also see how you can make sure you have the right version of Xcode installed on your Mac, to work with UE 5.4.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Installing UE5
&lt;/h2&gt;

&lt;p&gt;To get going in Unreal Editor, you will first need to download the Epic Games Launcher.  This manages your Unreal Engine install, letting you download Unreal Editor and flip between different installed versions.&lt;/p&gt;

&lt;p&gt;You can install the Epic Games Launcher with Homebrew, from the Terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; epic-games
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have that, the actual Unreal Engine install can be quite large and take a while.  To get started, open the Epic Games Launcher and create an account, if you do not already have one.  Once you are set up, select &lt;strong&gt;Unreal Engine&lt;/strong&gt; from the sidebar on the left, then &lt;strong&gt;Library&lt;/strong&gt; from the toolbar, which run along the top of the window.  You should see an icon showing the latest Unreal Engine version, which you can click to start the download.  You can jump ahead to the next section, while you wait for the download.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Installing Xcode for Unreal Engine on macOS
&lt;/h2&gt;

&lt;p&gt;Even though we do not use Xcode as the editor here, you will need Xcode installed on your system for compiling under the hood.  The officially supported Xcode version, may not be the latest Xcode version (even though you are using the latest version of Unreal).  As an example, at the time of writing, you should opt for Xcode 14.3.1 for use with the current Unreal release (5.4.1).&lt;/p&gt;

&lt;p&gt;You can check the &lt;a href="https://docs.unrealengine.com/5.3/en-US/ios-ipados-and-tvos-development-requirements-for-unreal-engine/"&gt;Unreal Engine Xcode requirements for your Unreal Engine version in the official UE docs&lt;/a&gt;.  It is important to use a supported version.  As an example, I was able to compile code successfully using Xcode 15 (and UE5), but when I tried to re-open the project in Unreal Engine, I got a “The following modules are missing or built with a different engine version” error below (even after rebuilding).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ujv2jvgd3a8zehjrbyn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ujv2jvgd3a8zehjrbyn.png" alt='Unreal Engine 5 macOS: Screenshot shows Unreal Engine Editor error dialogue advising "The following modules are missing or built with a different engine version".' width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, by downgrading to Xcode 14.3.1, I got everything to run smoothly.  You can download the latest version of Xcode from the macOS App Store, using a shortcut already on your dock or Launchpad.  However, if you require an older version, you will need to &lt;a href="https://developer.apple.com/download/all/?q=Xcode"&gt;use the Apple Developer site to download Xcode&lt;/a&gt;, logging in and completing any two-factor authorization requests.  I would recommend using Safari for the download; the file will be quite big and Safari supports resuming your download, in case the connection drops.&lt;/p&gt;

&lt;p&gt;You can have Unreal Engine generate the necessary VS Code project configuration by setting VS Code as your editor, then opening VS Code from the menu in Unreal Engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 Running Unreal Engine builds in VS Code
&lt;/h2&gt;

&lt;p&gt;With Xcode and Unreal Engine set up, let’s have a look at how to run builds from VS Code.  To build your Unreal project using VS Code, install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools"&gt;C/C++ VS Code extension from Microsoft&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcvo63uskmcwkut7y63d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcvo63uskmcwkut7y63d.png" alt="Unreal Engine with Neovim: Screen capture shows the C/C++ extension by Microsoft preview in the V S Code extensions view.  Current version is v.1.18.5 and there have been around 59 million downloads." width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are ready to roll.  To run a development build, go to the &lt;strong&gt;Terminal&lt;/strong&gt; menu and select &lt;strong&gt;Run build Task…&lt;/strong&gt;.  From the options, choose  &lt;strong&gt;[YourProjectName]Editor Mac Development Build&lt;/strong&gt;.  This will build your project, and you will be able to see any changes you made with the Unreal Engine Editor UI.  Run a quick rebuild, from within Unreal Engine Editor, by clicking the “Recompile on the fly” button in the status bar at the bottom right of the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Unreal Engine 5 macOS: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this Unreal Engine 5 macOS post, we saw how you can get going with game development in Unreal Engine using macOS.  More specifically, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to &lt;strong&gt;install Epic Games Launcher and Unreal Engine 5&lt;/strong&gt; on macOS;&lt;/li&gt;
&lt;li&gt;checking you have the &lt;strong&gt;right Xcode version for your UE5 install&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;how to &lt;strong&gt;set up VS Code as an Unreal Engine C++ editor&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  Do let me know if you found this content useful and would like to see more similar content.  Also reach out if there is anything I could improve to provide a better experience for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Unreal Engine 5 macOS: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>gamedev</category>
      <category>unrealengine</category>
    </item>
    <item>
      <title>Rapier Physics with Units of Measurement: Utilize Rust Types</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 15 May 2024 17:14:45 +0000</pubDate>
      <link>https://forem.com/askrodney/rapier-physics-with-units-of-measurement-utilize-rust-types-376n</link>
      <guid>https://forem.com/askrodney/rapier-physics-with-units-of-measurement-utilize-rust-types-376n</guid>
      <description>&lt;h2&gt;
  
  
  Unit of Measurement in Game Dev
&lt;/h2&gt;

&lt;p&gt;In this post, we will take a quick look at Rapier physics with &lt;strong&gt;units of measurement&lt;/strong&gt; (&lt;strong&gt;UOM&lt;/strong&gt;).  In a recent post, trying out the Rust Rapier physics engine with Macroquad, we noted that Rapier recommends using full-scale measurements for more realistic simulations.  We opted for &lt;strong&gt;SI units&lt;/strong&gt; (metres for distance and metres per second for velocities).  There was necessary conversion from these physical units to pixels, for rendering.&lt;/p&gt;

&lt;p&gt;This got me thinking about leveraging Rust’s types for conversion between the physics and graphics units.  I discovered the Rust &lt;strong&gt;uom crate&lt;/strong&gt;, which has applications in Aerospace.  In his Rust Nation UK talk, Lachezar Lechev mentioned how confusion over units might have been behind a &lt;strong&gt;costly financial loss&lt;/strong&gt; in a &lt;strong&gt;real-world Aerospace project&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If professional teams working full-time on a project with such high financial stakes can make mistakes, then, probably, anyone is prone to making similar mistakes.  So, I considered adding UOM to my Rust game stack and talk about how I set it up in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  📏 &lt;code&gt;uom&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;uom&lt;/code&gt; crate, has applications in Aerospace and Aeronautical Engineering.  By defining quantities with a unit of measurement, it can catch basic errors at compile time.  As an example, you might define the speed of a ball in metres-per-second, and its mass in kilograms.  Now, if you try (erroneously) to &lt;strong&gt;add the mass to the velocity&lt;/strong&gt;, in your Rust code — something that doesn’t make sense physically — you will get a compile-time error, potentially saving you debugging an error, which might be hard to catch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uom&lt;/code&gt; also helps with conversions, so you can safely add a displacement in kilometres to a diameter in metres.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Adding &lt;code&gt;uom&lt;/code&gt; to your Project
&lt;/h2&gt;

&lt;p&gt;You can just add &lt;code&gt;uom&lt;/code&gt; to your &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="c"&gt;# ...TRUNCATED&lt;/span&gt;
&lt;span class="py"&gt;rapier2d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.19.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"simd-stable"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;uom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.36.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For my use case, this worked just fine (with default features), though you might need to &lt;a href="https://docs.rs/crate/uom/latest/features"&gt;tweak the &lt;code&gt;uom&lt;/code&gt;  features&lt;/a&gt;, depending on your use case. &lt;/p&gt;

&lt;p&gt;To define a custom &lt;code&gt;pixel&lt;/code&gt; unit for conversion between the physics and rendering systems (using the &lt;code&gt;uom&lt;/code&gt; &lt;code&gt;unit&lt;/code&gt; macro), I also needed to add this snippet to my Rust source (last two lines):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;si&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="nn"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[macro_use]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We come to the full definition of the custom unit later.&lt;/p&gt;

&lt;p&gt;In this post, we use the example from the earlier &lt;a href="https://rodneylab.com/rapier-physics-with-macroquad/"&gt;Rapier Physics with Macroquad floating bubble post&lt;/a&gt;.  In the following section, we see some snippets where I added units to that code.  Find a link to the full code repo further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Units of Measurement
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgwvnntxs7gsll5d01zw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgwvnntxs7gsll5d01zw.png" alt="Rapier Physics with Units of Measurement: A collection of yellow, orange, and blue balls have floated to the top of the window in a screen-capture.  They are tightly packed, though not evenly distributed, with the collection being more balls deep at the centre of the window." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo features floating bubbles or balls.  For the demo domain, I use &lt;code&gt;uom&lt;/code&gt; to define the ball with typed values in SI units.  For rendering, I will need to convert these to pixels, and for use with Rapier, I will need a raw float value.&lt;/p&gt;

&lt;p&gt;Here is the ball struct Rust code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Ball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vector2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;physics_handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RigidBodyHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;uom&lt;/code&gt; has a predefined &lt;code&gt;Length&lt;/code&gt; type alias using standard SI units for length quantities (metres).  I use it here to set the type for the ball radius and current displacement within the simulation world.&lt;/p&gt;

&lt;h2&gt;
  
  
  🗡️ Rapier Physics Units
&lt;/h2&gt;

&lt;p&gt;I kept things simple, and used SI units (with &lt;code&gt;uom&lt;/code&gt;) within my own code, and converted the values to &lt;code&gt;f32&lt;/code&gt;s whenever I needed to pass the value to Rapier.  You could go a step further and use &lt;a href="https://www.lpalmieri.com/posts/2020-12-11-zero-to-production-6-domain-modelling/"&gt;type-driven development&lt;/a&gt;, where (by design) only validated quantities can get passed to the Rapier physics engine.&lt;/p&gt;

&lt;p&gt;Here is a code snippet, defining a new ball’s velocity and initializing it with Rapier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nn"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pseudo_random_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;linear_velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_velocity&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_velocity&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rigid_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RigidBodyBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.position.x.value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.position.y.value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;.linvel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linear_velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Velocity&lt;/code&gt; is a predefined &lt;code&gt;uom&lt;/code&gt; type aliases (like &lt;code&gt;Length&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the first line, above, I defined &lt;code&gt;x_velocity&lt;/code&gt; to be some random float, and associated metres per second as units, using the &lt;code&gt;uom&lt;/code&gt; types.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;rapier2d&lt;/code&gt;, I need to pass the velocity components as &lt;code&gt;f32&lt;/code&gt; values, so extract the raw value from the two, typed velocities via the &lt;code&gt;.value&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;Finally,  in the last line we pass the &lt;code&gt;linear_velocity&lt;/code&gt;, as an &lt;code&gt;nalgebra&lt;/code&gt; &lt;code&gt;Vector2&lt;/code&gt; of 32-bit floats (expected by Rapier).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example might seem a little contrived, as I convert a 32-bit float to a &lt;code&gt;uom&lt;/code&gt; velocity, and then immediately convert it back to a float for consumption by Rapier.  We shall see in a later section, though, that you can tweak this slightly to define a value in one unit, and then extract a converted value in another unit for passing to Macroquad for rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖥️ Macroquad Render Units of Measurement
&lt;/h2&gt;

&lt;p&gt;For rendering, I am using Macroquad, which works with pixels.  In the previous post, I set a scale of 50 pixels per metre.  I formalized that here using a &lt;code&gt;uom&lt;/code&gt; custom unit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Pixel Unit
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;uom&lt;/code&gt; provides the &lt;code&gt;unit&lt;/code&gt; macro for defining custom units, needed in your domain.  I used that macro to define a new &lt;code&gt;pixel&lt;/code&gt; unit as a length measurement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;unit!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;si&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;si&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 1 metre is 50 px&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s"&gt;"px"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pixel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pixels"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to include the snippet mentioned above if you use this macro.&lt;/p&gt;

&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;system&lt;/code&gt; adds the new unit to the &lt;a href="https://docs.rs/uom/latest/uom/si/index.html"&gt;&lt;code&gt;uom&lt;/code&gt; in-built SI units&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quantity&lt;/code&gt; defines the unit as a length; and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@pixel&lt;/code&gt;, in the final line, gives the abbreviation, singular and plural names for the unit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we can define variables using this new unit as a type, and convert between other units.  As an example, the &lt;code&gt;get_max_balls&lt;/code&gt; function uses quantities in both pixels and metres to determine the maximum number of balls that can fit across the app window, given the window has a pre-determined width:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_max_balls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;window_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WINDOW_WIDTH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ball_radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BALL_RADIUS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window_width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ball_radius&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="nf"&gt;.floor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;window_width&lt;/code&gt; is defined in pixels and &lt;code&gt;ball_radius&lt;/code&gt;, in metres.  Notice, we use &lt;code&gt;.value&lt;/code&gt; (as in the previous example) to extract the raw float value.  &lt;code&gt;WINDOW_WIDTH&lt;/code&gt; and &lt;code&gt;BALL_RADIUS&lt;/code&gt; are raw &lt;code&gt;f32&lt;/code&gt; constants.&lt;/p&gt;

&lt;p&gt;To convert a length between different length quantities (for example metres to pixels), we can call the &lt;code&gt;get&lt;/code&gt; method on the quantity.  For example, here is a snippet for rendering the ball where we need to convert the internal metre lengths to pixels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;draw_balls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Ball&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;balls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;Ball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;..&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;draw_circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="py"&gt;.x.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="py"&gt;.y.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The position and radius are all stored in metre values internally, yet there is no need to make sure we have the right conversion factor to get pixels out; the custom &lt;code&gt;uom&lt;/code&gt; does that for us.  Although the calculations are relatively simple to perform manually, converting automatically can save you making a simple mistake when revisiting code you haven’t seen in a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Rapier Physics with Units of Measurement: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post on Rapier Physics with Units of Measurement, we got an introduction to working with units of measurement with Rapier.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how you can &lt;strong&gt;use the &lt;code&gt;uom&lt;/code&gt; Rust crate to add SI units of measurement to game physics&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;how you can &lt;strong&gt;define custom quantities and convert between units&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;interface with code that does not expect units&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/rapier-units-of-measurement"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Rapier Physics with Units of Measurement: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Rapier Physics with Macroquad: Rust Game Physics</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Wed, 08 May 2024 16:41:07 +0000</pubDate>
      <link>https://forem.com/askrodney/rapier-physics-with-macroquad-rust-game-physics-77</link>
      <guid>https://forem.com/askrodney/rapier-physics-with-macroquad-rust-game-physics-77</guid>
      <description>&lt;h2&gt;
  
  
  Unit of Measurement in Game Dev
&lt;/h2&gt;

&lt;p&gt;In this post, we will take a quick look at Rapier physics with &lt;strong&gt;units of measurement&lt;/strong&gt; (&lt;strong&gt;UOM&lt;/strong&gt;).  In a recent post, trying out the Rust Rapier physics engine with Macroquad, we noted that Rapier recommends using full-scale measurements for more realistic simulations.  We opted for &lt;strong&gt;SI units&lt;/strong&gt; (metres for distance and metres per second for velocities).  There was necessary conversion from these physical units to pixels, for rendering.&lt;/p&gt;

&lt;p&gt;This got me thinking about leveraging Rust’s types for conversion between the physics and graphics units.  I discovered the Rust &lt;strong&gt;uom crate&lt;/strong&gt;, which has applications in Aerospace.  In his Rust Nation UK talk, Lachezar Lechev mentioned how confusion over units might have been behind a &lt;strong&gt;costly financial loss&lt;/strong&gt; in a &lt;strong&gt;real-world Aerospace project&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If professional teams working full-time on a project with such high financial stakes can make mistakes, then, probably, anyone is prone to making similar mistakes.  So, I considered adding UOM to my Rust game stack and talk about how I set it up in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  📏 &lt;code&gt;uom&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;uom&lt;/code&gt; crate, has applications in Aerospace and Aeronautical Engineering.  By defining quantities with a unit of measurement, it can catch basic errors at compile time.  As an example, you might define the speed of a ball in metres-per-second, and its mass in kilograms.  Now, if you try (erroneously) to &lt;strong&gt;add the mass to the velocity&lt;/strong&gt;, in your Rust code — something that doesn’t make sense physically — you will get a compile-time error, potentially saving you debugging an error, which might be hard to catch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uom&lt;/code&gt; also helps with conversions, so you can safely add a displacement in kilometres to a diameter in metres.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Adding &lt;code&gt;uom&lt;/code&gt; to your Project
&lt;/h2&gt;

&lt;p&gt;You can just add &lt;code&gt;uom&lt;/code&gt; to your &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="c"&gt;# ...TRUNCATED&lt;/span&gt;
&lt;span class="py"&gt;rapier2d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.18.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"simd-stable"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;uom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.36.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For my use case, this worked just fine (with default features), though you might need to &lt;a href="https://docs.rs/crate/uom/latest/features"&gt;tweak the &lt;code&gt;uom&lt;/code&gt;  features&lt;/a&gt;, depending on your use case. &lt;/p&gt;

&lt;p&gt;To define a custom &lt;code&gt;pixel&lt;/code&gt; unit for conversion between the physics and rendering systems (using the &lt;code&gt;uom&lt;/code&gt; &lt;code&gt;unit&lt;/code&gt; macro), I also needed to add this snippet to my Rust source (last two lines):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;si&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="nn"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[macro_use]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We come to the full definition of the custom unit later.&lt;/p&gt;

&lt;p&gt;In this post, we use the example from the earlier &lt;a href="https://rodneylab.com/rapier-physics-with-macroquad/"&gt;Rapier Physics with Macroquad floating bubble post&lt;/a&gt;.  In the following section, we see some snippets where I added units to that code.  Find a link to the full code repo further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Units of Measurement
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9utwmfegsz4h216n741.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9utwmfegsz4h216n741.png" alt="Rapier Physics with Units of Measurement: A collection of yellow, orange, and blue balls have floated to the top of the window in a screen-capture.  They are tightly packed, though not evenly distributed, with the collection being more balls deep at the centre of the window." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo features floating bubbles or balls.  For the demo domain, I use &lt;code&gt;uom&lt;/code&gt; to define the ball with typed values in SI units.  For rendering, I will need to convert these to pixels, and for use with Rapier, I will need a raw float value.&lt;/p&gt;

&lt;p&gt;Here is the ball struct Rust code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Ball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vector2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;physics_handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RigidBodyHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;uom&lt;/code&gt; has a predefined &lt;code&gt;Length&lt;/code&gt; type alias using standard SI units for length quantities (metres).  I use it here to set the type for the ball radius and current displacement within the simulation world.&lt;/p&gt;

&lt;h2&gt;
  
  
  🗡️ Rapier Physics Units
&lt;/h2&gt;

&lt;p&gt;I kept things simple, and used SI units (with &lt;code&gt;uom&lt;/code&gt;) within my own code, and converted the values to &lt;code&gt;f32&lt;/code&gt;s whenever I needed to pass the value to Rapier.  You could go a step further and use &lt;a href="https://www.lpalmieri.com/posts/2020-12-11-zero-to-production-6-domain-modelling/"&gt;type-driven development&lt;/a&gt;, where (by design) only validated quantities can get passed to the Rapier physics engine.&lt;/p&gt;

&lt;p&gt;Here is a code snippet, defining a new ball’s velocity and initializing it with Rapier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nn"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pseudo_random_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;y_velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter_per_second&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;linear_velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_velocity&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_velocity&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rigid_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RigidBodyBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vector!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.position.x.value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.position.y.value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;.linvel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linear_velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Velocity&lt;/code&gt; is a predefined &lt;code&gt;uom&lt;/code&gt; type aliases (like &lt;code&gt;Length&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the first line, above, I defined &lt;code&gt;x_velocity&lt;/code&gt; to be some random float, and associated metres per second as units, using the &lt;code&gt;uom&lt;/code&gt; types.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;rapier2d&lt;/code&gt;, I need to pass the velocity components as &lt;code&gt;f32&lt;/code&gt; values, so extract the raw value from the two, typed velocities via the &lt;code&gt;.value&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;Finally,  in the last line we pass the &lt;code&gt;linear_velocity&lt;/code&gt;, as an &lt;code&gt;nalgebra&lt;/code&gt; &lt;code&gt;Vector2&lt;/code&gt; of 32-bit floats (expected by Rapier).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example might seem a little contrived, as I convert a 32-bit float to a &lt;code&gt;uom&lt;/code&gt; velocity, and then immediately convert it back to a float for consumption by Rapier.  We shall see in a later section, though, that you can tweak this slightly to define a value in one unit, and then extract a converted value in another unit for passing to Macroquad for rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖥️ Macroquad Render Units of Measurement
&lt;/h2&gt;

&lt;p&gt;For rendering, I am using Macroquad, which works with pixels.  In the previous post, I set a scale of 50 pixels per metre.  I formalized that here using a &lt;code&gt;uom&lt;/code&gt; custom unit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Pixel Unit
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;uom&lt;/code&gt; provides the &lt;code&gt;unit&lt;/code&gt; macro for defining custom units, needed in your domain.  I used that macro to define a new &lt;code&gt;pixel&lt;/code&gt; unit as a length measurement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;unit!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;si&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;uom&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;si&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 1 metre is 50 px&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s"&gt;"px"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pixel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pixels"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to include the snippet mentioned above if you use this macro.&lt;/p&gt;

&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;system&lt;/code&gt; adds the new unit to the &lt;a href="https://docs.rs/uom/latest/uom/si/index.html"&gt;&lt;code&gt;uom&lt;/code&gt; in-built SI units&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quantity&lt;/code&gt; defines the unit as a length; and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@pixel&lt;/code&gt;, in the final line, gives the abbreviation, singular and plural names for the unit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we can define variables using this new unit as a type, and convert between other units.  As an example, the &lt;code&gt;get_max_balls&lt;/code&gt; function uses quantities in both pixels and metres to determine the maximum number of balls that can fit across the app window, given the window has a pre-determined width:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_max_balls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;window_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WINDOW_WIDTH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ball_radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BALL_RADIUS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window_width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ball_radius&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="py"&gt;.value&lt;/span&gt;&lt;span class="nf"&gt;.floor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;window_width&lt;/code&gt; is defined in pixels and &lt;code&gt;ball_radius&lt;/code&gt;, in metres.  Notice, we use &lt;code&gt;.value&lt;/code&gt; (as in the previous example) to extract the raw float value.  &lt;code&gt;WINDOW_WIDTH&lt;/code&gt; and &lt;code&gt;BALL_RADIUS&lt;/code&gt; are raw &lt;code&gt;f32&lt;/code&gt; constants.&lt;/p&gt;

&lt;p&gt;To convert a length between different length quantities (for example metres to pixels), we can call the &lt;code&gt;get&lt;/code&gt; method on the quantity.  For example, here is a snippet for rendering the ball where we need to convert the internal metre lengths to pixels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;draw_balls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Ball&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;balls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;Ball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;..&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;draw_circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="py"&gt;.x.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="py"&gt;.y.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="py"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The position and radius are all stored in metre values internally, yet there is no need to make sure we have the right conversion factor to get pixels out; the custom &lt;code&gt;uom&lt;/code&gt; does that for us.  Although the calculations are relatively simple to perform manually, converting automatically can save you making a simple mistake when revisiting code you haven’t seen in a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌🏽 Rapier Physics with Units of Measurement: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post on Rapier Physics with Units of Measurement, we got an introduction to working with units of measurement with Rapier.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how you can &lt;strong&gt;use the &lt;code&gt;uom&lt;/code&gt; Rust crate to add SI units of measurement to game physics&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;how you can &lt;strong&gt;define custom quantities and convert between units&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;interface with code that does not expect units&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/rapier-units-of-measurement"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Rapier Physics with Units of Measurement: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Macroquad egui DevTools: Rust Game Debugging UI</title>
      <dc:creator>Rodney Lab</dc:creator>
      <pubDate>Thu, 02 May 2024 11:01:03 +0000</pubDate>
      <link>https://forem.com/askrodney/macroquad-egui-devtools-rust-game-debugging-ui-n36</link>
      <guid>https://forem.com/askrodney/macroquad-egui-devtools-rust-game-debugging-ui-n36</guid>
      <description>&lt;h2&gt;
  
  
  🎮 Macroquad egui DevTools
&lt;/h2&gt;

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

&lt;p&gt;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 &lt;strong&gt;Chrome&lt;/strong&gt; and &lt;strong&gt;Firefox&lt;/strong&gt; to help &lt;strong&gt;web developers&lt;/strong&gt; 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 &lt;strong&gt;tweaking gameplay&lt;/strong&gt; or even just the &lt;strong&gt;game aesthetic&lt;/strong&gt;, working on real-world project.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;Bevy&lt;/strong&gt;, &lt;strong&gt;Godot&lt;/strong&gt; or &lt;strong&gt;Tauri&lt;/strong&gt; for example.  &lt;a href="https://github.com/emilk/egui?tab=readme-ov-file#3rd-party-integrations"&gt;egui enjoys wide support in the Rust Game Dev&lt;/a&gt; ecosystem.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  🧱 What I Built
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmclhrzekdxtqo4ls7vmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmclhrzekdxtqo4ls7vmc.png" alt='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.' width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 A Little more about egui
&lt;/h2&gt;

&lt;p&gt;I mentioned egui is an &lt;strong&gt;immediate mode&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you want to see a more general introduction to egui or immediate mode in Rust, see a recent post where we looked at &lt;a href="https://rodneylab.com/trying-egui/"&gt;creating a Cistercian clock using Rust with egui&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Project Setup
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"macroquad-egui"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;egui&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.21.0"&lt;/span&gt;
&lt;span class="py"&gt;egui-macroquad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.15"&lt;/span&gt;
&lt;span class="py"&gt;macroquad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0.3.26"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;default-features&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find &lt;a href="https://github.com/optozorax/egui-macroquad"&gt;some skeleton code in the &lt;code&gt;egui-macroquad&lt;/code&gt; repo&lt;/a&gt; to test your setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Source: https://github.com/optozorax/egui-macroquad#usage&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;macroquad&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[macroquad::main(&lt;/span&gt;&lt;span class="s"&gt;"egui with macroquad"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;clear_background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WHITE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Process keys, mouse etc.&lt;/span&gt;

        &lt;span class="nn"&gt;egui_macroquad&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;egui&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"egui ❤ macroquad"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Draw things before egui&lt;/span&gt;

        &lt;span class="nn"&gt;egui_macroquad&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Draw things after egui&lt;/span&gt;

        &lt;span class="nf"&gt;next_frame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🖱️ Adding egui to Macroquad
&lt;/h2&gt;

&lt;p&gt;Probably the hardest part, if you are new to egui, is to work out how to display the widgets you want.  The &lt;a href="https://www.egui.rs/"&gt;egui demo site is quite handy&lt;/a&gt; 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.&lt;/p&gt;

&lt;p&gt;I include the egui code I wrote here, in case it is useful for you as a starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;egui_macroquad&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="nf"&gt;.set_pixels_per_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nn"&gt;egui&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Developer Tools"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;egui_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;CollapsingHeader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ball Physics"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.default_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.horizontal&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Position"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s"&gt;"x: {:.2} y: {:.2}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.position.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.position.y&lt;/span&gt;
                    &lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.horizontal&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Velocity"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x: {} y: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.velocity.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.velocity.y&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nn"&gt;CollapsingHeader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ball Shape"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.default_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.horizontal&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Radius"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="nf"&gt;.add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;egui&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Slider&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ball&lt;/span&gt;&lt;span class="py"&gt;.radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some interesting points;&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 What Next for the Macroquad egui DevTools Interface?
&lt;/h2&gt;

&lt;p&gt;I mentioned this is just a basic proof-of-concept.  I have tried &lt;a href="https://rodneylab.com/using-jolt-with-flecs-dear-imgui/"&gt;a similar game debugging interface in C++ using raylib and Dear ImGui&lt;/a&gt; and would like to bring some features across such as:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  🙌🏽 Macroquad egui DevTools: Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this Macroquad egui DevTools post, we got an introduction to working with Macroquad and egui.  In particular, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which &lt;strong&gt;versions of Macroquad and egui work with egui-macroquad&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;how you might use &lt;strong&gt;egui to update game state from a debugging panel&lt;/strong&gt;; and&lt;/li&gt;
&lt;li&gt;some &lt;strong&gt;resources for getting started with egui&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this useful.  As promised, you can &lt;a href="https://github.com/rodneylab/macroquad-egui"&gt;get the full project code on the Rodney Lab GitHub repo&lt;/a&gt;.  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?&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏🏽 Macroquad egui DevTools: Feedback
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://rodneylab.com/giving/"&gt;consider supporting me through Buy me a Coffee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://twitter.com/messages/compose?recipient_id=1323579817258831875"&gt;@askRodney&lt;/a&gt; on X (previously Twitter) and also, join the &lt;a href="https://matrix.to/#/%23rodney:matrix.org"&gt;#rodney&lt;/a&gt; Element Matrix room. Also, see &lt;a href="https://rodneylab.com/contact/"&gt;further ways to get in touch with Rodney Lab&lt;/a&gt;. I post regularly on &lt;a href="https://rodneylab.com/tags/gaming/"&gt;Game Dev&lt;/a&gt; as well as &lt;a href="https://rodneylab.com/tags/rust/"&gt;Rust&lt;/a&gt; and &lt;a href="https://rodneylab.com/tags/c++/"&gt;C++&lt;/a&gt; (among other topics). Also, &lt;a href="https://newsletter.rodneylab.com/issue/latest-issue"&gt;subscribe to the newsletter to keep up-to-date&lt;/a&gt; with our latest projects.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
