A couple weeks ago, I wished that I could play a game about a potato that was having a dream, where you could explore deeper levels of the dream and in some way understand reality and dreams and dreaming potatoes just a little bit better. This game could be called Potato’s Cave.
Google was no help at finding such a game, although these days that doesn’t mean very much. In any case, I decided to start making it and see how far I could get.
It would be sensible to use an established game engine, there is no shortage. But I am equally curious about Compose Multiplatform and that is what I’m using to build apps. I thought that it might help me learn about aspects of the tool that I might not otherwise learn, and it certainly did.
It also doomed my dreaming potato, for now.
Not because Compose is bad for games. In fact, I discovered lots of potential. I’m not the only one, I found Kubriko which is a tool for making games in Compose. I happened to come across a discussion in which the developer was participating. I asked what he found promising in game development with Compose, here is part of his response:
The most exciting thing for me was the fact that it's multiplatform. Drawing the actual game can be done using a single Composable Canvas that gets refreshed in every frame, and then that could be embedded into a Compose application which handles the game's UI. Some games like RPG-s might have really complex menu systems, and implementing those in Compose would eliminate some headaches. And with this approach the same code is working on all platforms.
Exploring that project and the demo app, it clearly demonstrates the strong potential of the platform for creating games. The games that I like to play and make are extremely dependent on UI, and that was one of the best aspects of working on Potato’s Cave. The ease of publishing on many different platforms using the same codebase makes a very strong case for compose.
The problem that I couldn’t get past was related to how I wanted to provide visuals for the game. I’ve been interested in Lottie animations and there is a fantastic library (Compottie) for supporting Lottie files in compose. The format is the most modern way to provide animated visuals and it seems to have huge potential.
Lotties are vector-based images with meta-information on how they should be animated. Vector images can scale to any resolution without compromising quality, and they are shockingly efficient to transfer. My potato above is just a couple kilobytes as a lottie, a thousand times smaller than its GIF. The animation is based on instructions rather than frame-by-frame content, so it is also extremely smooth with a tiny data footprint. It is fun and easy to reason about the animations you would like to create, you just think about which changes to position, rotation, and scale you would apply to the shapes in your composition.
I think if it were invented a decade earlier, it would be wildly popular and there would be many resources for learning, creating, and sharing this kind of animation. There is something eye-catching about them. Unfortunately, almost all the tools and resources we have available are dominated by companies with an Adobe-like philosophy on who should be able to use them. Unless you want to sign up for an expensive yearly subscription to software you will never own, you will not be able to play in that sandbox.
Glaxnimate is an open-source alternative still in progress which allowed me to create these animations. If the future of creative expression as it relates to the internet is a battlefield, projects like Glaxnimate are the heroes we need.
As efficient as they are to transfer, they are somewhat less efficient than raster-based animations for playback. And that’s the problem I ran into with Potato’s Cave. At a small number of objects on the screen, everything was pretty smooth.
The game could handle up to 30 sprites at 60 FPS. When I added 100 to the game, things got a little choppy (14 FPS).
Because that wasn’t quite broken enough, I had to see how well it could handle 1000 sprites (1 FPS):
The truth is, it was surprisingly robust considering all the computation involved in my barely-optimized approach and my choice to use technologies that weren’t designed for games. I could probably still make the game if I was limited to 30 objects on the screen. Unfortunately, things got a little more complicated for the wasm version of the game. There I maxed out at 20 sprites at my target framerate (30+ FPS).
All of this was on a workstation with a dedicated GPU (although far from the fanciest). On mobile it was even more grim. With the Android version, I started to experience choppiness at more than 10 sprites on the screen. I will need more simultaneous objects to support the kind of gameplay that I had in mind, so I either need to rethink the gameplay or rethink the visuals.
For what its worth, I did try a few things before throwing in the towel. In the videos above, there is a separate Canvas composable for each sprite. I tried an approach where all of the painters that draw the sprites are invoked on a single Canvas, without much difference. I also verified using the profiler in IntelliJ IDEA that it was indeed the Lottie animations that were responsible for the low framerate.
It’s possible that there are approaches that could work without compromising too much. I’m very open to suggestions and the project is on github. The relevant code for entity visuals can be found in EntityView.kt. I apologize that it is not easier to build, it has a couple of dependencies that are only available as public github repositories.
If you are interested in making games with Compose, I think there is a lot to work with, particularly if you start with something like Kubriko and more traditional approaches to visuals.