Game Week 7

Last week's post was a bit of nonsense about abstractions while I researched and got a little more acquainted with Rust. This week we actually got some working code! Most of the features referenced here are available on Github in violet_bevy_plugins. I intend to keep as much of my code as I can modular and widely usable in that plugin as I build my own game on top of it.

With Rust+Bevy we managed to knock out several base features in one week!

  • Grid-based everything
  • Scrollwheel zoom
  • Click-drag (you're going to need an easy way to navigate around the world of machines!)
  • Perlin noise terrain generation
  • Generation of ores at random locations
  • Some basic diagnostics information as an optional feature using cargo run --features diagnostics - thanks to a very friendly person on the Bevy Discord!

So far, we're using Bevy and enjoying the heck out of it (Github). Bevy is "A refreshingly simple data-driven game engine built in Rust" created by Carter Anderson. It's built from the ground up as an open source library. More specifically, it's an ECS: Entity Component System. If we borrow this image from Wikipedia:

We can think of the ECS as being a spreadsheet, where each column is an entity, rows are components, and an entity will have an entry in the given row if it has the given component. The "Systems" part of ECS is a series of functions that will act on our entities and components. Systems are written using "Queries"; for example, we can write a function called render_shapes which makes a query for any object that has both a position and a shape; then the function has enough information to render the shape in the given location. It iterates over all objects that match the query and renders all of them.

Then for any functionality we want, such as collision detection and barriers that prevent player movement (such as walls or buildings), all we need to do is ensure that every entity (players, npcs..) that can't walk through a wall or building has the needed components (probably something like a Rigid Body and a Position).

The great thing about the entity-component system is that it is parallelizable. As long as you organize your entities, components, and systems properly, each system will only have to do so much work per frame while many of the systems work at the same time, resulting in a minimum amount of time required to perform all the work each frame.

While I haven't actually dug down into how Bevy's deep internals work, I can imagine it's a very interesting bit of technology that somehow calculates dependencies and creates some of computational graph to determine what can run in what order. Just by using base Bevy we already get parallelization benefits, while in C++ we would have had to learn a lot of deeply technical stuff just to perform simple multithreading that wouldn't lead to deadlocks. This will help us focus on the logic of our game machinery rather than focusing on "getting the code to work".

We have a pair of animations showing click-drag to move around the map and zooming in and out. We'll sandwich the animations between static images so hopefully anyone who's prone to dizziness can skip relatively easily.

First, the entire game is grid-based. There might be characters that can move independent of the grid, but machinery will be placed on the grid. This will make programming much easier.

Here we show ore generation at random locations on the grid. We mostly just needed to slap some stuff on the map so we could test out our scrolling and zooming, and as simple examples of how to draw sprites and give entities positions on the map, particularly ensuring that the sprites are scaled to the correct size to fit in their expected grid.

Below, we have our zoom. I got help from an MIT-licensed game called Magus Parvus on Github. Their code uses a part of the Bevy API that was removed only recently (it might have even been in the past week or two; we're running off the main branch, which is now 0.15.0-dev, while Magus Parvus was written on Bevy 0.14.0 and current stable is 0.14.2), but as a Rust beginner, it has been reasonably easy to figure out how to navigate around and use the code.

Above, we have a demonstration of click-dragging on the world to navigate. The player may have a character body to control in the future, but right now the focus is towards building machines and interactions between them, so making a character and enabling it to walk around would just slow us down, both from development time and from being able to navigate and interact with the world.

Next, we have some nice little diagnostics along with an actual grid made of lines. The diagnostics helped in aligning the grid and making sure all our code everywhere was correct. Tera wrote the original grid and we iterated on it. Eventually the grid will be toggleable and fill the whole screen instead of a hard-coded area of the map. I also borrowed a cute little wizard from Tera for demo purposes.

Last, for the moment, is perlin noise generation. There's a Perlin noise crate that we pulled in by simply cargo add noise in our project, and used their little code snippet to get us up and running.

Last week I was so excited about getting into Rust that I ended up spending too much time researching! This week I really focused on implementing game functionality. While learning a new programming language is challenging, the main challenge has been about how to organize the code, both in the context of Rust and of Bevy. Bevy offers the ability to develop and use functionality as logical units called Plugins, which we've used since day one. While there is some level of dependencies for the low-level stuff, such as the MainCamera that is needed in multiple locations to perform calculations and move the camera around correctly, plugins have kept our code nice and modular.

The only (or biggest?) downside of using Bevy is the instability of their API. They are upfront about the fact that it's not stable and users can expect breaking changes approximately every 3 months. However, I think it has really good bones and so far seems well maintained as Carter has scaled it beyond his own capacities by delegating to trusted parties.

The Bevy examples are kept updated and there's lots of automation for keeping everything updated together. For example, just skimming some of the recent CI activity, I end up on an issue where a feature was added in Cargo.toml and the bot responded: You added a new feature but didn't update the readme. Please run cargo run -p build-templated-pages -- update features to update it, and commit the file change. That's so cool!! This gives me a lot of confidence that Bevy is growing healthily.

My goals for next week are to start implementing basic machinery. We have these "ore" patches which supposedly have an associated amount of data/resource (haven't actually confirmed that it worked properly), and we want to simulate the data being passed and processed from one machine to another.

I'm really happy with how much progress we've made in one week, and Rust has been a joy to work with!!! Thank you for joining us this week!

P.S. I spent way too much time researching for last week's post, wasn't even sure what I was going to write it about, and then didn't feel great when it didn't meet my expected level of quality. This week I focused on development first, and this post only took 2 hours to write. I think focusing on the project first and having the blog post come after is much better and ends in a much better blog post anyways!

One comment on “Game Week 7

  1. Wow! Sounds like you are making great progress and having a blast. Looking forward to hearing about your progress.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.