Don't Take My Beans! post-release retrospective, part 1: development and testing
This is part 1 of a retrospective/postmortem for our game Don't Take My Beans!, which we fully released on Android on July 26, 2022. You can download it for free from Google Play.
In the fall of 2017 my friend Thad Andreades and I decided to make a video game together. This would be the first proper game development project for both of us. A year prior I had released ARanormal, an augmented reality ghost hunting simulation for iPhone developed in Swift, though that was more of a tech demo/proof of concept than a full-fledged game. But I was a professional software developer, and he produced 3D animation for virtual reality, so we felt we had the necessary skills.
We decided to make our game for Android and iPhone, figuring it would maximize the game's reach since everyone has a smartphone. (Probably the biggest mistake we made, right out of the gate; more about that later.) I was also enamored with the challenge of creating a game that takes advantage of the mobile form factor despite its constraints. We planned to release it as free-to-play, though without a firm monetization model in mind (another big mistake). I had been thinking about making a simple action game based around quick time events, so we agreed to attempt something like that, set in a Mad Max–esque postapocalyptic future, designed to make the player feel like a badass. We codenamed the game QT Action.
Since I primarily worked with C#/.NET and had experimented with Unity some already, we used Unity for the game engine. I handled the coding and engine work, Thad handled the graphics and UI, and we shared design work. Later we recruited Thad's friend Gavin Slyman to do the sound design. After growing attached to the placeholder music we found in a collection of free tracks for YouTube videos, we reached out to the creator of that track, Ethan Meixsell, and licensed a few tracks from him. We commissioned Clayton Belcher to create the app icon.
As development progressed, we gave it the official name Don't Take My Beans! (DTMB), reflecting the player's quest to reclaim their hoard of canned food stolen by a gang of mutants and robots. The initial quick time event design shifted to that of a lane-based autorunner, though the player stopped running to exchange fire with each wave of enemies.
Because this was a project pursued in our spare time, and because we had so much to learn along the way, DTMB's development lasted an unfortunate length of time. Having started in late 2017, we entered closed beta testing in March 2020, early access/open beta testing in March 2021, and launched the full production release on July 26, 2022. The full release only included Android, not iPhone; more on that later.
DTMB was not a commercial or critical success, but thankfully it didn't have to be, as it was a side project with no real financial stakes. And we learned a lot, some of which I'll share here.
As I mentioned, I'm a .NET developer by trade, so Unity with its C# scripting was a natural fit for me. Knowing the language already made it easy to jump in and get started, especially since I was mostly developing in the evening after work, and learning a new language on top of everything else would have been too much. I was also already familiar with the relevant tools. (I used Visual Studio Code for editing script files, since Visual Studio proper felt too heavy, and so I could have the same development environment on Windows or a Mac.)
Unity was nice for its visual scene editing and quickly seeing and testing changes. Thad and Gavin were able to jump in and make changes directly in the Unity project without much trouble, making integration with their assets simpler. Unity is a mature engine with lots of features, as well as lots of documentation and tutorials, so nothing we needed to do hadn't already been accounted for. Building for Android and iOS was as straightforward as Google and Apple allowed it to be. And though I initially bristled at Unity's game objects being essentially bags of components with no definite
is-a identity, I came to appreciate how it favors composition, and actually sort of miss that now that I'm experimenting with Godot, which emphasizes inheritance.
…except Unity also kinda sucks
I mention experimenting with Godot, because by the end of developing DTMB, I had grown tired of Unity.
As much as I love C#, I don't consider it a good language for game development. Relying on a garbage collector and lacking manual memory management means doing weird things like using structs when you'd normally use classes, and otherwise avoiding memory allocations on the heap, to prevent random pauses when the garbage collector kicks in. There were plenty of times I wished I could just delete objects. (Though to be fair, I could have done better at using techniques like object pooling to get more mileage out of my allocations.)
One of the downsides of such a long-running project is keeping your tech stack up-to-date, but updating Unity wasn't always a smooth process. There was a point release that broke the game until several point releases later. There were API updates with changes requiring several fixes just to maintain the same behavior. But the worst offender: moving from Unity 2018 to 2019 broke some of our shaders. At that point Thad had mostly moved on to other things, and I didn't have the tools, knowhow, or time to fix them. So we remained on Unity 2018 for the rest of development.
Unfortunately Unity 2018 locks us out of releasing updates on Android, because apps now need to target Android API level 31 or higher, for which Unity had an issue building that wasn't fixed in 2018. Also, apps with in-app purchases must now use Google Play Billing Library version 4, but only Unity 2020 and higher support that. So we'd have to upgrade by at least two major Unity versions, and fix all the resultant issues, to push even a minor fix for DTMB, which is a level of effort I can't justify anymore. Eventually, if the target API level isn't increased, Google will stop making DTMB discoverable on Google Play. It's such a shame that a finished project has such a short lifespan imposed on it by platforms without constant, costly updates.
Even beyond that, despite the investment I've made in learning Unity, it's become hard to trust them for future projects. They have a reputation for deprecating features and replacing them with new ones that never leave the experimental/beta phase. They've made a number of decisions and acquisitions that have little to do with helping developers create games, suggesting a lack of focus on their core userbase. Their CEO makes crass comments about developers who value monetization less than their creative process. Nothing unexpected from a corporation that wants to drive growth and create shareholder value, but there's nothing stopping them from making decisions at the expense of small creators like me, and they've already shown a willingness to. Why put all my eggs in their basket?
Source control, project management, and integrating assets
For source control, we used Git, hosted in a private GitHub repository. There was a bit of a learning curve around Git for Thad and Gavin, since they weren't developers, but nothing insurmountable. I've heard others say Git doesn't work well with Unity, but we had no real issues, probably because we each had our own areas and didn't run into merge conflicts. None of our asset files was too huge, but we did use Git Large File System (LFS) for the larger files.
We used GitHub issues to track tasks, which worked pretty well and let us keep everything in one place. Prior to the early access release, I tracked and prioritized issues using GitHub's projects, which I find work well for a kanban style of development. But after that initial release, when we had to plan out future releases, I switched to using milestones, which I think work better for sprints that end with deliverables.
Thad and Gavin mostly added their graphics and audio assets into the Unity project themselves. That saved me a lot of work, though in retrospect, I wish I had exerted a little more control. I didn't do a good job upfront of specifying file formats, compression parameters, etc., so I had to go back and edit a lot of those files and their import settings to bring the build size below the 150 MB that Google Play allowed. (Converting dozens of icon and texture files one by one in GIMP to be square powers of two to optimize compression isn't my idea of a good time.) And as much as I appreciated not having to lay out all of the user interface, I did have to reimplement a lot of it to adapt to different aspect ratios, support programmatic manipulation, and keep everything consistent.
Demoing and playtesting
During most of DTMB's development Thad and I lived in New York City. (I then moved to Cincinnati, Ohio.) I often went to the indie game expos hosted by Playcrafting, and we decided to exhibit DTMB at their Winter Play event in 2019. It gave us good motivation to reach a certain level of completion, and it was a lot of fun! We set up a table with a couple mobile devices with the game installed, and had several people play it, subtly watching over their shoulders. It was the first time we had a lot of people trying it. Their response was encouraging; people would keep hitting continue after game over to play again. We also learned what aspects of the game were confusing, and how difficult the game was for a general audience.
In March 2020 (when people suddenly had lots of time on their hands) we released a closed beta version to a handful of friends, family, and colleagues, via Google Play internal testing for Android and TestFlight for iPhone. Derek Yu has a good article about soliciting feedback, and this was what he classifies as sympathetic feedback, so not quite unfiltered. But one thing became clear: the game was still too hard. To address this we added power-ups randomly dropped by defeated enemies, and an inventory system for items purchased with in-game currency (thinly disguised as "forging" items with gold gears representing collected scrap). The currency had the added benefit of feeding into the monetization; we included optional rewarded ads that, when viewed, allowed players to double the currency collected during a run. I also updated the difficulty progression to adjust dynamically between runs—slower for players who died often to ease frustration, and faster for players who did well to prevent boredom.
I could see how well the game retained players from the telemetry collected via Unity Analytics: our beta testers weren't playing for very long. So we changed the game's level-based progression (collect a certain number of bean cans to unlock the next difficulty level) to a high score system, hoping players would care more about beating their last score (and with the eventual goal of an online leaderboard). And we tied continuing to a sort of wager system—spend half the bean cans you've collected in the run to continue, in the hope you can do even better, but risking your current spot on the top score board. (Players could of course also watch a rewarded ad to continue, once per run.)
A year later, in March 2021, we launched in early access to the public. Several fixes and adjustments happened during this time. (Many thanks to everyone who tested, and a major shout-out to Butch Wesley in particular for extensive, thorough feedback.) I also overhauled the Jesdo Software website, because Unity Ads required an app-ads.txt file hosted on the developer site, and the WordPress.com plan I had didn't support hosting a plain text file. That site was also ugly and hard to make look how I wanted, and remaking it helped me catch up on web development for the day job. I'm proud of how the new site turned out, especially on a technical level, and plan to write a blog post about it. But it was a major distraction that pushed DTMB's development out even further.
Toward the end of the early access period I had second thoughts about monetizing via rewarded ads, worrying it was too pay-to-win ("paying" in this case with the time spent watching the ad). So I replaced the rewarded ads with skippable interstitial ads that played between every few runs, and added a small, one-time in-app purchase to remove them. I felt this was fair to everyone—paying only removed the ads and conferred no other bonus, and was in line with how e.g. streaming services run ads. But, in another example of how the whims of platforms can cause great harm to small devs, Google decided to no longer allow "unexpected" interstitial ads, right after all that work to change them! To ensure compliance with the new policy, I changed the ads back to optional rewarded ones, though only to continue after dying, as the ones for doubling collected currency never saw much use. I also kept the in-app purchase for removing ads, which gave the player a "free" continue per run. (So even more pay-to-win than before, oops.)
Next, in part 2: the production release
That's it for part 1 of the Don't Take My Beans! retrospective! In part 2 I'll write about how the production release went, adventures in advertising, and how I plan to approach future projects. For notification when I post part 2, subscribe to our mailing list or to our RSS feed, and feel free to reach out on Mastodon, Twitter, or email. Thanks for reading!
Part 2 of this retrospective is now available here.