From ca7bc4c03072aada2821fcd75631a98491282dc1 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Fri, 5 Sep 2025 01:34:17 +0200 Subject: [PATCH] Add blog post on game jam architecture and development practices --- posts/beyond-it-works.mdx | 172 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 posts/beyond-it-works.mdx diff --git a/posts/beyond-it-works.mdx b/posts/beyond-it-works.mdx new file mode 100644 index 0000000..8c09bbf --- /dev/null +++ b/posts/beyond-it-works.mdx @@ -0,0 +1,172 @@ +--- +title: "Beyond 'It Works': A Deep Dive into Game Jam Architecture" +date: "2025-09-04" +description: "Exploring the importance of good architecture in game jams and how it can lead to powerful features like modding support." +--- + +# Beyond "It Works": A Deep Dive into Game Jam Architecture + +## The Countdown Begins + +The timer starts. 48 hours. That seems like a lot of time, but when you have a goal at the end, it turns into a frantic sprint. The theme is announced: **"Your cure is your curse."** My mind draws a blank. Tick... tock... A mix of excitement and a little bit of panic takes over. + +Every game jam begins this way for me. It’s a mad dash from a single idea to a playable _thing_. But amidst the chaos, there’s a method. Over several jams, I’ve developed a personal playbook for turning that panic into a product. In this post, I’m going to pull back the curtain on my process, focusing on the backbone of it all: the **code**, the **structure**, and the **composition** that holds the game together. + +--- + +## My Golden Rule: Scope, Scope, and Scope It Down Again + +Before I write a single line of code, I have to talk about the most critical skill in a game jam: **managing scope**. + +You're not going to complete your entry if you try to recreate _World of Warcraft_. Your idea must be small enough to be completed and polished in about **60%** of the allotted time. The other **40%** will be devoured by bugs, unforeseen problems (like exporting builds—web builds take a _really_ long time, apparently), and the desperate need for sleep. Every technical decision I make is guided by this principle. + +--- + +## The "Just Make it Work!" Counter-Argument + +This summer, I started doing game jams with my friends. The goal was simple: actually finish and release small games. I've now managed to release two ([Broberry](https://gabrielkaszewski.itch.io/broberry) and [Parasitic God](https://gabrielkaszewski.itch.io/parasitic-god)) and, in the process, I've found my own development rhythm. My mindset is always to keep the code as clean and maintainable as possible, even under pressure. + +This approach isn't always popular. + +When I talk to my friends, they often say, "Proper architecture takes too much time! Players don't care about code; they care about the game." They have a point. A beautiful codebase is worthless if you don't have a game to show for it. + +But here’s my counter-argument: **good architecture doesn't slow you down; it speeds you up.** A clean, modular structure isn't a chore. It's a powerful tool that allows you to iterate, debug, and add features faster than you could with messy "spaghetti code." + +Let me show you how. + +--- + +## My Process: From Theme to Systems + +When the theme drops, I spend a few minutes thinking about what kind of game I want to create. Once I have a rough idea, I immediately start thinking about the **systems**. What modular pieces will I need? Can I build a weapon system where I can easily create a sword that deals poison damage or a gun that shoots healing bullets? + +Thinking in terms of systems and reusable parts is the key. This naturally leads to my favorite design pattern for game development. + +### My Secret Weapon: Building with LEGOs (Composition) + +In programming, there's a principle: **"prefer composition over inheritance."** In a game jam, I treat this as law. For those who aren't familiar with the terms, here's a quick breakdown. + +#### The Inheritance Trap + +Inheritance is what we're often taught first. It's an "**is-a**" relationship. A `Cat` **is an** `Animal`. A `Dog` **is an** `Animal`. This creates a rigid family tree. + +```csharp +// The "is-a" relationship +public class Dog : Animal { ... } +``` + +This works for simple things, but it falls apart with complex game mechanics. Let's use a weapon example. You might start with this: + +```csharp +public class Weapon { ... } +public class MeleeWeapon : Weapon { ... } +public class RangedWeapon : Weapon { ... } +``` + +Easy, right? But now you want a **sword that also shoots projectiles**. Uh oh. Does it inherit from `MeleeWeapon` or `RangedWeapon`? What if you want a **gun that sets enemies on fire**? Do you make a `FieryRangedWeapon` class? What about a **healing, fiery, projectile-shooting sword**? + +You end up in a tangled mess of classes. This is the inheritance trap, and it's a nightmare under a 48-hour time limit. + +#### The Composition Solution: Think LEGOs + +Composition is a "**has-a**" relationship. Instead of saying an object _is_ something, you say it _has_ functionalities. + +Think of a `GameObject` in Unity or a `Node` in Godot as a single LEGO brick. On its own, it does nothing. The magic happens when you start snapping other bricks—**components**—onto it. A "weapon" in my game isn't a complex class; it's an empty object that **has** a collection of scripts defining its behavior. + +Let's rebuild our crazy weapon idea with composition: + +- We create small, single-purpose scripts (our LEGOs): + - `DealDamageOnHit.cs`: **Has** a damage value. + - `SwingAttack.cs`: **Has** a swing animation. + - `ShootProjectile.cs`: **Has** a bullet prefab. + - `ApplyFireEffect.cs`: **Has** a burn duration. + +Now, we can build any weapon just by mixing and matching: + +- **Simple Sword**: `GameObject` + `DealDamageOnHit.cs` + `SwingAttack.cs` +- **Flamethrower**: `GameObject` + `DealDamageOnHit.cs` + `ShootProjectile.cs` + `ApplyFireEffect.cs` + +Suddenly, you're not writing new classes; you're just assembling new objects. It’s fast, flexible, and exactly what you need when the clock is ticking. + +--- + +## The Payoff: Modding Support for Free + +So, what’s the ultimate benefit of this LEGO-based approach? You get powerful features that seem impossible in a 48-hour jam. For my game _Parasitic God_, this architecture meant I could implement a full-blown **modding system** with very little extra effort. + +It wasn't a feature I planned for. It was a natural, almost free, consequence of the data-driven design I had already built. + +### The Core Idea: Game Content is Just Data + +The central principle is that the "things" in my game—miracles, events, and upgrades—are not hardcoded. They're defined in simple **JSON text files**. + +The C# code is just an **engine** that reads and interprets that data. My `GameLogic.cs` doesn’t know what a "Miracle of Growth" is; it only knows how to process a list of abstract `Effect` resources. This separation is what unlocks modding. + +It’s built on three pillars: + +#### Pillar 1: JSON as the Blueprint + +Instead of coding a miracle's properties, I define its structure in a "Data Transfer Object" (DTO) like `MiracleDto.cs`. This means anyone can define a new miracle in a simple `.json` file—no compiler needed. + +```json +{ + "Name": "Gift of Life", + "FaithCost": 25.0, + "Effects": [ + { + "Type": "AddResource", + "TargetResource": "Followers", + "Value": 50.0 + } + ] +} +``` + +#### Pillar 2: The Universal Loader + +The game's `MiracleLoader.cs` finds and loads these JSON files. The trick is that it looks in two places: the game's internal `res://` directory for my default files and the player's special `user://` data folder for mods. The loader code simply loads the defaults and then loads the user's mods, allowing players to add new content or overwrite existing files. + +```csharp +// From Core/MiracleLoader.cs +public static Dictionary LoadAllMiracles() +{ + var loadedMiracles = new Dictionary(); + + // 1. Load the default game content + LoadMiraclesFromPath("res://Mods/Miracles", loadedMiracles); + + // 2. Load the user's mods, overwriting defaults if names match + LoadMiraclesFromPath("user://Mods/Miracles", loadedMiracles); + + return loadedMiracles; +} +``` + +#### Pillar 3: The Reusable LEGOs (Effect System) + +This is where it all ties back to composition. How can a JSON file actually _do_ things? The `"Effects"` array in the JSON is a list of commands. Each command tells my C# engine which LEGO brick to use. The `MiracleLoader.cs` reads the `"Type"` string (e.g., `"AddResource"`) and creates an instance of the corresponding C# `Effect` class. + +A modder can create a complex new miracle—modifying stats, applying buffs, and unlocking other content—all without writing a single line of C# code. They just assemble the available `Effect` LEGOs in a JSON file. + +--- + +## The Final Result & What's Next + +This system is the ultimate proof that good architecture pays for itself. The time I spent separating data from logic wasn’t a detour; it was a direct path to a more robust, flexible, and feature-rich game. + +So when my friends say, “Just make it work,” I say, “Let’s make it work _right_.” Because sometimes, working right from the beginning means you get incredible features like a modding system, not as a massive time sink, but as a happy accident. + +Going forward, I'm definitely going to take part in more game jams. While I'm happy with my coding practices, I've realized I need to grow as a **game designer**. My games work, but they often lack that certain _oomph_. That's the crucial skill I need to develop to create better experiences for players. + +In a perfect world, I'd find a team where I could focus on what I love: coding and creating tools for designers to bring their visions to life. + +--- + +### References + +- [**Itch.io**](http://Itch.io) **Pages:** + - [Broberry](https://gabrielkaszewski.itch.io/broberry) + - [Parasitic God](https://gabrielkaszewski.itch.io/parasitic-god) +- **Source Code:** + - [Broberry on GitLab](https://git.gabrielkaszewski.dev/GKaszewski/broberry) + - [Parasitic God on GitLab](https://git.gabrielkaszewski.dev/GKaszewski/parasitic-god) \ No newline at end of file