Filip Hráček / text /
I thought I'd share some of the approaches that I use when developing GIANT ROBOT GAME. This post will be about code organization, classes, NPC AI, and so on. I thought some folks might find it interesting.
For those who don’t know, GIANT ROBOT GAME is a game of vehicular combat, presented in sci-fi UI aesthetics. Focus is on depth (i.e. variety of options at any given time) and moddability. It is a spiritual descendant (though not a narrative successor) to my award winning game, Knights of San Francisco. It is currently in development for PC/Mac/Linux.
The game is not a roguelike (at least not in the strict sense) but the focus on depth and variety of options naturally leads to engineering approaches you’ll find in most roguelike codebases. That is to say, nothing is special. Everything is built from parts. The player character is just another entity in the simulated world.
Everything in the game is an Entity
: from pieces of debris to projectiles to factories.
Bare entities can’t do anything except react to physics, catch on fire, and so on.
The only class that extends Entity
is called ComplexEntity
. Complex entities
have things like AI, faction allegiance, status reporting, and — most importantly —
parts.
Parts are what defines the capabilities of each entity. You can put legs
on a factory and it will have locomotion capability. You can put a warhead
on a fuel tank and you have a ... slightly more explosive fuel tank.
There are parts for repairing, for deploying, for shooting, for swiveling.
Most of these parts depend on energy for operation, which means
you have to include an EnergyPart
. You get the point.
This way, we can mix and match capabilities, sometimes with hilarious results. The philosophy is more of a playground than of a realistic simulation. As long as something is fun, I don’t much care if it’s weird or unrealistic.
This level of qualitative combination is what first brought me to the idea of making my next game about giant battle robots. Robots are great in that it’s easy to replace parts and have a slightly different robot. This is hard to do with humans.
My motto for Knights of San Francisco was “radical powers, radical effects and simulationism”. This applies for GIANT ROBOT GAME as well.
+2
on damage as they level up. I’m not saying it won’t be in the game, but I focus on things that have radical, narrative effects. For example, the player gets a smoke grenade launcher. Suddenly, there’s a way for them to radically modify the visibility on the battlefield. To me, this is more interesting than a damage modifier, and leads to more strategic gameplay.For one of my past games, I used Goal Oriented Action Planning (GOAP) as the basis for the game agents’ AI. If you don’t know GOAP — it’s beautiful. It boils down to an A* search through a possibility space towards a desirable goal.
My reason for using GOAP was so that the actors in the game could surprise the player (and me) with novel approaches to problems. To an extent, this worked. But in reality, this advantage of GOAP didn't materialize nearly often enough. And if it did, the player didn't notice. It didn't make the NPCs seem more intelligent, nor more fun to interact with. It didn't make the game any more fun.
In the end, for my first commercial game (Knights of San Francisco), I removed GOAP and went with the much simpler mini-max approach. This new AI was less elegant, at least in my mind. But it was good enough, and much more malleable and easier to reason about.
Akin's Law of Spacecraft Design #36:
“Any run-of-the-mill engineer can design something which is elegant. A good engineer designs systems to be efficient. A great engineer designs them to be effective.”
“Effective” here translates to “provides customer value”. In game development terms, that’s mostly how fun the game is to play. I’m often surprised how common it is to sink time and talent into stuff that ultimately has no bearing on actual gameplay, or how the player perceives it.
Game developers, but especially solo devs with a programming background, are especially prone to this “elegancy” pitfall. I know because I've definitely fallen for it several times.
This is because writing the code for a game is, in itself, a sort of a puzzle game. Sometimes the engineering is more interesting than the actual game. And I want to avoid that scenario at all cost.
I try to remember that some of the most important and interesting games were built with tools and code that any “serious” developer would scoff at. Spelunky and Undertale were built in GameMaker Studio. The original code for Minecraft, the best-selling videogame in history, was, reportedly, a terrible mess. Everyone loves to laugh at the code comments in the source code for Team Fortress 2 — a game with a Metacritic score of 92/100.
I’m, of course, not advocating for a complete lack of software engineering standards. All I’m saying is that, in game development more than anywhere else, nobody cares about how elegant your code is unless they get the value out of it.
And, besides that, I'd say a lot of the “elegancy” pitfall in game development is really just us nerds getting enamored with a cool algorithmic approach. I fell in love with the idea of ECS (Entity-Component-System), then I realized it’s not really needed — nor desirable! — for my use case. I do have entities and, to an extent, the idea of components and systems, but the implementation is miles away from anything that could be considered ECS. I built a pretty feature-rich implementation of Behavior Trees (for AI), then I realized it’s just making things more complicated and messy (link to PDF) — despite its claims of elegancy and its popularity among AAA studios.
These are the things that I have learned to respect:
If you need more depth to the ideas above, I recommend Paul Boyd’s The Cargo Cult of Good Code, Chris Keihl’s Software development topics I've changed my mind on, tef’s Write code that’s easy to delete, Tess Snider’s tidbits for programmers, John Salvatier’s Reality has a surprising amount of detail, André Staltz’s don’t rewrite everything, Joel Spolsky’s Things You Should Never Do, Lidwell’s book Universal Principles of Design (I have the pocket edition on my table), much of the rest of Akin’s laws.
The game aesthetics are rooted in sci-fi movie interfaces. Think of a movie such as Star Wars or Oblivion, and the UIs the characters use in those movies. High contrast, simple, functional. I have studied these interfaces for years. (I even gave a talk about Futuristic UI earlier this year.)
This means I’m focusing less on realism and more on symbolism. Think of a military Heads-Up Display (HUD), with its limited colors, high contrast, simple shapes and lines. That’s what I’m going for.
For that reason, I felt that going with a traditional game engine (such as Unity or Unreal or Godot) would be counter productive. I was considering it but then I reverted back to Flutter (a portable UI framework) and Flame (a simple “game engine” built on top of Flutter). So far, it’s been mostly a positive experience. I do know that I’m missing out on some things that are “included in the box” for something like Unity. But the ease of UI and vector graphics development makes it up for me.
For the game, I wanted to have a 1970’s style 3D renderer. I ended up building it myself, and it was not only an interesting-yet-achievable problem, but it also allowed me a lot more flexibility than I would have with a 3D engine made for modern 3D games. More on that in a future article.
Code is organized in a roughly feature-first manner.
lib
├── audio
├── campaign <-- Meta game
├── editor <-- Game creation tools
├── experimental <-- What it says
├── game <-- The game itself (play session)
│ ├── ai
│ ├── camera
│ ├── combat
│ ├── deployment
│ ├── dialogue
│ ├── energy
│ ├── entity <-- The “game object”
│ ├── faction
│ ├── levels <-- Level loading
│ ├── locomotion
│ ├── map
│ ├── movement
│ ├── physics
│ ├── player
│ ├── repair
│ ├── scripting <-- Machinery for game scripting
│ ├── sensors
│ ├── status <-- Temporary buffs, reporting
│ ├── storage <-- Entities carrying other entities
│ ├── swivel <-- The part that lets turrets swivel
│ ├── systems <-- Remnants of ECS
│ ├── ui <-- How to show stuff to player
│ └── util
├── retro3d <-- The retro 3D renderer
└── style <-- Palette, typography
That’s it for now. Excuse me while I go implement wind.
— Filip Hráček