2017-03-12

Genergy - 7 Day Roguelike 2017



Genergy was my entry to the 7DRL challenge 2017. The idea of the game was to explore the idea of energy transfer as an implementation of magic - rather than a concept such as "mana" or "power", energy as produced by fuel and food would be the limiting factor in combat.

That didn't really come to fruition. The game, as it was released at the end of the challenge, was a single level with a group of enemies which you had to fight, with the energy component being present only in the existance of a capacitor which empties when you fire off "thermal bolts".

That said, it was a completed roguelike and I considered, on balance, the experience to be a success given that it was my first real roguelike and considering that I did the vast majority of the work in a highly time-limited environment - implementing features in lunch breaks at work, and at evenings after work.

You can follow my progress as I made the game in a week through the devlog, and check out a more detailed postmortem of what went right - and wrong - during development. The game is available for download for Linux, macOS and Windows on GitHub.


Postmortem

Whatever the quality of the end product, I learnt a lot from Genergy and just as importantly I had a lot of fun. I wanted to write a postmortem to describe my experiences during development, the bits that were good or bad and maybe to make some plans for the future. In some ways, a postmortem is overkill because the challenge is over and I know what went right and what went wrong in my head. That said, I think crystallising those thoughts into text will be productive, and will help to reinforce the lessons I've learnt. If you read this and it helps in any way, please reach out - I'd love to hear from you.

What Went Right

I think first and foremost the obvious success is that I made a roguelike in 7 days by the strictest definition of a roguelike from the 7DRL website - a procedurally generated world with permadeath along with a focus on gameplay over graphics. It runs on the 3 main desktop platforms to some degree, which was an important goal for me during the 7DRL challenge, and all of this in an extremely time limited environment. I had only about 3 days of real work on the game thanks to my day job and previously arranged events.

An unseen success is that the quality of the codebase is much, much higher than the kind of hacky, tech-debt filled code often seen resulting from game jams. Maintaining a higher-than-usual quality was a goal of mine at the start of the 7DRL challenge and I'm happy with how a lot of the code came together. Much of it can be used verbatim in a future where I build on what I have and create the game I wanted to make in the first place.

Finally there's a more nebulous success: I think the idea of a game where energy transfer is a major mechanic remains solid. At no point during development did I feel like I was short of great ideas for features, attacks or items I could implement - despite me not having the time to implment them in the end.

What Went Wrong

I said that the codebase was in remarkably good shape and I wasn't lying - for the most part, it's far better than previous game jam code I've written. That said, the biggest failure of the game is where, in short, I used hacky code in completely the wrong place at completely the wrong time.

The offending code is the map code; specifically, tracking where entities are on the map. This is basically the cause of all 3 major known bugs in the final 7DRL release, and endless problems during development - exacerbated because the sloppy handling deals with a lot of pointers which will get nullified and then dereferenced. I don't really know why I didn't spend the time to get it right first time around. The code is jimmied into a Level class; I think tracking positions should be the job of a MapSystem or similar which has complete access to all of the appropriate map data.

Another problem was perhaps a slight lack of planning relating to what I wanted the game to be. The gameplay at release - running around throwing energy bolts at monsters - just evolved from thinking "I guess I need attacks that use energy" and then "I guess I need a monster" and "I guess I need some sort of capacitor thing" and just implementing the features without a real goal in mind. This mistake pales in comparison to the technical debt introduced by the maps issues, however - with a better entity tracking system I'd have saved a lot of time and had more time to plan and implement a more "complete" game.

Finally, I made the decision to spend most of the 7DRL challenge using the same development environment I use in my day job - i.e. vim + YouCompleteMe. This was mainly to see how the C++ development cycle works out in that environment. It turned out that C++ development in Vim is nothing like the smooth sailing that Python development is at my day job and I felt like I spend a lot of time writing code which a fully fledged IDE would have written for me. I don't doubt that there's a Vim environment which would have made it work better, but it was a mistake to try a new environment for a time-limited challenge, and when I returned to my "usual" Linux + IDE setup on the Saturday my productivity noticeably improved and I made a lot more progress. Still, the learning experience about that environment was invaluable - I just know now to use the IDE!

What I'd Like to Change

I'd like to keep on working on Genergy. I said previously there's a better game inside waiting to come out and I do believe that to be true in this case. The first thing, and the most important thing, is to fix the entity tracking on the map, which would on its own fix most of the bugs and issues with the game. The fix wouldn't even be difficult, and would lead to a much more streamlined game loop which is easier to build new features on top of.

Another beneficial change might be to prevent blocking on input after a game loop. The design of AshleyCPP doesn't lend itself easily to a game where the engine is not being continually updated - this makes it difficult to remove entities in a timely manner as entities cannot be safely removed until the end of the engine update cycle, after they've already been drawn. A realtime check for input coupled with a "re-render flag" which redraws the screen only after input has been processed would remove these issues while also keeping the smooth, curses-based style the game currently has. This would also have the benefit of allowing the removal of a second slightly-gross hack in entity handling.

Relating to my ECS library, AshleyCPP there's a perennial programming goal I have of making the library do more at compile time. This could reduce boilerplate (there was a considerable amount in Genergy) and improve runtime performance (which was not a concern in Genergy, but often is in other games). One day, I'll sit down and actually make those changes to see what I can create. Creating distribution packages (think .deb files or homebrew) would also be great, although it wasn't a major burden to compile AshleyCPP into the game for distribution.

Conclusions

I agonised over whether to mark Genergy a "success" or a "failure" on the 7DRL website. As an extensible codebase which has a lot of scope for future improvement, and as a learning experience, Genergy was a definite success. I'm thoroughly glad I took part in the 7DRL and as I keep repeating, the experience I've gained (insert joke about "levelling up" here) has been invaluable.

However, in the end I marked the game a failure. Not because I consider myself to have failed - far from it. But from a cold, analytical viewpoint, I can't argue that there's enough of a game to justify Genergy being downloaded, reviewed and played. There's just not enough to do, and even the game that is there is plagued by segfaults and a couple of persistent bugs.

So watch this space; who knows what I might create from the "ashes" of the 7DRL challenge. And really, if there was an option to choose between "success" and "failure" I'd pick that.


Dev Log

All times UTC

2017-03-12 18:24 - The final devlog entry! Today, as the challenge was finished, I didn't touch the code in any major way. I did create a "crushed" version of the game, requiring a smaller console and thus a smaller screen resolution, which I released on the GitHub repo. This did require a couple of trivial code changes (changing numbers) - I suppose whether or not the fixed versions count for the 7DRL challenge would depend on how strict you want to be with rules, although I think the fixed versions are well within the spirit of the law. Either way, I left the originals in the repo for any purists.

Other than that it was mostly a day of cleaning, then relaxing and reading the newspaper I'd neglected over the past week, plus a fair amount of work on this page. I'm writing this devlog post first - before I write the postmortem - to give me more time to plan the postmortem and what I gained from the project, and the problem areas I encountered.

Creating the new versions made something incredibly clear: I need an automated build process if I'm going to continue with Genergy past the 7DRL challenge. Build systems and continuous integration/continuous delivery is actually my day job, and it gives me the fuzzies to have a decent build system. A system creating a .deb file, a homebrew cask and a Windows binary file would be great.


2017-03-11 22:00 - Emoji of the day: 🔥 - and not really the good kind. Today went by far too quickly, and most of it was spent putting out fires due to technical debt I'd accrued earlier in development in places I'd mentally marked off as things I would "do better" outside a time-restricted environment. But at the end of the day, there's a game available to play, however buggy it might be. And there's a much better game hidden inside it, waiting to come out.

Update: 2017-03-12 01:23 - I spent the last few hours packaging, writing README files, and - as is tradition for me in game jams - fighting against Windows to get something built. Eventually, I got Genergy running on Linux, OS X and Windows (Cygwin) in some form. There were two late discoveries: on Linux I accidentally compiled for release with -march=native which made my binary non-portable. That was fixed and committed. Another thing that'll need to be fixed is to crunch the game in a little - at the moment it requires a terminal too large for some screens. I'll probably add a crunched-down version tomorrow, because it won't take much to change it.

Overall, and being realistic, I'm feeling pretty happy with how Genergy turned out. Ignoring the areas of technical debt I know about, the codebase is remarkably decent and will be easy enough to improve in the future. If I go back at a later date and take an axe to most of the code which tracks entity positions, I think most of the issues with crashes and segfaults will be quickly resolved.

As well as the obvious code improvements and the gameplay ideas I'd originally had, I'd love to also do something with Kenney's excellent roguelike asset pack. Another option is to integrate the code I have with my MMO server toy project, PlayPG that I've been meaning to pick up again for a while.

That's enough rambling now, though; I think I need to relax a bit before the postmortem and the couple of tiny fixes I should make.


2017-03-10 23:47 - Another day, another busy lunch, where I improved message rendering. I built on and improved that this evening, and also took some important steps; the most important being that I refactored input processing so that multiple character commands were possible. With that, I added "zapping", allowing the player to cast thermal energy bolts.

If I can add a few more attacks like that - with a few more interesting effects, perhaps - and a fair few monster types, I could have a decent endless-waves survival roguelike to submit by the end of tomorrow. It's not exactly the deep emergent energy transformation based gameplay I wanted, but it'd be a game. And, as I said before, I'd be glad to keep working on this game and the way I've programmed it is conducive to that (barring a few dirty hacks I've had to make as the deadline has started to approach).

As a technical aside - and further to my comment yesterday about adding & removing entities during an update cycle - further problems I had tonight have reinforced what I started to suspect last night: that ECS (or at least AshleyCPP) cannot very well handle situations where input is not in real time. I suspect that non-blocking input might be a better way of going, with a redraw only when necessary. Blocking on input seems to be a pretty bad idea generally.


2017-03-09 23:53 - At lunch, I abstracted the GUI into ncurses windows where it belongs, but didn't have time to make a devlog post because I was struck with work-related inspiration.

In the evening, I added death which proved to be an ordeal, largely because adding and removing entities is a huge pain point in ECS during an update cycle - or at least in AshleyCPP. In my past roguelike experiments, realtime input actually helped. Using ncurses, though, redrawing at 60fps is both wasteful and doesn't look great as far as I can tell. This means that destroying an entity is difficult in the same cycle in which it died.

Entity death is, therefore, horrendously hacky, but will work in the short term. And it does work; as of tonight, the player can attack and kill monsters. Tonight was meant to be for a message system in a window, however. I think I'll try for a short while to get the window created and a couple of messages rendering if possible, and then call it a night.


2017-03-08 23:12 - I made the monster chase you if you get too close, and added (yet more) boilerplate - this time for a messaging system to show things like "Your thermal energy cooks the monster's brain!".

Recent progress has led me to a conclusion: with my style of making games, there's a tremendous amount of code that just needs to be written to get anything done at all. This is partially a symptom of my heavy ECS-based style, and also my choice to use C++. But that's not really a bad thing; if I want to keep on going with Genergy after the 7DRL challenge, the heavy use of ECS is also going to lead to a much smoother time.

I suppose my point is: if I were approaching the 7DRL as a challenge to make the best possible game I could in 7 days, I would've already failed. I've spent a lot of time writing code with no obvious benefit to gameplay. But I'm not; I'm approaching it as a stepping stone to making the game I want to make, and as a learning experience. And by that metric, everything is going great and I'm having a great time.

On an unrelated note, the football tonight was unbelievable. Much better than last night! ⚽️


2017-03-07 12:54 - Lunch break boilerplate: attack system to prepare for the first battle!


2017-03-07 01:00 - Quite a lot of progress tonight, and all there is to show of it is a stationary M. Well, it's stationary after it detects what it considers an enemy and becomes hostile. Unfortunately, the route tracking isn't in yet so hostility is a little passive-aggressive right now. Accompanying the coding first were two absolute blowouts in the football, and then several episodes of Rick & Morty.


2017-03-07 13:25 - Short session during lunch at work; I added a quick + dirty system to track entity positions in levels. This will be useful for things like LOS + Monster AI.


2017-03-07 00:26 - On the train, I added a monster, some wandering AI and scope to expand the AI system greatly in the future if needed. After arriving home, I also started to mix in the unit operators to prepare for battling and energy conversion, as per the stated theme on the 7DRL website.

I've been pondering the battle system, and how energy might be converted between different forms. I feel like the central idea - converting energy between different forms could be a fairly realistic depiction of "real-world magic" - is solid. However, it tends to favour the idea of spellcasting and is difficult to apply to melee types. I'd find it a shame to miss out on melee, as that's always been my staple in RPG/roguelike games. There are options with "imbue" type effects or kinetic energy transfers, though.


2017-03-06 14:06 - Movement is now improved with a wall collision system, and I added a simple UI rendering system with stats. Slightly torn about where I want to go next; implementing an LOS algorithm would be fun but I feel like I need to get some real gameplay implemented based on the theme.


2017-03-06 00:15 - I got a lot more done than anticipated; lots of boilerplate + initialisation of various systems. That meant a moving '@' in full, extensible, ECS style and with the possibility of adding more input easily. This was accompanied by watching The Greasy Strangler and Robocop which made for a pretty unique evening. 😲


2017-03-05 14:23 - Map generation is in. Quite happy with how my BSP implementation is working out, although there are definitely kinks which need to be ironed out.


2017-03-05 12:22 - Finished setting up the build system and integrating the NoRogue code I had previously. I also fixed the wall generation problem in NoRogue which itself led to a confusing piece of code but there's no time to work out what the hell is going on there. Next stop: add a map, player and movement.


2017-03-05 11:00 - Yesterday, nothing got done except planning. I'm not going to have that much time to actually work on Genergy since I have to work as well as take part in already-planned trips, but that just means I'll have to make the time count. On the right there's a picture taken at Bletchley of part of a Bombe, used to help crack codes during WWII.

This morning I got the build system set up (CMake) and started to get ready to make actual gameplay. First thing's first, I've got to fix a bug with wall generation in the library I'm using. After that I'll create some components for tracking and using energy, which will be a pretty core part of the game.

A picture of part of the Bombe at Bletchley Park

2017-03-04 06:51 - There's nothing here yet! I'll be at Bletchley Park all day, so progress won't start until this evening at the earliest. I intend to use a small map generation library I worked on over the past week called "norogue", and my ECS framework AshleyCPP.