Jam postmortem


This was my entry for the Celesta game jam, a small friendly competition centered around the cpp-gamedev Discord server. I learned a lot along the way, as I was pretty much a complete newbie with Bevy and the ECS architectural pattern in production when I started.
I'll be sharing my overall experience with the Bevy toolset. Keep in mind version 0.7 was used, which is already out of date by one major release, but everything I say here can be probably applied to 0.8 as well as it was majorly a graphics update.
One thing to mention though is that this project started much before the gamejam, and it consisted of me playing around with platformer mechanics. I'm pretty proud of the resulting controls, which you can check out here. For the jam, I branched off this template and continued with this base set up.

The Engine (bevy)

Bevy is definitely a step up in difficulty from using any other basic media framework like SFML, Macroquad or ggez. This should be obvious given that Bevy centers all of its decisions on the underlying ECS architecture, which is an abstraction on how to represent game objects and divides concerns into Entities, Components and Systems. I won't get into much detail here into how it works as there are other articles about it, but basically, lying a bit: "Entities" are collections of components, "Components" hold data and "Systems" perform modifications to these components and the graph.

One of the main advantages of ECS over other architectural patterns is the fact that all behavior done in the game is described in terms of simple functions called "Systems", which can be parallelized to an extent. This however also means that, given no restrictions, the systems will seemingly run in an arbitrary order. As such, system ordering is required to keep this order consistent and avoid issues and 1-frame delays from appearing. I felt this task of system ordering to be somewhat tedious as I had to keep in mind the interactions each system had with each other separately. For such a small project, I dare to say that it would have been much easier to define the order by just ordering the function calls myself, like so:

fn update() {
  update_player();
  update_platforms();
  // ...
}

Of course, for large projects or AAA titles, I'd imagine doing this would be a nightmare if you have 5400 different behaviors implemented, so ECS is probably most useful there. Heck, it worked for Overwatch.

Bevy's ECS feels surprisingly robust and complete at first, seeing how the user-facing API you will most likely use is completely safe and it offers nice abstractions for context-derived singletons (Resources) and event writing & reading. However, in its current state, it's a bit finicky, as the set of systems executed is separated on stages (Kind of like how in Unity/Godot you have methods for Update, another one for LateUpdate and whatnot). This makes it extra hard to interact with systems in other stages as their order is determined by the stage's. It also has another few quirks like Exclusive Systems which aren't considered systems and cannot be run in parallel with other systems. Thankfully however, this is being worked on. (The RFC got merged 5 days ago!)

The Graphics (bevy + bevy_ecs_tilemap)

Setting up sprites is relatively straightforward in bevy. However, there is no standard support for tilemaps, so I had to use bevy_ecs_tilemap for drawing the map. Setting up a tilemap is much harder than it sounds as you need to load the file (be it LDTK or Tiled) and then place everything in its place carefully. bevy_ecs_tilemap is a bit verbose with this (It can easily take ~300 lines to load a map) but thankfully it provides LDTK and Tiled examples which you can copy as a starting point. v0.7.0 of the crate also got better with the amount of boilerplate required.

UI was probably the most painful of all the things to set up along with physics. Bevy provides a minimal flexbox implementation you can use for just about anything, but the documentation is really lacking and layouts more complex than one single node can be really tricky to set up. I also believe the implementation has a few bugs as I spent a few hours messing around with the aspect_ratio node style property only to make the node I was editing taller and nothing else. I can't confirm this though, it may just as well be an issue with my code.

At this point you may be asking "Which UI?". I'm referring to the title card, which I initially meant to separate in two nodes (One for the title and another one for the "Press key" text). However it proved to be too difficult of a task so I just gave up and rendered it all into a single node.

The Physics

I decided to make my own physics engine because I was really understimating the work that it would pose, and because I thought crates like Heron or Rapier2D were way too overkill for a platformer like this one. Turns out they aren't and I realized that the second I had to start implementing moving platforms (Fun fact: That's why they only move horizontally).

I did however try out Rapier in a different project, and my experience with it wasn't great. Since the plugin is separated into so many different components (e.g. Velocity, Collider, Sensor, Ccd, Friction, etc) it made really difficult to track what I was actually meant to attach to an entity for it to achieve the behavior that I wanted. I found myself repeatedly rebuilding just because I forgot some component for the physics to properly work on a body.

Overall

Overall, Bevy was an interesting experience, but not one that I'd repeat again for the next two years. It's too immature, but it shows potential and I'm sure it'll be really nice to use once we get a visual editor and most of the quirks are ironed out.

Leave a comment

Log in with itch.io to leave a comment.