I started making games on my own in Gamemaker: Studio around 2012 when I was 20.
At the start of 2017 I began making a game called FIGHT KNIGHT with a few friends.
I ran a modestly successful kickstarter for it in July 2017, and it released on November 30 2021.
FIGHT KNIGHT's development was somewhat troubled, even compared to most indie games, but that actually had surprisingly little to do with the game itself. Maybe I will talk about those troubles at some point, but this post is not about that.
This post is about the successes and failures of the code architecture of FIGHT KNIGHT and what I learned from making it. READ: this article is about nerdy programming crap
My motivation for writing this is partly to cement my own memory of things, but also because perhaps someone will read this and find it useful, or at least interesting. I've also enjoyed reading other devs talk about their process and I want to do the same thing. Playing the game would obviously provide a lot of context but I'll do my best to outline whatever is relevant in this post so it shouldn't be necessary.
FIGHT KNIGHT
To roughly quantify the scale of the project, FIGHT KNIGHT is about 10-20 hours long, 25 or so different characters, 45 different enemies, 7 themed levels which each take a few hours to complete, wrapped up in about 70 pages of "story" about a cursed knight climbing a magic tower. It's pretty substantial. For some reason I have this thing where I need to make "real" games (whatever that means) so I can't get really excited about a project unless it'll take multiple years to make.
FIGHT KNIGHT is kind of two games in one. one layer is a straightforward first-person dungeon crawler, where the objective is to explore a hand-made level and find the exit. There are various navigation challenges, puzzles, NPCs to talk to, a story develops, etc. you progress linearly through levels, open up shortcuts to return to the hub to restock items and save your game.
The second layer is a first-person action game which I'd most closely relate to Punch-out! or Godhand with significant caveats. You fight on a grid in realtime, and enemies advance towards you along a 2-by-infinity combat hallway. you always face forward and all enemies are always in front of you. you have a bunch of different attacks, special moves, and different equippable gauntlets which change your moveset.
In case you haven't figured this out, the premise of the game is that you punch a lot. Punching is the only way to interact with everything in the game including enemies, NPCs, shops, etc. It's a silly joke but I think we executed on it pretty well. I'll probably go into the story at some point but this isn't the post for it.
it's also got this pixelated faux-retro art style which is honestly pretty overdone these days but I promise it was cool in 2017. There are a lot of good reasons for the art to be the way that it is and that's probably one of the biggest successes of the game as a whole. This topic also deserves a post of its own so I'll link that here when I make it.
WHAT EVEN IS 'CODE ARCHITECTURE' WHY ARE YOU TALKING ABOUT THIS
big projects get really unbelievably big. I don't think anyone really understands how big they can get until you make one yourself. and if you aren't careful, their size can become so large and so unwieldy and unstable that it tips over and permanently crushes you. many such cases.
but they can also be made in a way that allows you to achieve amazing things on your own.
what I mean by "code architecture" is basically the framework you construct that all of the actual game content leans against to work. It's things like, the systems you make, how you organize your code, the large-scale structure of how the different pieces of the game talk to eachother and work together to do stuff. how you set yourself up basically. and you can do a really good job at this and save yourself years, or you can do a really bad job and cost yourself years (or more). And I've done both in fight knight, so it's kind of a good example of this actually. I was learning as I went and some parts turned out a lot better than others.
I personally find this to be an underdiscussed topic in programming circles. you have schools of thought like "clean code" which describe a hypothetical best practice on a line-by-line basis. and you'll also find endless discussion about specific systems or subsystems for specific games and genres. but there's not much i've found out there which helps you figure out how to actually put these things together in a good way. I suspect because very few people actually have an idea that this is even something to think about, much less know anything about how to do it well.
but what is "a good way", anyways? "a good way" is something that solves more problems than it creates. the ideal would be a 1:0 ratio of solutions to problems, but in practice every line of code and every single decision you make about your project has a cost, even if it's just creating more lines to scroll past.
So the realistic ideal is something that is of maximum usefulness to you while also creating minimal friction for you. and good and bad examples of that are what I wanna talk about here.
FIGHT KNIGHT WINS
FIGHT KNIGHT is far from a perfect project, and i'm very far from a perfect programmer, but I did make some decisions early on which paid off bigly by the end. I assume that none of this is revolutionary by any means, but i simply don't see people talking about this kind of stuff and that's why I want to put it out there.
ENEMIES
fight knight has a lot of different enemy types, like more than most AAA games that come out have these days. They all have different behaviors, different attack patterns, they all add different wrinkles to the combat which, when you combine that with combinations of enemies, creates a genuinely interesting combat experience. how do you do this in a way where you don't kill yourself implementing it all?
all the enemies in fight knight are secretly actually just one enemy.
99% of their variables and behaviors are shared. they mostly just have different values, such as how much damage they deal, or how much health they have. different mutations are turned on and off, like if their melee attack deals damage or throws a projectile, if they run back or not after an attack, things like that. bosses and some more elaborate enemies have special routines which run through different sequences of behaviors, but otherwise still share the same parent as everything else.
through remixing these extremely simplistic micro-behaviors I created every enemy in the game extremely efficiently. plainly, "the enemies" constitute roughly 50% of the video game, for approximately 10% of the work it took to make the video game.
overall this structure is just an extremely strong way to make enemies. I basically think that the more that your concept of an enemy diverges from this, the more you're going in the wrong direction.
This is certainly not a novel solution, but again I never see anyone talk about this so here I am talking about it.
this leads me to another topic: Inheritance... Yep I'm talking about this now.
If you read gamedev programming debates/arguments online, people get heated about inheritance. for some it's basically seen as a flawed invention from the stupid ages that we've evolved beyond. But there's a wide range here. Out of the gamedevs who read this far, 25% of them rolled their eyes when they saw the word "inheritance", 25% said "you can program without inheritance?" and a further 25% said "what's inheritance?"
Let me just make sure everyone understands what i'm talking about. Inheritance is really what it says on the tin, it's a concept where one object can inherit behaviors and properties from another. it becomes a "child" of that "parent" object.
what does that actually mean and why is it useful? There are a lot of situations in games where you want to do very similar things to different entities, such as deal damage, recieve damage, push eachother around, just classic rectangle bumping stuff. you see this pattern constantly where many entities want to act on one, or one entity wants to act on many. and inheritance makes that easy to do.
on a practical level, it's just easy to check for collisions against par_enemy, where all actual enemies are children and are therefore automatically included in the check. it's also useful to put common things that every enemy will use within par_enemy, so that stuff automatically propagates out where it's needed.
and the really powerful thing about it is that the state remains mutable, so you can set up the concept of HP inside the parent, and then change the individual HP for each child enemy, or not change it and leave it default, and it all just works.
You can take the concept very far, because if you think about it, the player is very similar to an enemy too, and so are NPCs, and so on... Over time, you can end up with a very complicated structure. This runaway complexity is one of the downsides of inheritance, because maybe you want to inherit certain behaviors, and not others, and you end up having to fight with this architecture just to get what you want when it's supposed to be there to help you. That's the danger.
But anyways, this is my blog so I get to say whatever I want so i'm gonna say it: inheritance isn't evil.
It really does have limitations. it's not good for every situation, or even a lot of them. but it's very useful for some things, and IMO, enemy types are one of those things.
A lot of people online argue about Inheritance versus Components. A lot. Components have their uses and limitations too. But this is just it: you don't have to use just one. you can use both
in FIGHT KNIGHT, there's a shallow inheritance structure that I use for some things. There's ALSO, essentially, built-in, enemy-specific behaviors which I internally programmed within the inheritance structure in a pattern that is basically components in terms of how I can arbitrarily apply the behaviors to any relevant entity/instance.
all enemies exist on the battle grid. all enemies walk towards their intended range and then execute an action. all enemies take damage and die. inheritance.
some enemies will throw a projectile when they attack. some enemies spawn an entity in front of them. some enemies run away after attacking. components.
the purists and the big brains will be groaning about things like the specific strict definition of the "component" pattern and memory implications here but I just don't care about that. it's not relevant, my entire game is 200MB. I'm talking about the actual meaningful application of these concepts.
and it's not even about any of this crap. the big point I want to make with this is that I learned you need to pick the best structure for the problem. Don't get caught up in pattern wars, just use the right tool for the job.
I was lucky because I just organically invented this component-like pattern myself.
DUNGEONS
similar to the combat, I managed to get a lot of video game out of a relatively slim amount of content through the presentation and the structure of the game as a whole.
FIGHT KNIGHT FAILURES
UI
I had no idea what I was doing.
CUTSCENES
I had no idea what I was doing.
DATA
I really had no idea what I was doing.


Comments
Comments are disabled in this build. Set
PUBLIC_COMMENTS_APIwhen the AWS endpoint is ready.