I made a cozy little 2D top down mage game. You walk around the map, activating statues and killing enemies by casting spells. In this blog post, I describe my development process and the challanges I faced using the Bevy game engine.
The game Magus Parvus, which you can play on itch, is a small 2D top down game where you cast spells by typing them out. It's fairly short, but that's kind of the point. I wanted to make a very small and compact game that would teach me the basics of how to create a 2D top down game.
In this post I will focus on the technical implementation and some challanges that I see that need to be addressed to make the development process of this kind of game more feasible. The whole game is open source, so feel free to look around, fork the project or open a PR.
The game is made in bevy 0.12.1
and uses
the following third party crates.
bevy_rapier2d
- Physics and Collisions
bevy_ecs_ldtk
- Map Loading
bevy_kira_audio
- Music and Sounds
bevy_asset_loader
- Loading assets
bevy_trickfilm
- 2D Sprite Animations
bevy_screen_diagnostics
- Debugging Diagnostics
noisy_bevy
- Noise Map (for camera shake)
The main challange here was to get dynamic chunkloading to work
with the bevy_ecs_ldtk
package.
Implementing physics and collisions with bevy_rapier2d
was also a little tedious, but this is mostly because their documentation
is somewhat broken (quite a lot of outdated href links).
Apart from that, most of the development was concerned with implementing
the actual features of the game.
As this is only the second game I made in Bevy, there were still a lot of things I didn't know how to do and I had to figure them out along the way. That being said, I feel like I was able to make progress rather quickly, which I contribute to the fact that the workflow of the Bevy simply works better for me then that of engines like Unity and Godot.
I chose to create the map using the
LDtk tool,
which is a feature rich 2D level creator.
Using the bevy_ecs_ldtk
crate, I was able to load the .json
files generated by
LDtk into Bevy.
I wanted to implement chunkloading into the game, however there weren't any examples or blogs about it (at least none that I could find), but luckily the maintainer of the crate is really friendly. I opened an issue and he was quick to guide me on how to implement it. Turns out, there actually IS an example on how to implement this, I of course somehow managed to miss that.
So following the example on how to load and delete level sections, I got a basic chunkloading system in place. This is actually a bit overkill for this project, but it's going to be very useful for future games.
While I don't need any fancy physics in my game,
I do need collisions and basic linear velocity.
bevy_rapier2d
is perfect for this.
It's pretty straightforward, however the documentation is a little broken at places,
so I had to look through the source code for certain informations
(which is pretty common with most of Bevy I find).
The only issue I still haven't fixed here is how to handle camera movement.
In Magus Parvus, the camera is always dead center on the player,
at least it should. Because the player has a Rigidbody
,
it is controlled by Rapier, meaning I can't just set the camera position
equal to the players position.
I did manage to somewhat circumvent this problem by adding the current player velocity
to the camera position, but that creates new isssues of it's own
(when the player is walking into a wall the camera will be a little bit in front of the player).
Both of the above problems deal with the backend, which you write once and pretty much leave alone for the rest of the project. Animations on the other hand are much more problematic. The more content you add to your game, the more your workflow of adding the content matters. Physics and chunkloading are independent of the content size, they work the same if you have 1 or 100 different enemies. However, if your workflow of adding animations is by hardcoding sprite indices into your source doe, then you will have a terrible time managing this mess for even 10 types of enemies, let alone 100.
This is one of my major problems with Unity and Godot,
their animation tools are really tedious to use
(mainly because you must use the mouse, which tends to store
relevant information in meta files. This works horrible with verison control).
Bevy's built in solution is: ... Well it doesn't have one of course.
I found the bevy_trickfilm
crate to be quite intuitive, the API feels similar to that of Godot,
but you define the animations inside of a .ron
file,
which means I don't have to use the mouse to create animations.
The crate still needs quite a bit of work though, but that's to be expected.
With some more work on the crate it should be much simpler to use
sprite animations in the future.
The game also has UI, which is kept to a minimum as I already know that UI is one of the worst parts of game development. Despite it looks, the UI for the spell book took my a dozen hours or so to implement. The code for it is fairly clean now actually, after many refactors.
My long term plan is to work on some more small projects like this one, all in the 2D top down style. The goal with each is to learn something new so that I can eventually work on bigger and bigger projects. I already have some ideas for future projects, so stay tuned for those. I also plan to make all of my future games open source and put them on my github, I will also write blogs about each of them here. Till then, keep it slow and steady.