In this article I will tell you about the personal experience of developing a small game on Rust. It took about 24 hours to create a working version (mostly I worked in the evenings or on weekends). The game is far from over, but I think the experience will be rewarding. I will tell you what I learned and about some observations made when building a game from scratch.
Skillbox recommends: A two-year hands-on course “I am a PRO web developer . ”
We remind: for all readers of "Habr" - a discount of 10,000 rubles when recording for any Skillbox course on the promotional code "Habr".
Why Rust?
I chose this language because I heard a lot of good things about it and I see that it is becoming increasingly popular in the field of game development. Before writing the game, I had little experience developing simple applications on Rust. This was just enough to feel a certain freedom while writing the game.
Why the game and what game?
Making games is fun! I would like more reasons, but for “home” projects I choose topics that are not too closely related to my regular work. What game is this? I wanted to do something like a tennis simulator that combines Cities Skylines, Zoo Tycoon, Prison Architect and tennis itself. In general, it was a game about the academy of tennis, where people come to play.
')
Technical training
I wanted to use Rust, but I didn’t know exactly how much “from scratch” I would need to get started. I did not want to write pixel shaders and use drag-n-drop, so I was looking for the most flexible solutions.
I found useful resources that I share with you:
I studied several Rust game engines, eventually choosing Piston and ggez. I faced them during the work on the previous project. In the end, I chose ggez, because it seemed more suitable for implementing a small 2D game. The modular structure of Piston is too complicated for a novice developer (or someone who first works with Rust).
Game structure
I spent some time thinking about the architecture of the project. The first step is to make the “ground”, people and tennis courts. People have to move around the courts and wait. Players must have skills that improve over time. Plus, there should be an editor that allows you to add new people and courts, but this is no longer free.
Having thought of everything, I set to work.
Game creation
Start: Circles and AbstractionsI took an example from ggez and got a circle on the screen. Amazing Now some abstractions. It seemed to me that it was a good idea to abstract from the idea of ​​a game object. Each object must be rendered and updated as indicated here:
This piece of code allowed me to get an excellent list of objects that I can update and render in an equally excellent cycle.
mpl event::EventHandler for MainState { fn update(&mut self, context: &mut Context) -> GameResult<()> {
main.rs is necessary because all lines of code are in it. I spent a little time to split the files and optimize the directory structure. Here is how everything began to look after this:
resources -> this is where all the assets are (images)
src
- entities
- game_object.rs
- circle.rs
- main.rs -> main loopPeople, floors and imagesThe next stage is the creation of the game Person object and the loading of images. Everything should be built on the basis of tiles of size 32 * 32.
Tennis courtsHaving studied how tennis courts look, I decided to make them out of 4 * 2 tiles. Initially, it was possible to make an image of this size, or to put together 8 individual tiles. But then I realized that only two unique tiles are needed, and here's why.
In total we have two such tiles: 1 and 2.
Each section of the court consists of tile 1 or tile 2. They can be placed as usual or be turned upside down by 180 degrees.
The main mode of construction (assembly)After it turned out to achieve the rendering of sites, people and maps, I realized that I also needed a basic build mode. It is implemented as follows: when the button is pressed, the object is selected, and the click places it in the right place. So, button 1 allows you to select a court, and button 2 allows you to select a player.
But you still need to remember that we have 1 and 2, so I added a wireframe to make it clear which object is selected. Here's what it looks like.
Questions on architecture and refactoringNow I have several game objects: people, courts and floors. But in order for wired frames to work, each entity of the object needs to be informed whether the objects themselves are in demonstration mode, or whether the frame is simply drawn. This is not very convenient.
It seemed to me that it was necessary to rethink the architecture so that some limitations emerged:
- the presence of an entity that displays and updates itself is a problem, since this entity cannot “find out” what it has to render - an image and a wireless frame;
- the absence of an instrument for the exchange of properties and behavior between individual entities (an example is the property is_build_mode or the drawing of behavior). Inheritance could be used, although there is no normal way to implement it in Rust. What I really needed was the layout;
- a tool for interaction between entities was needed to assign people to the courts;
- the entities themselves were a mixture of data and logic, which very quickly got out of control.
I conducted additional research and discovered the ECS - Entity Component System architecture, which is commonly used in games. Here are the benefits of ECS:
- data is separated from logic;
- layout instead of inheritance;
- data-oriented architecture.
ECS has three basic concepts:
- Entities - the type of object referenced by the identifier (it can be a player, a ball or something else);
- components - are entities. An example is the rendering component, locations, and others. These are data warehouses;
- systems - they use both objects and components, plus they contain behavior and logic that are based on this data. An example is a rendering system that iterates all entities with components for rendering and deals with rendering.
After studying it became clear that ECS solves such problems:
- using layout instead of inheritance for the system organization of entities;
- getting rid of code hassle due to control systems;
- using methods like is_build_mode to store the logic of a wireframe in the same place - in the rendering system.
That's what happened after the introduction of ECS.
resources -> this is where all the assets are (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 -> world factory functions
- main.rs -> main loopAppoint people to the courts
ECS has made life easier. Now I had a system way of adding data to entities and adding logic based on this data. And this, in turn, allowed to organize the distribution of people on the courts.
What have I done:
- added data about the assigned courts in Person;
- added data about distributed people in TennisCourt;
- Added CourtChoosingSystem, which allows you to analyze people and sites, discover available courts and distribute players to them;
- Added a PersonMovementSystem system that looks for people assigned to the courts, and if they are not there, then sends people where they need to.
Summing up
I really enjoyed working on this simple game. Moreover, I am pleased that I used Rust to write it, because:
- Rust gives you what you need;
- he has excellent documentation, Rust is very elegant;
- consistency is cool;
- there is no need to resort to cloning, copying or other similar actions, which I often did in C ++;
- Options are very convenient for work, they also handle errors well;
- if the project was able to compile, then in 99% it works, and exactly as it should. Compiler error messages, it seems to me, are the best I've seen.
Rust game development is just beginning. But there is already a stable and rather large community working to open Rust for everyone. Therefore, I look at the future of the language with optimism, looking forward to the results of our common work.
Skillbox recommends: