Add blog post on game jam architecture and development practices
All checks were successful
Build and Deploy Blog / build-and-deploy-local (push) Successful in 41s
All checks were successful
Build and Deploy Blog / build-and-deploy-local (push) Successful in 41s
This commit is contained in:
172
posts/beyond-it-works.mdx
Normal file
172
posts/beyond-it-works.mdx
Normal file
@@ -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<string, MiracleDefinition> LoadAllMiracles()
|
||||
{
|
||||
var loadedMiracles = new Dictionary<string, MiracleDefinition>();
|
||||
|
||||
// 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)
|
Reference in New Issue
Block a user