In early 2021, the "My First Game Jam" was hosted on itch.io, welcoming all first-time game developers. As with many, I have toyed with the idea of making a video game many times over the years, but had never actually gotten around to it. This game jam, hosted over the course of 2 weeks in late January 2021 seemed like a perfect opportunity.
The Engine: Bevy
Months prior, I had been dabbling with the very new (Only at v0.4.0
at the time) rust
game engine: Bevy. The engine didn't have many of the quality of life features one would
expect from a full-featured game engine like Unreal or Unity, but that wasn't a problem for me. The
part of game development (at the time) that most interested me was more the nitty gritty of the
various systems. How did graphics work? How do you handle assets? How do you implement dialog
systems and quest lines? While a more mature engine would have let me make this game in likely half
or less of the time, I wasn't really interested in what I saw as a shortcut.
Bevy uses the "ECS" or Entity, Component, System model of game development. The
benefit of that system is that it strongly encourages modularity by making each element of the game
world a simple object (Component), group of objects (Entity), or function (System). In Bevy, a
Component could be as simple as this plain rust struct
for tracking player data:
pub struct Player {pub moving: bool,pub impulse: Vec3,}
Player
component that keeps track of data relevant to a player.Systems as well are very simple. Their whole purpose is to perform operations on the data stored in the Entities, and since they are just plain rust functions, there continues to be a strong incentive to modularize. In New York 2121 there were some Systems that were as short as 3 lines long, like this one:
fn update_moving_state(mut player_query: Query<&mut Player>) {for mut player in player_query.iter_mut() {player.moving = player.impulse.x != 0. || player.impulse.y != 0.;}}
Asset Creation
All those nice features aside, Bevy didn't really have any sort of good interface for designing levels or assets. To supplement the engine, I chose to use Aseprite for editing pixel art and sprites, and Tiled for map creation.
Sprites: Aseprite
I started with a sprite sheet from itch.io, and modified it to support the visual style I was going for. I'm not an artist by any stretch, and I got a lot of help from my collaborator and story writer Molly Raven, but I think the sprite sheet turned out surprisingly cohesive:
Map Creation: Tiled
Then came the assembling of the tilemap I'd created into a level. Bevy at this point did not have any sort of level editor, so I turned to Tiled, which seemed to be a pretty good option for a 2d top-down game. A few hours in tiled were all it took to have a rudimentary map for my game. The next step, then, was to integrate the map that Tiled generated into Bevy. Unfortunately, I did not like any of the existing Tiled integrations for bevy, so I ended up building my own, which involved cobbling together some custom shaders to support rendering a the tilemap, and custom parsing logic to handle the XML file that Tiled produced.
void main() {int sprite_number = Tilemap[Vertex_Tile_Index];if (sprite_number == 0) {return;}Rect sprite_rect = Textures[sprite_number - 1];vec2 sprite_dimensions = sprite_rect.end - sprite_rect.begin;vec3 vertex_position = vec3(Vertex_Position.xy * sprite_dimensions,0.0);vec2 atlas_positions[4] = vec2[](vec2(sprite_rect.begin.x, sprite_rect.end.y),sprite_rect.begin,vec2(sprite_rect.end.x, sprite_rect.begin.y),sprite_rect.end);v_Uv = floor(atlas_positions[gl_VertexIndex % 4] + vec2(0.01, 0.01)) / AtlasSize;v_Color = Color;gl_Position = ViewProj * ChunkTransform * vec4(ceil(vertex_position), 1.0);}
Once I had all of that in place, I (and my collaborator) could easily edit the map and sprites. I used Tiled to encode event triggers and collision zones, and from there I was able to have a little character walking around in the city I had designed. Then I ran into the final big hurdle.
The Dialog System
You see, my plan for this game was to make it a sort of RPG-lite, so I needed dialog, quest lines, and items. The problem was that my story writer was not a programmer, so embedding the dialog in the rust code was not an option (and, in retrospect, would have made development much slower).
I looked into several dialog systems including Yarn Spinner, but found them to both be more complicated than I needed, and none of them had rust libraries. So I once again implemented my own! I took some of the features I liked from Yarn Spinner (like their method of linking parts of a conversation together), and added the Handlebars templating system, and produced a sort of proto-language that My writer could easily edit and add their content to.
The beginning of the dialog for the first quest in the game.Finally, I had all of the elements needed, to build my game, and only a few days left to complete it. I had thankfully chosen a game jam that was 2 weeks long, rather than the 2-3 days of your Ludum Dare or other such jams. Since I ended up implementing so much of the needed scaffolding on my own during the jam, I really needed the extra time. In the end, I created a game that I am very happy with despite its short runtime.
If you'd like, the game is available for download on windows at at: itch.io.
I built this project with Molly Raven.