Rust-Spiel in 24 Stunden: persönliche Entwicklungsreise

Rust-Spiel in 24 Stunden: persönliche Entwicklungsreise

In diesem Artikel teile ich meine persönlichen Erfahrungen bei der Entwicklung eines kleinen Spiels in Rust. Die Erstellung einer funktionierenden Version hat etwa 24 Stunden in Anspruch genommen (hauptsĂ€chlich arbeitete ich abends oder am Wochenende). Das Spiel ist noch lange nicht abgeschlossen, aber ich denke, die Erfahrung wird nĂŒtzlich sein. Ich werde erlĂ€utern, was ich gelernt habe, und einige Beobachtungen beim Spielaufbau von Grund auf schildern.

Skillbox empfiehlt: ZweijÀhriger praktischer Kurs Ich bin PRO-Webentwickler.

Wir erinnern daran: alle Leser von „Habr“ erhalten einen Rabatt von 10.000 Rubel bei der Anmeldung zu einem beliebigen Kurs von Skillbox mit dem Aktionscode „Habr“.

Warum Rust?

Ich habe diese Sprache gewĂ€hlt, weil ich viel Positives darĂŒber gehört habe und sehe, dass sie in der Spieleentwicklung immer beliebter wird. Vor der Entwicklung des Spiels hatte ich ein wenig Erfahrung in der Erstellung einfacher Anwendungen mit Rust. Das war genau genug, um ein gewisses Maß an Freiheit beim Programmieren des Spiels zu spĂŒren.

Warum gerade ein Spiel und welches Spiel?

Das Spielen von Videospielen macht Spaß! Ich wĂŒnschte, es gĂ€be mehr GrĂŒnde, aber fĂŒr "Hausprojekte" wĂ€hle ich Themen, die nicht zu eng mit meiner gewohnten Arbeit verbunden sind. Um welches Spiel handelt es sich? Ich wollte etwas wie einen Tennissimulator machen, der Elemente aus Cities Skylines, Zoo Tycoon, Prison Architect und natĂŒrlich Tennis kombiniert. Letztendlich entstand ein Spiel ĂŒber eine Tennisakademie, in der Menschen zum Spielen kommen.

Technische Vorbereitung

Ich wollte Rust verwenden, wusste aber nicht genau, wie "von Grund auf" ich anfangen sollte. Ich wollte keine Pixel-Shader schreiben und Drag-and-Drop verwenden, also suchte ich nach den flexibelsten Lösungen.

Ich habe nĂŒtzliche Ressourcen gefunden, die ich mit Ihnen teile:

Ich habe einige Spiel-Engines fĂŒr Rust untersucht und letztendlich Piston und ggez ausgewĂ€hlt. Ich hatte bereits in einem frĂŒheren Projekt mit ihnen zu tun. Schließlich entschied ich mich fĂŒr ggez, da es sich besser fĂŒr die Umsetzung eines kleinen 2D-Spiels eignete. Die modulare Struktur von Piston ist fĂŒr einen AnfĂ€nger (oder jemanden, der zum ersten Mal mit Rust arbeitet) zu komplex.

Spielestruktur

Ich habe etwas Zeit damit verbracht, ĂŒber die Architektur des Projekts nachzudenken. Der erste Schritt besteht darin, die "Umgebung", die Personen und die TennisplĂ€tze zu schaffen. Die Personen sollten sich auf den PlĂ€tzen bewegen und warten. Die Spieler sollten FĂ€higkeiten haben, die sich im Laufe der Zeit verbessern. Außerdem sollte es einen Editor geben, der es ermöglicht, neue Personen und PlĂ€tze hinzuzufĂŒgen, aber das kostenfrei ist nicht möglich.

Nachdem ich alles durchdacht habe, begann ich mit der Arbeit.

Spielerstellung

Anfang: Kreise und Abstraktionen

Ich habe mir ein Beispiel aus ggez genommen und einen Kreis auf dem Bildschirm erzeugt. Erstaunlich! Jetzt ein wenig Abstraktionen. Ich dachte, es wÀre gut, sich von der Idee des Spielobjekts zu abstrahieren. Jedes Objekt sollte gerendert und aktualisiert werden, wie hier angegeben:

// the game object trait
trait GameObject {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;
}
 
// a specific game object - Circle
struct Circle {
    position: Point2,
}
 
 impl Circle {
    fn new(position: Point2) -> Circle {
        Circle { position }
    }
}
impl GameObject for Circle {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        let circle =
            graphics::Mesh::new_circle(ctx, graphics::DrawMode::Fill, self.position, 100.0, 2.0)?;
 
         graphics::draw(ctx, &circle, na::Point2::new(0.0, 0.0), 0.0)?;
        Ok(())
    }
}

Dieser Codeabschnitt ermöglichte es mir, eine großartige Liste von Objekten zu erhalten, die ich in einem ebenso hervorragenden Zyklus aktualisieren und rendern kann.

mpl event::EventHandler fĂŒr MainState {
    fn update(&mut self, context: &mut Context) -> GameResult {
        // Aktualisieren Sie alle Objekte
        for object in self.objects.iter_mut() {
            object.update(context)?;
        }
 
        Ok(())
    }
 
    fn draw(&mut self, context: &mut Context) -> GameResult {
        graphics::clear(context);
 
        // Zeichnen Sie alle Objekte
        for object in self.objects.iter_mut() {
            object.draw(context)?;
        }
 
        graphics::present(context);
 
        Ok(())
    }
}

main.rs ist notwendig, da es alle Codezeilen enthÀlt. Ich habe etwas Zeit investiert, um die Dateien zu trennen und die Verzeichnisstruktur zu optimieren. So sieht es jetzt aus:
resources -> Hier sind alle Assets (Bilder)
src
— entities
— game_object.rs
— circle.rs
— main.rs -> Hauptschleife

Menschen, Ebenen und Bilder

Der nÀchste Schritt besteht darin, das Spielobjekt Person zu erstellen und Bilder zu laden. Alles sollte auf 32*32 Fliesen basieren.

Rust-Spiel in 24 Stunden: persönliche Entwicklungsreise

TennisplÀtze

Nachdem ich mir angeschaut habe, wie TennisplĂ€tze aussehen, habe ich beschlossen, sie aus 4*2 Fliesen zu erstellen. ZunĂ€chst konnte ich entweder ein Bild dieser GrĂ¶ĂŸe erstellen oder 8 separate Fliesen zusammensetzen. SpĂ€ter wurde mir klar, dass nur zwei einzigartige Fliesen notwendig sind, und das ist der Grund dafĂŒr.

Insgesamt haben wir zwei solcher Fliesen: 1 und 2.

Jede Sektion des Courts besteht aus Fliese 1 oder Fliese 2. Diese können entweder in der ĂŒblichen Ausrichtung oder um 180 Grad gedreht angeordnet werden.

Rust-Spiel in 24 Stunden: persönliche Entwicklungsreise

Hauptmodus der Konstruktion (Zusammenstellung)

Nachdem es gelungen ist, die Renderings der PlĂ€tze, Menschen und Karten zu erstellen, wurde mir klar, dass auch ein Grundmodus der Zusammenstellung notwendig ist. Dies habe ich so umgesetzt: Wenn die Taste gedrĂŒckt wird, wird das Objekt ausgewĂ€hlt und ein Klick platziert es an der gewĂŒnschten Stelle. So ermöglicht die Taste 1, den Court auszuwĂ€hlen, wĂ€hrend die Taste 2 es ermöglicht, den Spieler auszuwĂ€hlen.

Es ist jedoch auch erforderlich, sich zu merken, was 1 und 2 bedeuten, deshalb habe ich einen Wireframe hinzugefĂŒgt, um zu zeigen, welches Objekt ausgewĂ€hlt ist. So sieht das aus.

Rust-Spiel in 24 Stunden: persönliche Entwicklungsreise

Fragen zur Architektur und Refaktorisierung

Jetzt habe ich mehrere Spielobjekte: Menschen, Courts und Etagen. Um die Wireframes zu aktivieren, muss jedoch jeder EntitÀt des Objekts mitgeteilt werden, ob sich die Objekte im Demomodus befinden oder ob einfach nur ein Rahmen gezeichnet wurde. Das ist nicht sehr praktisch.

Mir schien, dass ich die Architektur neu ĂŒberdenken sollte, um bestimmte EinschrĂ€nkungen zu erkennen:

  • Das Vorhandensein einer EntitĂ€t, die sich selbst darstellt und aktualisiert, ist ein Problem, da diese EntitĂ€t nicht „erkennen“ kann, was sie rendern soll – entweder ein Bild oder ein Wireframe.
  • Das Fehlen eines Tools zum Austausch von Eigenschaften und Verhalten zwischen einzelnen EntitĂ€ten (zum Beispiel die Eigenschaft is_build_mode oder das Rendering von Verhalten). Man könnte Vererbung verwenden, obwohl es in Rust keine ordentliche Möglichkeit gibt, dies umzusetzen. Was ich wirklich benötigte, war Komposition.
  • Ein Tool, um die Interaktion zwischen EntitĂ€ten zu ermöglichen, war erforderlich, um Personen den PlĂ€tzen zuzuweisen.
  • Die EntitĂ€ten selbst waren eine Mischung aus Daten und Logik, was sehr schnell außer Kontrolle geriet.

Ich habe zusĂ€tzliche Recherchen durchgefĂŒhrt und die Architektur entdeckt, ECS – Entity Component System, die normalerweise in Spielen verwendet wird. Hier sind die Vorteile von ECS:

  • Daten sind von der Logik getrennt;
  • Komposition statt Vererbung;
  • Eine datenzentrierte Architektur.

ECS zeichnet sich durch drei grundlegende Konzepte aus:

  • EntitĂ€ten – ein Objekttyp, auf den sich ein Identifikator bezieht (das kann ein Spieler, ein Ball oder etwas anderes sein);
  • Komponenten – aus denen EntitĂ€ten bestehen. Ein Beispiel – Rendering-, Positions- und andere Komponenten. Dies sind Datenspeicher;
  • Systeme – sie nutzen sowohl Objekte als auch Komponenten und enthalten zusĂ€tzlich Verhalten und Logik, die auf diesen Daten basieren. Ein Beispiel – ein Rendering-System, das alle EntitĂ€ten mit Komponenten zum Rendern durchlĂ€uft und fĂŒr das Zeichnen verantwortlich ist.

Nach der Analyse wurde klar, dass ECS folgende Probleme löst:

  • Verwendung von Komposition anstelle von Vererbung zur systematischen Organisation von EntitĂ€ten;
  • Beseitigung von Code-Mischung durch Verwaltungssysteme;
  • Verwendung von Methoden wie is_build_mode, um die Logik des Wireframes am selben Ort zu speichern – im Rendering-System.

Hier ist das Ergebnis nach der Implementierung von ECS.

resources -> Hier sind alle Assets (Bilder)
src
– Komponenten
– position.rs
– person.rs
– tennis_court.rs
– floor.rs
– wireframe.rs
– mouse_tracked.rs
– Ressourcen
– mouse.rs
– Systeme
– rendering.rs
– constants.rs
– utils.rs
– world_factory.rs -> Funktionen zur Weltfabrik
— main.rs -> Hauptschleife

Wir weisen Personen den PlÀtzen zu

ECS hat das Leben erleichtert. Jetzt hatte ich einen systematischen Weg, Daten zu EntitĂ€ten hinzuzufĂŒgen und Logik basierend auf diesen Daten zu integrieren. Das ermöglichte es mir, die Verteilung von Personen auf den PlĂ€tzen zu organisieren.

Was ich getan habe:

  • Ich habe Daten zu den zugewiesenen PlĂ€tzen in Person hinzugefĂŒgt;
  • Ich habe Daten zu den verteilten Personen im TennisCourt hinzugefĂŒgt;
  • Ich habe das CourtChoosingSystem hinzugefĂŒgt, das es ermöglicht, Personen und PlĂ€tze zu analysieren, verfĂŒgbare PlĂ€tze zu entdecken und Spieler darauf zuzuweisen;
  • Ich habe das System PersonMovementSystem hinzugefĂŒgt, das nach den auf den PlĂ€tzen zugewiesenen Personen sucht und, falls sie dort nicht sind, die Personen dorthin sendet, wo sie benötigt werden.

Rust-Spiel in 24 Stunden: persönliche Entwicklungsreise

Zusammenfassung

Es hat mir sehr gefallen, an diesem einfachen Spiel zu arbeiten. DarĂŒber hinaus bin ich froh, dass ich fĂŒr die Entwicklung Rust verwendet habe, da:

  • Rust liefert, was man braucht;
  • es hat eine hervorragende Dokumentation und ist sehr elegant;
  • Konsistenz ist großartig;
  • man muss nicht auf Klonen, Kopieren oder Ă€hnliche Aktionen zurĂŒckgreifen, die ich oft in C++ gemacht habe;
  • Options sind sehr praktisch, sie behandeln auch Fehler hervorragend;
  • Wenn das Projekt kompiliert wird, funktioniert es zu 99%, genau so, wie es sollte. Die Fehlermeldungen des Compilers sind meiner Meinung nach die besten, die ich je gesehen habe.

Die Entwicklung von Spielen in Rust befindet sich noch am Anfang. Es gibt jedoch bereits eine stabile und recht große Community, die daran arbeitet, Rust fĂŒr alle zugĂ€nglich zu machen. Daher blicke ich optimistisch in die Zukunft der Sprache und freue mich auf die Ergebnisse unserer gemeinsamen Anstrengungen.

Skillbox empfiehlt:

Quelle: habr.com

ZuverlĂ€ssiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen đŸ”„ ZuverlĂ€ssiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster