24 Stunden Rostspiel: Erfahrung in der persönlichen Entwicklung

Bild

In diesem Artikel werde ich über meine persönlichen Erfahrungen bei der Entwicklung eines kleinen Spiels in Rust sprechen. Die Erstellung einer funktionierenden Version dauerte ungefähr 24 Stunden (ich habe hauptsächlich abends oder am Wochenende gearbeitet). Das Spiel ist noch lange nicht vorbei, aber ich denke, die Erfahrung wird nützlich sein. Ich werde Ihnen erzählen, was ich gelernt habe und einige der Beobachtungen, die beim Erstellen des Spiels von Grund auf gemacht wurden.

Skillbox empfiehlt: Zweijähriger Praktikumskurs "Ich bin ein PRO-Webentwickler . "

Wir erinnern Sie daran: Für alle Leser von „Habr“ - ein Rabatt von 10.000 Rubel bei der Anmeldung für einen Skillbox-Kurs mit dem Promo-Code „Habr“.

Warum rosten?


Ich habe diese Sprache gewählt, weil ich viele gute Dinge darüber gehört habe und sehe, dass sie im Bereich der Spieleentwicklung immer beliebter wird. Vor dem Schreiben des Spiels hatte ich wenig Erfahrung mit der Entwicklung einfacher Anwendungen in Rust. Dies war gerade genug, um beim Schreiben des Spiels eine gewisse Freiheit zu spüren.

Warum genau das Spiel und welche Art von Spiel?


Spiele machen macht Spaß! Ich würde aus mehr Gründen gerne, aber für "Heim" -Projekte wähle ich Themen, die nicht zu eng mit meiner üblichen Arbeit verbunden sind. Was für ein Spiel? Ich wollte so etwas wie einen Tennissimulator machen, der Cities Skylines, Zoo Tycoon, Prison Architect und Tennis selbst kombiniert. Im Allgemeinen stellte sich ein Spiel über eine Tennisakademie heraus, in der Leute zum Spielen kommen.

Technische Ausbildung


Ich wollte Rust verwenden, wusste aber nicht genau, wie „von Grund auf“ ich anfangen müsste. Ich wollte keine Pixel-Shader schreiben und Drag & Drop verwenden, also suchte ich nach den flexibelsten Lösungen.

Es wurden nützliche Ressourcen gefunden, die ich mit Ihnen teile:


Ich erkundete mehrere Rust-Game-Engines und entschied mich schließlich für Piston und Ggez. Ich bin ihnen bei der Arbeit an einem früheren Projekt begegnet. Am Ende habe ich mich für ggez entschieden, weil es für die Implementierung eines kleinen 2D-Spiels besser geeignet schien. Der modulare Aufbau von Piston ist für einen Anfänger (oder jemanden, der zum ersten Mal mit Rust arbeitet) zu komplex.

Spielstruktur


Ich habe ein wenig über die Architektur des Projekts nachgedacht. Der erste Schritt besteht darin, das „Land“, die Menschen und die Tennisplätze zu schaffen. Die Leute müssen sich auf den Gerichten bewegen und warten. Die Spieler müssen über Fähigkeiten verfügen, die sich im Laufe der Zeit verbessern. Außerdem sollte es einen Editor geben, mit dem Sie neue Personen und Gerichte hinzufügen können. Dies ist jedoch nicht mehr kostenlos.

Ich dachte über alles nach und machte mich an die Arbeit.

Spieleerstellung


Start: Kreise und Abstraktionen

Ich nahm ein Beispiel von ggez und bekam einen Kreis auf dem Bildschirm. Erstaunlich Nun ein paar Abstraktionen. Es schien mir schön, die Idee eines Spielobjekts zu ignorieren. Jedes Objekt muss wie hier angegeben gerendert und aktualisiert werden:

// 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(()) } } 

Mit diesem Code konnte ich eine hervorragende Liste von Objekten erhalten, die ich aktualisieren und in einer ebenso hervorragenden Schleife rendern kann.

 mpl event::EventHandler for MainState { fn update(&mut self, context: &mut Context) -> GameResult<()> { // Update all objects for object in self.objects.iter_mut() { object.update(context)?; } Ok(()) } fn draw(&mut self, context: &mut Context) -> GameResult<()> { graphics::clear(context); // Draw all objects for object in self.objects.iter_mut() { object.draw(context)?; } graphics::present(context); Ok(()) } } 

main.rs wird benötigt, da es alle Codezeilen enthält. Ich habe ein wenig Zeit damit verbracht, die Dateien zu trennen und die Verzeichnisstruktur zu optimieren. So fing alles an, sich darum zu kümmern:

Ressourcen -> Hier befinden sich alle Assets (Bilder)
src
- Entitäten
- game_object.rs
- circle.rs
- main.rs -> Hauptschleife

Menschen, Böden und Bilder

Der nächste Schritt besteht darin, ein Personenspielobjekt zu erstellen und Bilder zu laden. Alles sollte auf Kacheln mit einer Größe von 32 * 32 basieren.



Tennisplätze

Nachdem ich untersucht hatte, wie Tennisplätze aussehen, entschied ich mich, sie aus 4 * 2 Kacheln zu machen. Anfangs war es möglich, ein Bild dieser Größe zu erstellen oder 8 separate Kacheln zusammenzustellen. Aber dann wurde mir klar, dass nur zwei einzigartige Kacheln benötigt werden, und deshalb.

Insgesamt haben wir zwei solche Kacheln: 1 und 2.

Jeder Abschnitt des Platzes besteht aus Kachel 1 oder Kachel 2. Sie können wie gewohnt angeordnet oder um 180 Grad auf den Kopf gestellt werden.



Die Hauptbauweise (Montage)

Nachdem ich das Rendern von Websites, Personen und Karten erreicht hatte, wurde mir klar, dass auch ein grundlegender Erstellungsmodus erforderlich war. Es wurde folgendermaßen implementiert: Wenn die Schaltfläche gedrückt wird, wird das Objekt ausgewählt und durch Klicken auf die richtige Stelle platziert. Mit Taste 1 können Sie also ein Spielfeld auswählen, und mit Taste 2 können Sie einen Spieler auswählen.

Sie müssen sich jedoch noch daran erinnern, was wir unter 1 und 2 verstehen. Deshalb habe ich ein Drahtmodell hinzugefügt, damit klar ist, welches Objekt ausgewählt ist. So sieht es aus.


Fragen zu Architektur und Refactoring

Jetzt habe ich mehrere Spielobjekte: Menschen, Gerichte und Stockwerke. Damit Drahtgitter funktionieren, müssen Sie jeder Entität des Objekts mitteilen, ob sich die Objekte selbst im Demonstrationsmodus befinden oder ob ein Rahmen einfach gezeichnet wird. Dies ist nicht sehr praktisch.

Es schien mir, dass ich die Architektur überdenken musste, damit einige Einschränkungen aufgedeckt wurden:

  • Das Vorhandensein einer Entität, die sich selbst anzeigt und aktualisiert, ist ein Problem, da diese Entität nicht „wissen“ kann, was sie rendern soll - ein Bild und ein Drahtmodell.
  • Fehlen eines Tools zum Austausch von Eigenschaften und Verhalten zwischen einzelnen Entitäten (z. B. die Eigenschaft is_build_mode oder das Renderverhalten). Vererbung könnte verwendet werden, obwohl es keinen normalen Weg gibt, sie in Rust zu implementieren. Was ich wirklich brauchte, war das Layout;
  • Es wurde ein Instrument für die Interaktion von Einheiten untereinander benötigt, um Personen den Gerichten zuzuweisen.
  • Die Entitäten selbst waren eine Mischung aus Daten und Logik, die schnell außer Kontrolle gerieten.

Ich habe weitere Nachforschungen angestellt und die ECS- Architektur - Entity Component System - entdeckt , die häufig in Spielen verwendet wird. Hier sind die Vorteile von ECS:

  • Daten sind von der Logik getrennt;
  • Layout statt Vererbung;
  • datenorientierte Architektur.

ECS zeichnet sich durch drei Grundkonzepte aus:

  • Entitäten - die Art des Objekts, auf das sich die Kennung bezieht (es kann sich um einen Spieler, einen Ball oder etwas anderes handeln);
  • Komponenten - Entitäten bestehen aus ihnen. Ein Beispiel ist eine Rendering-Komponente, ein Layout und andere. Es ist ein Data Warehouse.
  • Systeme - Sie verwenden sowohl Objekte als auch Komponenten und enthalten Verhalten und Logik, die auf diesen Daten basieren. Ein Beispiel ist ein Rendering-System, das alle Entitäten mit Komponenten zum Rendern durchläuft und mit dem Rendern beschäftigt ist.

Nach dem Studium wurde klar, dass ECS solche Probleme löst:

  • Verwenden Sie das Layout anstelle der Vererbung für die Systemorganisation von Entitäten.
  • einen Code-Hash aufgrund von Steuerungssystemen loswerden;
  • Verwenden Sie Methoden wie is_build_mode, um die Logik des Drahtgitters an derselben Stelle zu speichern - im Rendering-System.

Folgendes ist nach der Implementierung von ECS passiert.

Ressourcen -> Hier befinden sich alle Assets (Bilder)
src
- Komponenten
- position.rs
- person.rs
- Tennis_court.rs
- floor.rs
- Drahtmodell.rs
- mouse_tracked.rs
- Ressourcen
- mouse.rs
- Systeme
- rendering.rs
- constants.rs
- utils.rs
- world_factory.rs -> Funktionen der Weltfabrik
- main.rs -> Hauptschleife

Weisen Sie Personen den Gerichten zu


ECS hat das Leben leichter gemacht. Jetzt hatte ich eine systematische Möglichkeit, Daten zu Entitäten hinzuzufügen und Logik basierend auf diesen Daten hinzuzufügen. Dies wiederum ermöglichte es, die Verteilung der Personen vor Gericht zu organisieren.

Was hab ich gemacht:

  • Daten über zugewiesene Gerichte zur Person hinzugefügt;
  • Daten zu verteilten Personen zu TennisCourt hinzugefügt;
  • CourtChoosingSystem hinzugefügt, mit dem Sie Personen und Websites analysieren, verfügbare Plätze finden und Spieler an diese verteilen können;
  • Das PersonMovementSystem-System wurde hinzugefügt, das nach Personen sucht, die den Gerichten zugewiesen sind. Wenn diese nicht vorhanden sind, werden bei Bedarf Personen gesendet.


Zusammenfassend


Ich habe es wirklich genossen, an diesem einfachen Spiel zu arbeiten. Außerdem freue ich mich, dass ich Rust zum Schreiben verwendet habe, weil:

  • Rust gibt Ihnen, was Sie brauchen;
  • er hat eine ausgezeichnete Dokumentation, Rust ist sehr elegant;
  • Konstanz ist cool;
  • Sie müssen nicht auf das Klonen, Kopieren oder ähnliche Aktionen zurückgreifen, die ich in C ++ häufig ausgeführt habe.
  • Optionen sind sehr praktisch für die Arbeit, sie behandeln auch Fehler perfekt;
  • Wenn das Projekt kompiliert werden könnte, dann funktioniert es zu 99% und genau so, wie es sollte. Compiler-Fehlermeldungen scheinen mir die besten zu sein, die ich je gesehen habe.

Die Spieleentwicklung auf Rust steht erst am Anfang. Aber es gibt bereits eine stabile und ziemlich große Community, die daran arbeitet, Rust für alle zu öffnen. Deshalb schaue ich optimistisch in die Zukunft der Sprache und freue mich auf die Ergebnisse unserer gemeinsamen Arbeit.

Skillbox empfiehlt:


Source: https://habr.com/ru/post/de444364/


All Articles