
Dans cet article, je vais partager mon expérience personnelle dans le développement d'un petit jeu en Rust. La création de la version fonctionnelle a pris environ 24 heures (principalement, je travaillais le soir ou le week-end). Le jeu est encore loin d'être terminé, mais je pense que l'expérience sera bénéfique. Je vais expliquer ce que j'ai appris et certaines observations faites lors de la construction du jeu à partir de zéro.
Skillbox recommande : Cours pratique de deux ans .
Rappelons-le : pour tous les lecteurs de « Habr » — une réduction de 10 000 roubles lors de l'inscription à tout cours Skillbox avec le code promo « Habr ».
Pourquoi Rust ?
J'ai choisi ce langage car j'en ai beaucoup entendu de bien et je vois qu'il devient de plus en plus populaire dans le domaine du développement de jeux. Avant d'écrire le jeu, j'avais une petite expérience dans le développement d'applications simples en Rust. C'était juste assez pour sentir une certaine liberté lors de l'écriture du jeu.
Pourquoi un jeu et quel type de jeu ?
Créer des jeux est amusant ! J'aimerais qu'il y ait plus de raisons, mais pour les projets « maison », je choisis des thèmes qui ne sont pas trop étroitement liés à mon travail habituel. Quel type de jeu ? Je voulais faire quelque chose comme un simulateur de tennis, combinant Cities Skylines, Zoo Tycoon, Prison Architect et bien sûr, le tennis. Au final, j'ai créé un jeu sur une académie de tennis où les gens viennent jouer.
Préparation technique
Je voulais utiliser Rust, mais je ne savais pas exactement par où commencer si je partais de « zéro ». Je ne voulais pas écrire de shaders pixelisés ou utiliser du glisser-déposer, donc je cherchais les solutions les plus flexibles.
J'ai trouvé des ressources utiles que je partage avec vous :
- — une liste d'éléments nécessaires au développement de jeux en Rust ;
J'ai étudié plusieurs moteurs de jeux en Rust, et au final, j'ai choisi Piston et ggez. Je les avais rencontrés lors d'un projet précédent. Au final, j'ai choisi ggez car il me semblait plus adapté pour réaliser un petit jeu 2D. La structure modulaire de Piston est trop complexe pour un développeur débutant (ou pour quelqu'un qui découvre Rust pour la première fois).
Structure du jeu
J'ai passé un certain temps à réfléchir à l'architecture du projet. La première étape consiste à créer le « terrain », les personnages et les courts de tennis. Les personnages doivent se déplacer sur les courts et attendre. Les joueurs doivent avoir des compétences qui s'améliorent avec le temps. De plus, il doit y avoir un éditeur permettant d'ajouter de nouveaux personnages et courts, mais cela ne sera pas gratuit.
Une fois tout cela réfléchi, j'ai commencé à travailler.
Création de jeu
Début : cercles et abstractions
J'ai pris un exemple de ggez et j'ai obtenu un cercle à l'écran. Incroyable ! Maintenant, un peu d'abstractions. J'ai pensé qu'il serait bon de s'abstraire de l'idée d'objet de jeu. Chaque objet doit être rendu et mis à jour comme indiqué ici :
// 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(())
}
}Ce morceau de code m'a permis d'obtenir une excellente liste d'objets que je peux mettre à jour et rendre dans une boucle tout aussi excellente.
mpl event::EventHandler pour MainState {
fn update(&mut self, context: &mut Context) -> GameResult {
// Mettre à jour tous les objets
for object in self.objects.iter_mut() {
object.update(context)?;
}
Ok(())
}
fn draw(&mut self, context: &mut Context) -> GameResult {
graphics::clear(context);
// Dessiner tous les objets
for object in self.objects.iter_mut() {
object.draw(context)?;
}
graphics::present(context);
Ok(())
}
} main.rs est nécessaire car il contient toutes les lignes de code. J'ai passé un peu de temps à séparer les fichiers et optimiser la structure des répertoires. Voici à quoi tout cela ressemble après cela :
resources -> c'est ici que se trouvent tous les actifs (images)
src
— entités
— game_object.rs
— circle.rs
— main.rs -> boucle principale
Personnes, terrains et images
La prochaine étape consiste à créer l'objet de jeu Person et à charger des images. Tout doit être construit sur la base de tuiles de 32*32.

Terrains de tennis
Après avoir étudié à quoi ressemblent les terrains de tennis, j'ai décidé de les faire avec des tuiles de 4*2. Au départ, il était possible de créer une image de cette taille ou de composer 8 tuiles séparées. Mais j'ai ensuite réalisé qu'il fallait seulement deux tuiles uniques, et voici pourquoi.
Nous avons donc deux de ces tuiles : 1 et 2.
Chaque section du court se compose soit de la tuile 1, soit de la tuile 2. Elles peuvent être disposées normalement ou être retournées de 180 degrés.

Mode principal de construction (assemblage)
Après avoir réussi à rendre les terrains, les personnes et les cartes, j'ai compris qu'il fallait également un mode de construction de base. Je l'ai mis en œuvre ainsi : lorsque le bouton est pressé, l'objet est sélectionné, et un clic le place à l'endroit souhaité. Ainsi, le bouton 1 permet de sélectionner le court, et le bouton 2 permet de sélectionner le joueur.
Mais il faut aussi se rappeler ce que signifient 1 et 2, donc j'ai ajouté un wireframe pour qu'il soit clair quel objet est sélectionné. Voici à quoi cela ressemble.

Questions sur l'architecture et le refactoring
Maintenant, j'ai plusieurs objets de jeu : des personnes, des courts et des étages. Cependant, pour que les wireframes fonctionnent, il est nécessaire d'indiquer à chaque entité de l'objet si les objets eux-mêmes sont en mode de démonstration, ou s'il ne s'agit que d'une simple bordure dessinée. Ce n'est pas très pratique.
Il me semblait qu'il fallait repenser l'architecture pour faire ressortir certaines limitations :
- la présence d'une entité qui s'affiche et se met à jour elle-même est un problème, car cette entité ne pourra pas « savoir » ce qu'elle doit rendre — une image ou un wireframe ;
- l'absence d'un outil d'échange de propriétés et de comportements entre des entités individuelles (par exemple, la propriété is_build_mode ou le rendu du comportement). On aurait pu utiliser l'héritage, bien qu'il n'y ait pas de manière normale de l'implémenter en Rust. Ce dont j'avais vraiment besoin, c'était de composition ;
- un outil pour faire interagir les entités entre elles était nécessaire pour assigner des personnes aux courts ;
- les entités elles-mêmes représentaient un mélange de données et de logique, ce qui devenait très vite ingérable.
J'ai mené une étude approfondie et découvert l'architecture , qui est généralement utilisée dans les jeux. Voici les avantages de l'ECS :
- les données sont séparées de la logique ;
- composition plutôt qu'héritage ;
- architecture orientée données.
L'ECS se caractérise par trois concepts de base :
- entités — un type d'objet auquel fait référence un identifiant (cela peut être un joueur, une balle ou autre) ;
- composants — ce dont sont composées les entités. Exemple — le composant de rendu, de position, et d'autres. Ce sont des stockages de données ;
- systèmes — ils utilisent des objets et des composants, plus contiennent des comportements et une logique basés sur ces données. Exemple — un système de rendu, itérant sur toutes les entités avec des composants à rendre et s'occupant du dessin.
Après l'étude, il est devenu évident que l'ECS résout les problèmes suivants :
- application de la composition au lieu de l'héritage pour l'organisation systémique des entités ;
- élimination du fouillis de code grâce à des systèmes de gestion ;
- utilisation de méthodes comme is_build_mode pour stocker la logique du wireframe au même endroit — dans le système de rendu.
Voici ce qui a été obtenu après l'implémentation de l'ECS.
resources -> c'est ici que se trouvent tous les actifs (images)
src
— components
— position.rs
— person.rs
— tennis_court.rs
— floor.rs
— wireframe.rs
— mouse_tracked.rs
— resources
— mouse.rs
— systems
— rendering.rs
— constants.rs
— utils.rs
— world_factory.rs -> fonctions de la fabrique du monde
— main.rs -> boucle principale
Nous assignons des personnes aux courts
ECS a rendu la vie plus simple. J'avais maintenant un moyen systémique d'ajouter des données aux entités et d'ajouter de la logique basée sur ces données. Cela a permis d'organiser la répartition des personnes aux courts.
Ce que j'ai fait :
- ajouté les données des courts assignés dans Person ;
- ajouté les données des personnes réparties dans TennisCourt ;
- ajouté CourtChoosingSystem, qui analyse les personnes et les installations, détecte les courts disponibles et répartit les joueurs sur ceux-ci ;
- ajouté le système PersonMovementSystem, qui cherche des personnes assignées aux courts, et si elles n'y sont pas, les envoie là où elles doivent aller.

Bilan
J'ai beaucoup aimé travailler sur ce jeu simple. De plus, je suis contente d'avoir utilisé Rust pour son développement, car :
- Rust vous fournit ce dont vous avez besoin ;
- il a une excellente documentation, Rust est très élégant ;
- la constance, c'est génial ;
- je ne dois pas recourir à des clonages, copies ou autres actions similaires, ce que je faisais souvent en C++ ;
- Les Options sont très pratiques à utiliser, elles gèrent aussi très bien les erreurs ;
- si le projet a réussi à se compiler, il fonctionne dans 99 % des cas comme il le devrait. Les messages d'erreur du compilateur me semblent les meilleurs que j'ai jamais vus.
Le développement de jeux en Rust ne fait que commencer. Mais il y a déjà une communauté stable et assez importante qui travaille à rendre Rust accessible à tous. C'est pourquoi je suis optimiste quant à l'avenir du langage, attendant avec impatience les résultats de notre travail collectif.
Skillbox recommande :
- Cours en ligne .
- Cours pratique .
- Cours pratique d'un an .
Source : habr.com
