Zum Inhalt springen

Spieleentwicklung in Rust: Bevy, Macroquad und mehr

Viele Leute lernen Rust, weil sie ein Spiel machen wollen. Ein toller Grund! Spiele brauchen Geschwindigkeit, einen sorgfältigen Umgang mit Speicher und Code, der nicht mitten im Bosskampf abstürzt. Genau darin ist Rust gut.

Auf dieser Seite bekommst du ein ehrliches Bild: was Leute heute schon mit Rust bauen, welche Engine oder Bibliothek du nehmen solltest, und die zwei Ideen, die fast jedes Spiel verwendet – die Game Loop (Spielschleife) und ECS. Danach siehst du ein erstes Programm in Bevy und in Macroquad.

Welche Beispiele laufen wo? Die Game Loop, die winzige ECS-Welt und die Übung sind reines Rust. Sie haben deshalb einen Button „Selbst ausprobieren“ für den Rust Playground. Bevy und Macroquad öffnen ein Fenster, diese Beispiele sind also für deinen eigenen Rechner mit cargo run gedacht. Das Macroquad-Beispiel wurde beim Schreiben dieser Seite kompiliert (auch für das Web); das Bevy-Beispiel folgt den offiziellen Beispielen von Bevy.

Die Community-Seite Are We Game Yet? (auf Englisch) verfolgt das Spiele-Ökosystem von Rust. Ihre Antwort auf die eigene Frage lautet: „Almost. We have the blocks, bring your own glue.“ – fast: Die Bausteine sind da, den Kleber bringst du selbst mit. Das ist eine faire Zusammenfassung.

Was heute schon gut ist:

  • Echte Spiele erscheinen. Tiny Glade, ein gemütliches Burgenbau-Spiel auf Steam, und Veloren, ein Open-Source-Voxel-RPG, stehen beide als Rust-Spiele auf Are We Game Yet.
  • Eine starke Engine. Bevy hat eine große, aktive Community und alle paar Monate ein neues Release.
  • Viele Bausteine. Are We Game Yet listet Crates für Physik, Audio, Eingabe, Netzwerk, UI und mehr.
  • Dein Spiel kann im Web laufen. Rust kompiliert nach WebAssembly, deshalb laufen viele Rust-Spiele auch im Browser.

Was noch schwierig ist:

  • Noch kein „Unity für Rust“. Die meisten Rust-Engines sind code-first. Visuelle Editoren gibt es (Fyrox hat einen, und Godot kann Rust verwenden), aber sie sind jünger als die großen kommerziellen Werkzeuge.
  • Vieles ändert sich. Bevy ist noch vor Version 1.0, und jedes Release bringt Breaking Changes. Ein Tutorial vom letzten Jahr kompiliert vielleicht nicht mehr.
  • Kompilierzeiten. Bei großen Engines dauert der erste Build Minuten. Die Tricks dagegen lernst du weiter unten.

Wenn du nächstes Jahr mit einem Team aus Artists ein kommerzielles 3D-Spiel veröffentlichen willst, bist du mit Unity, Unreal oder Godot sicherer unterwegs. Wenn du lernen willst, 2D-Spiele, Game Jams, Werkzeuge oder Simulationen machen möchtest oder Rust einfach magst, ist das ein richtig spaßiger Ort. 🦀

Es gibt zwei Arten von Werkzeugen. Eine Engine gibt dir eine ganze Struktur vor: wie Spielobjekte, Szenen und Assets funktionieren. Eine Bibliothek (oder ein Framework) gibt dir ein Fenster, Zeichnen, Sound und Eingabe, und den Rest organisierst du selbst.

Crate Was es ist Gut für Gut zu wissen
bevy (0.20) Eine komplette 2D- und 3D-Engine, aufgebaut rund um ECS Größere Projekte, 3D, verstehen, wie moderne Engines funktionieren Die bekannteste Rust-Engine. Breaking Changes bei jedem Release; langer erster Build
macroquad (0.4) Eine einfache 2D-Bibliothek, inspiriert von raylib Dein erstes Spiel, Game Jams, Web-Spiele Sehr wenige Abhängigkeiten, baut schnell. Du rufst Zeichenfunktionen in einer Schleife auf
ggez (0.10) Ein leichtgewichtiges 2D-Framework, inspiriert von LÖVE 2D-Spiele mit klarer Struktur Du implementierst einen EventHandler-Trait mit update und draw
fyrox (1.0) Eine 2D- und 3D-Engine mit Szenen-Editor Alle, die einen Editor wie in Godot oder Unity wollen Hat 2026 Version 1.0 erreicht. Kleinere Community als Bevy
raylib (6.0) Rust-Bindings für die C-Bibliothek raylib Alle, die raylib aus C kennen Braucht CMake und C-Build-Tools, weil es die C-Bibliothek baut (siehe FFI)
godot (0.5, „gdext“) Rust-Bindings für die Engine Godot 4 Den Editor von Godot nutzen, mit Spiellogik in Rust Du schreibst Rust-Klassen, die Godot als GDExtension lädt

Eine einfache Regel: Fang für dein erstes 2D-Spiel mit Macroquad an. Nimm Bevy, wenn du eine komplette Engine willst und gern ECS lernst. Nimm Godot mit gdext oder Fyrox, wenn du Dinge in einem Editor visuell platzieren willst.

Fast jedes Spiel ist eine große Schleife, die immer wieder läuft, bis du aufhörst:

  1. Eingabe lesen (Tasten, Maus, Controller).
  2. Spiel aktualisieren (Update): Dinge bewegen, Kollisionen prüfen, Punkte zählen.
  3. Zeichnen: das neue Bild auf den Bildschirm bringen.

Ein Durchlauf heißt Frame. Bei 60 Frames pro Sekunde (FPS) hat jeder Frame etwa 16 Millisekunden. Aber nicht alle Frames dauern gleich lang. Dein Rechner ist vielleicht gerade beschäftigt, oder ein Frame muss eine Datei laden.

Wenn dein Spiel den Spieler „2 Einheiten pro Frame“ bewegt, läuft es auf einem schnellen Rechner schneller und auf einem langsamen langsamer. Eine beliebte Lösung ist ein fester Zeitschritt (Fixed Timestep): Die Spiellogik geht immer in gleich großen Schritten voran (hier 10 ms), und jeder Frame führt so viele Schritte aus, wie in die vergangene Zeit passen. Übrige Zeit wartet in einem Akkumulator auf den nächsten Frame.

const STEP_MS: u32 = 10; // die Spiellogik geht immer 10 ms auf einmal voran
const SPEED: i32 = 2; // der Spieler bewegt sich 2 Einheiten pro Update
fn main() {
// Wie lange jeder Frame wirklich gedauert hat. Ein echtes Spiel misst das mit einer Uhr.
let frame_times = [16, 16, 45, 5, 16, 33];
let mut accumulator = 0; // Zeit, die vergangen, aber noch nicht simuliert ist
let mut position = 0;
for (frame, elapsed) in frame_times.into_iter().enumerate() {
accumulator += elapsed;
// So viele feste Schritte ausführen, wie in die vorhandene Zeit passen
let mut updates = 0;
while accumulator >= STEP_MS {
position += SPEED; // update(): Spiellogik
accumulator -= STEP_MS;
updates += 1;
}
// render(): einmal pro Frame zeichnen, egal was passiert ist
println!("Frame {frame}: {elapsed:>2} ms, {updates} Update(s), Spieler bei {position:>2}, {accumulator} ms übrig");
}
}

Ausgabe:

Frame 0: 16 ms, 1 Update(s), Spieler bei 2, 6 ms übrig
Frame 1: 16 ms, 2 Update(s), Spieler bei 6, 2 ms übrig
Frame 2: 45 ms, 4 Update(s), Spieler bei 14, 7 ms übrig
Frame 3: 5 ms, 1 Update(s), Spieler bei 16, 2 ms übrig
Frame 4: 16 ms, 1 Update(s), Spieler bei 18, 8 ms übrig
Frame 5: 33 ms, 4 Update(s), Spieler bei 26, 1 ms übrig
▶ Selbst ausprobieren

Schau dir Frame 2 an: Er war langsam (45 ms), also hat das Spiel 4 Updates ausgeführt, um aufzuholen. Insgesamt sind 131 ms vergangen, und das Spiel hat 13 Updates zu je 10 ms ausgeführt. Der Spieler landet bei 26, ganz egal, wie die Zeit auf die Frames verteilt war. Das macht die Physik stabil und auf jedem Rechner gleich – wichtig auch für Replays und Online-Spiele. Eine Gefahr gibt es: Ist ein Frame sehr langsam, führt das Spiel viele Updates zum Aufholen aus, und dadurch wird auch der nächste Frame langsam. Echte Schleifen begrenzen deshalb zusätzlich die Zahl der Updates pro Frame (die Spieluhr von Bevy rückt zum Beispiel standardmäßig höchstens 250 ms pro Frame vor).

Der andere übliche Weg ist Delta Time: Du misst die Sekunden seit dem letzten Frame und multiplizierst die Geschwindigkeiten damit (x += speed * dt). Das ist einfacher, und genau das machen die Beispiele für Bevy und Macroquad weiter unten. Viele Engines nutzen beides: Delta Time für Animationen, einen festen Zeitschritt für die Physik.

In vielen Sprachen würdest du eine Klasse Player bauen, eine Klasse Enemy, die von Character erbt, und so weiter. Das wird schnell unübersichtlich: Was ist mit einem Gegner, den man auch aufheben kann? Die meisten Rust-Engines verwenden stattdessen ECS (Entity Component System):

  • Eine Entity ist nur eine ID, zum Beispiel eine Zahl. Sie hat weder eigene Daten noch eigenes Verhalten.
  • Eine Component ist ein Stück einfacher Daten, das du an eine Entity hängst: Position, Velocity, Health. In Rust sind das ganz normale Structs oder Enums.
  • Ein System ist eine Funktion, die auf jeder Entity läuft, die bestimmte Components hat. Ein Bewegungssystem arbeitet zum Beispiel mit allem, was sowohl eine Position als auch eine Velocity hat.

Ein Baum hat eine Position, aber keine Velocity, also überspringt ihn das Bewegungssystem. Soll der Baum sich bewegen? Gib ihm eine Velocity. Du änderst dafür keine Klasse. Daten und Verhalten bleiben getrennt, und das passt auch gut zu den Borrowing-Regeln von Rust.

Hier ist eine winzige ECS-Welt in reinem Rust. Jeder Component-Typ lebt in seinem eigenen Vektor, und die Entity-ID ist der Index. None bedeutet „diese Entity hat diese Component nicht“ (siehe Option). Die Namen bleiben hier auf Englisch, weil {:<6} sie auf eine feste Breite auffüllt.

struct Position { x: i32, y: i32 }
struct Velocity { dx: i32, dy: i32 }
// Eine Entity ist nur eine ID: ein Index in jede Component-Liste
type Entity = usize;
#[derive(Default)]
struct World {
names: Vec<&'static str>,
positions: Vec<Option<Position>>,
velocities: Vec<Option<Velocity>>,
}
impl World {
fn spawn(&mut self, name: &'static str, pos: Option<Position>, vel: Option<Velocity>) -> Entity {
self.names.push(name);
self.positions.push(pos);
self.velocities.push(vel);
self.names.len() - 1
}
}
// Ein System: arbeitet mit jeder Entity, die SOWOHL eine Position als auch eine Velocity hat
fn movement_system(world: &mut World) {
for (pos, vel) in world.positions.iter_mut().zip(&world.velocities) {
if let (Some(pos), Some(vel)) = (pos, vel) {
pos.x += vel.dx;
pos.y += vel.dy;
}
}
}
// Noch ein System: gibt jede Entity aus, die eine Position hat
fn print_system(world: &World, tick: u32) {
println!("tick {tick}:");
for (id, pos) in world.positions.iter().enumerate() {
if let Some(pos) = pos {
println!(" #{id} {:<6} at ({}, {})", world.names[id], pos.x, pos.y);
}
}
}
fn main() {
let mut world = World::default();
world.spawn("player", Some(Position { x: 0, y: 0 }), Some(Velocity { dx: 1, dy: 0 }));
world.spawn("tree", Some(Position { x: 5, y: 2 }), None); // keine Velocity: bewegt sich nie
world.spawn("arrow", Some(Position { x: 0, y: 3 }), Some(Velocity { dx: 3, dy: -1 }));
for tick in 1..=2 {
movement_system(&mut world);
print_system(&world, tick);
}
}

Ausgabe:

tick 1:
#0 player at (1, 0)
#1 tree at (5, 2)
#2 arrow at (3, 2)
tick 2:
#0 player at (2, 0)
#1 tree at (5, 2)
#2 arrow at (6, 1)
▶ Selbst ausprobieren

Der Baum (tree) bleibt bei (5, 2), weil er keine Velocity hat. Niemand hat „Bäume bewegen sich nicht“ geschrieben – das folgt einfach aus den Daten. Echte ECS-Bibliotheken sind viel schneller und schlauer als das hier (sie speichern Components so, dass die CPU sie schnell lesen kann, und führen Systems parallel aus), aber die Idee ist dieselbe.

Bevy ist die bekannteste Rust-Engine, und in ihr ist alles ECS. Version 0.20 ist im Oktober 2026 erschienen. Bevy braucht das neueste stabile Rust. Aktualisiere also zuerst und leg dann ein Projekt an:

Terminal window
rustup update
cargo new my_bevy_game
cd my_bevy_game
cargo add bevy

Unter Linux brauchst du außerdem ein paar Systembibliotheken (Sound, Eingabe, Grafik). Die Setup-Anleitung von Bevy (auf Englisch) listet sie für jede Distribution auf. Unter Windows brauchst du die Visual Studio C++ Build Tools, unter macOS die Xcode-Kommandozeilentools.

Dieses Programm basiert auf dem offiziellen Bevy-Beispiel 2d/move_sprite. Es zeichnet ein oranges Quadrat, das nach links und rechts gleitet. (Das offizielle Beispiel lädt ein Bild; hier wird ein einfarbiges Quadrat verwendet, damit du keine Bilddateien brauchst.)

use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins) // Fenster, Rendering, Eingabe, Audio …
.add_systems(Startup, setup) // läuft einmal beim Start
.add_systems(Update, sprite_movement) // läuft in jedem Frame
.run();
}
// Eine Component: in welche Richtung sich diese Entity bewegt
#[derive(Component)]
enum Direction {
Left,
Right,
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
// Eine Entity mit drei Components erzeugen
commands.spawn((
Sprite::from_color(Color::srgb(1.0, 0.5, 0.2), Vec2::new(50.0, 50.0)),
Transform::from_xyz(0.0, 0.0, 0.0),
Direction::Right,
));
}
// Ein System: läuft auf jeder Entity, die eine Direction UND eine Transform hat
fn sprite_movement(time: Res<Time>, mut sprite_position: Query<(&mut Direction, &mut Transform)>) {
for (mut logo, mut transform) in &mut sprite_position {
match *logo {
Direction::Right => transform.translation.x += 150. * time.delta_secs(),
Direction::Left => transform.translation.x -= 150. * time.delta_secs(),
}
if transform.translation.x > 200. {
*logo = Direction::Left;
} else if transform.translation.x < -200. {
*logo = Direction::Right;
}
}
}

Vergleich das mit der winzigen ECS-Welt von oben. #[derive(Component)] macht aus einem normalen Enum eine Component. commands.spawn((...)) erzeugt eine Entity mit einem Tupel aus Components. Und Query<(&mut Direction, &mut Transform)> fragt Bevy nach „jeder Entity, die beides hat“ – genau wie unser movement_system mit zip. time.delta_secs() ist die Delta Time aus dem Abschnitt zur Game Loop. So bewegt sich das Quadrat auf jedem Rechner mit 150 Pixeln pro Sekunde.

Starte es mit cargo run. Der erste Build kompiliert Hunderte Crates und kann mehrere Minuten dauern, also hab etwas Geduld. Danach geht es viel schneller.

Die Setup-Anleitung von Bevy empfiehlt zwei Dinge. Erstens: Füg das hier zu Cargo.toml hinzu. Es optimiert Bevy und deine anderen Abhängigkeiten, lässt deinen eigenen Code aber schnell kompilieren:

[dependencies]
bevy = "0.20"
# Etwas Optimierung im Dev-Profil einschalten.
[profile.dev]
opt-level = 1
# Viel Optimierung im Dev-Profil für die Abhängigkeiten einschalten.
[profile.dev.package."*"]
opt-level = 3

Ohne diese Einstellungen kann ein Debug-Build eines Spiels sehr langsam sein: niedrige Bildraten und lange Ladezeiten. Abhängigkeiten werden nur einmal kompiliert, du zahlst für die Optimierung also nur beim ersten Build. (Profile werden auf Performance in Rust erklärt.)

Zweitens: Schalte während der Entwicklung das Feature dynamic_linking ein. Bevy wird dann als eigene gemeinsame Bibliothek gebaut, und Cargo muss nicht bei jeder Änderung das ganze Bevy erneut in dein Programm linken:

Terminal window
cargo run --features bevy/dynamic_linking

Zwei Warnungen aus der Bevy-Anleitung: Unter Windows funktioniert dynamic_linking nur zusammen mit den opt-level-Einstellungen von oben. Und liefere dein Spiel nicht damit aus: Das Spiel bräuchte sonst eine zusätzliche Bibliotheksdatei daneben. Bau dein Release ohne das Feature. Die Anleitung zeigt außerdem, wie du einen schnelleren Linker verwendest (lld oder mold). Das hilft vor allem unter Linux sehr.

Macroquad fühlt sich ganz anders an. Es gibt kein ECS und keinen App-Builder: Du schreibst die Game Loop selbst und zeichnest mit einfachen Funktionen wie draw_circle. Macroquad hat sehr wenige Abhängigkeiten, deshalb dauert ein frischer Build Sekunden statt Minuten.

Terminal window
cargo new move_the_ball
cd move_the_ball
cargo add macroquad
[dependencies]
macroquad = "0.4"

Dieses kleine Spiel zeigt einen Ball, den du mit den Pfeiltasten bewegst:

use macroquad::prelude::*;
#[macroquad::main("Move the Ball")]
async fn main() {
let mut player = vec2(screen_width() / 2.0, screen_height() / 2.0);
let speed = 250.0; // Pixel pro Sekunde
loop {
// 1. Eingabe: Pfeiltasten bewegen den Spieler
let dt = get_frame_time(); // Sekunden seit dem letzten Frame
if is_key_down(KeyCode::Right) {
player.x += speed * dt;
}
if is_key_down(KeyCode::Left) {
player.x -= speed * dt;
}
if is_key_down(KeyCode::Down) {
player.y += speed * dt;
}
if is_key_down(KeyCode::Up) {
player.y -= speed * dt;
}
if is_key_pressed(KeyCode::Escape) {
break;
}
// 2. Den Spieler auf dem Bildschirm halten
player.x = player.x.clamp(0.0, screen_width());
player.y = player.y.clamp(0.0, screen_height());
// 3. Alles für diesen Frame zeichnen
clear_background(DARKBLUE);
draw_circle(player.x, player.y, 20.0, ORANGE);
draw_text("Arrow keys to move, Esc to quit", 20.0, 30.0, 28.0, WHITE);
next_frame().await
}
}

Du siehst die drei Schritte der Game Loop: Eingabe, Update, Zeichnen. #[macroquad::main("Move the Ball")] öffnet ein Fenster mit diesem Titel. main ist async, aber du brauchst weder Tokio noch Wissen über Async/Await: next_frame().await heißt einfach „zeig diesen Frame und warte auf den nächsten“. Unter Linux nennt das README von Macroquad ein paar Systempakete, die du vorher installieren solltest (pkg-config libx11-dev libxi-dev libgl1-mesa-dev libasound2-dev unter Ubuntu).

Sowohl Macroquad als auch Bevy können dein Spiel als WebAssembly für den Browser bauen. Wenn Wasm neu für dich ist, lies zuerst Rust und WebAssembly. Du fängst immer damit an, das Wasm-Target hinzuzufügen:

Terminal window
rustup target add wasm32-unknown-unknown

Macroquad macht es dir sehr leicht. Ein einziger Befehl baut eine .wasm-Datei:

Terminal window
cargo build --release --target wasm32-unknown-unknown

Die Datei landet in target/wasm32-unknown-unknown/release/move_the_ball.wasm (für das Beispiel oben waren es etwa 600 KB). Das README von Macroquad enthält eine kurze index.html, die sie zusammen mit einer kleinen JavaScript-Datei, mq_js_bundle.js, lädt. Leg beide Dateien in einen Ordner und stell ihn mit irgendeinem lokalen Webserver bereit, zum Beispiel basic-http-server . (installieren mit cargo install basic-http-server). Ein Doppelklick auf die HTML-Datei reicht meistens nicht, weil Browser das Laden der .wasm-Datei direkt von der Festplatte blockieren.

Bevy erzeugt den JavaScript-Verbindungscode mit wasm-bindgen. Diese Schritte sind aus dem README der Bevy-Beispiele übernommen (für das Projekt my_bevy_game von oben):

Terminal window
cargo install wasm-bindgen-cli
cargo build --release --target wasm32-unknown-unknown
wasm-bindgen --out-dir web --target web target/wasm32-unknown-unknown/release/my_bevy_game.wasm

Das schreibt eine .js- und eine _bg.wasm-Datei in den Ordner web. Eine kleine HTML-Seite importiert die .js-Datei und startet das Spiel. Die Setup-Anleitung von Bevy schlägt außerdem ein Profil wasm-release mit opt-level = "s" vor, damit der Download kleiner wird. Web-Builds sind eine tolle Möglichkeit, Game-Jam-Beiträge zu teilen: Man spielt mit einem Klick, ohne etwas zu installieren.

In jedem Frame um einen festen Betrag bewegen. x += 5.0 pro Frame bedeutet, dass das Spiel bei 120 FPS doppelt so schnell läuft wie bei 60 FPS. Multiplizier mit der Delta Time (x += speed * dt) oder nimm einen festen Zeitschritt.

Einem alten Bevy-Tutorial folgen. Bevy ändert seine API bei jedem Release. Ein Blogbeitrag für 0.12 kompiliert deshalb oft nicht mit 0.20. Prüf, welche Version ein Tutorial verwendet, und nimm lieber die offiziellen Beispiele für deine Version. Zu jedem Release gibt es außerdem eine Migrationsanleitung auf der Bevy-Website.

Langsame Debug-Builds. Wenn dein Bevy-Spiel nur ein paar Frames pro Sekunde schafft, hast du wahrscheinlich die opt-level-Einstellungen in Cargo.toml vergessen.

Zu groß anfangen. Ein Open-World-RPG ist in jeder Sprache ein schweres erstes Projekt. Fang mit Pong, Snake, Breakout oder Flappy Bird an. Bring es zu Ende und mach das nächste dann ein bisschen größer.

Jedes Spiel hat Bildschirme: ein Menü, das eigentliche Spiel, einen Pause-Bildschirm, „Game Over“. Bau ein Enum GameState mit Menu, Playing, Paused und GameOver und ein Enum Input mit Enter, Escape und PlayerDied. Schreib fn next_state(state: GameState, input: Input) -> GameState: Enter startet das Spiel aus dem Menü, Escape pausiert und setzt fort, Sterben beendet das Spiel, und Enter bei „Game Over“ führt zurück ins Menü. Jede andere Eingabe ändert nichts. Füttere die Funktion mit einer festen Liste von Eingaben und gib jede Änderung aus. Tipp: Mach ein match auf das Tupel (state, input) und schließ mit (same, _) => same ab.

Lösung anzeigen
#[derive(Debug, Clone, Copy)]
enum GameState {
Menu,
Playing,
Paused,
GameOver,
}
#[derive(Debug, Clone, Copy)]
enum Input {
Enter,
Escape,
PlayerDied,
}
fn next_state(state: GameState, input: Input) -> GameState {
match (state, input) {
(GameState::Menu, Input::Enter) => GameState::Playing,
(GameState::Playing, Input::Escape) => GameState::Paused,
(GameState::Paused, Input::Escape) => GameState::Playing,
(GameState::Playing, Input::PlayerDied) => GameState::GameOver,
(GameState::GameOver, Input::Enter) => GameState::Menu,
// Alles andere: Eingabe ignorieren und bleiben, wo wir sind
(same, _) => same,
}
}
fn main() {
use Input::*; // damit können wir Enter statt Input::Enter schreiben
let inputs = [Escape, Enter, Escape, PlayerDied, Escape, PlayerDied, Enter];
let mut state = GameState::Menu;
for input in inputs {
let new_state = next_state(state, input);
println!("{state:?} + {input:?} -> {new_state:?}");
state = new_state;
}
}

Ausgabe:

Menu + Escape -> Menu
Menu + Enter -> Playing
Playing + Escape -> Paused
Paused + PlayerDied -> Paused
Paused + Escape -> Playing
Playing + PlayerDied -> GameOver
GameOver + Enter -> Menu
▶ Selbst ausprobieren

Ein match auf ein Tupel listet jeden erlaubten Wechsel an einer Stelle auf, und der letzte Zweig macht alle anderen Eingaben harmlos. Wer pausiert, kann nicht sterben, denn (Paused, PlayerDied) landet in (same, _). Bevy hat dieselbe Idee schon eingebaut, unter dem Namen States.

  • Rust ist eine echte Option für Spiele: „fast da“, mit einer starken Engine (Bevy), einfachen Bibliotheken (Macroquad, ggez), Optionen mit Editor (Fyrox, Godot mit gdext) und vielen Bausteinen.
  • Jedes Spiel führt eine Schleife aus: Eingabe, Update, Zeichnen. Nimm Delta Time oder einen festen Zeitschritt, damit die Geschwindigkeit nicht von der Bildrate abhängt.
  • ECS teilt ein Spiel in Entities (IDs), Components (einfache Daten) und Systems (Funktionen, die auf Entities mit bestimmten Components laufen).
  • Bevy ist durch und durch ECS. Ergänze die opt-level-Einstellungen im Profil und nutze dynamic_linking nur während der Entwicklung.
  • Macroquad ist der schnellste Einstieg in 2D: Du schreibst die Schleife selbst und zeichnest mit einfachen Funktionen.
  • Beide können mit dem Target wasm32-unknown-unknown für den Browser bauen.

Kurzer Check

1. Was ist in ECS eine Entity?

2. Warum verhält sich ein Spiel mit festem Zeitschritt auf jedem Rechner gleich?

3. Mit welcher Bevy-Einstellung solltest du dein fertiges Spiel NICHT ausliefern?

4. Du willst dein erstes kleines 2D-Spiel mit sehr kurzen Build-Zeiten machen. Welches Crate empfiehlt diese Seite?

Punkte: 0 / 4

  • Are We Game Yet? (auf Englisch): der Community-Überblick über Rust-Spiele-Engines, Bibliotheken für Physik, Audio und Netzwerk und Spiele, die mit Rust gemacht wurden.
  • Bevy Quick Start: Setup (auf Englisch): die offizielle Anleitung zur Installation von Bevy, zu den Einstellungen für schnelles Kompilieren und zu den Release- und Wasm-Profilen.
  • Game development in Rust with Macroquad (auf Englisch): ein kostenloses Online-Buch, das Schritt für Schritt einen kompletten Space-Shooter mit Macroquad baut.
  • The godot-rust book (auf Englisch): wie du Spiellogik für Godot 4 mit gdext in Rust schreibst.
  • Game Loop – Game Programming Patterns (auf Englisch): eine freundliche, ausführliche Erklärung von Game Loops, Delta Time und festen Zeitschritten.