Cold Takes/19 - Game Jams
This week's cold take is early, unusual, and short because Ludum Dare is this weekend. Ludum Dare is a game jam: an event where individuals or teams of people make games in a short period of time. They tend to run for a few days to a whole week, depending on the jam, and teams are given a short prompt or "theme" to incorporate into the game. There are many game jams these days, I just do Ludum Dare for the schedule and because I like rating other peoples' at the end.
Everyone with an interest in making games should give game jams a go. The actual event only takes a weekend, although a bit of upfront investment to find an appropriate engine goes a long way. The barrier to entry is fairly low, and I even think game jams are a great way to learn certain aspects of programming and design. A jam is a microcosm of project management, where choices made at the start of a project tend to come back to bite you at the end. This process is particularly instructive when there are only three days between the choices and their consequences. And by the end, you get a game out of it.
I especially recommend game jams to anyone working on a long-term game, such as Zero-K, because speedrunning the whole process can clarify your progress on the longer game. It also lets you experiment with new ideas and flex your creativity, which probably helps prevent feature creep. Besides, making games is a matter of practise, and as the Go master said to the game designer: "make your first fifty games quickly".
I got involved in game jams through the Spring Cabal, a group of Spring developers that used Spring in several Ludum Dares between 2015 and 2018. The release pages for most of them were lost in the LD site rework, but builds can still be found in the organisation's GitHub repositories. Dominic (aka Shadowfury333) was also kind enough to document most of them on his YouTube channel.
- LD 32 - Gravitas
- LD 33 Warmup - Hunted
- LD 33 - Area 17
- LD 34 - FLove
- LD 35 - Parts Needed
- LD 43 - To The Last Drop
The only video missing is LD 42, My Cube, which I recall being a tower/unit defense style game about defending an iceberg. I only made it to a few of these weekends, so only worked on Hunted, Area 17, and To The Last Drop. The main organiser was gajop, and with the gradual disintegration of the Spring developer community, plus the difficulty of organising team across multiple timezones, we were less able to get teams together.
I am really pleased with To The Last Drop. The theme was "Sacrifices Must Be Made", so we thought up the idea of defending a town using magic powered by the lives of the townsfolk. It turned out to be a game, with a beginning, middle, and end, which kills idle players, but otherwise let you play around with different approaches. The success of To The Last Drop is possibly why I went on to make ten more games with people I knew locally, solving the timezone problem and making the event more social. For these games, we used the 2D Lua game engine LÖVE, since I have a lot of Lua experience, and because 2D art is easier to produce.
You can check out the games on my games page. We tend to make games that sit somewhere between puzzle and sandbox, which might be called problem-solving or "strategy" games. But we are also careful to give the games goals, and make the basic actions of the game inherently fun. You can see bits of Zero-K in many of them, in the way they are about mastering simple systems with emergent complexity. I think they are all quite solid, at least the "Post Jam" versions (make sure to click the right download), so my recommendations would depend on what you are looking for. Here are a few:
- Seeds of Civilisation is a bit like asteroids, but with planets and space police.
- Vivere Computatrum is a take on simple colony management, and even has a goal.
- Regular Railway is the most puzzle-like: Pipe Dream but Trains.
- Warp Trash Flotilla is absolute mayhem.
- Crocodial is about spinning a wheel of spells as fast as possible, but you are the wheel.
- Compact Domain is another puzzle game, this time pretending to be a city builder.
- Euclid's Elementals can be hard to get into, but the payoff is you get to feel like a wizard.
- To The Last Drop lets you be an "evil" wizard, and shows off what you can do with Spring.
Another common feature of our games is hard mode, since otherwise we risk subconsciously adjusting the difficulty upwards as we get better at the game. It also lets me play the game after the jam, just to push the systems to their limits and see what is possible. This approach is probably why we end up with open-ended problem-solving games rather than discrete single-solution puzzles: you can't really play your own puzzle games. Anyway, I hope this makes up for the lack of a more Zero-K-focused topic, and perhaps you end up having fun with a few games, or even have a go at game jamming.
Debug data:
[SQLBagOStuff] MainObjectStash using store ReplicatedBagOStuff
[objectcache] MainWANObjectCache using store EmptyBagOStuff
IP: 18.222.61.232
Start request GET /mediawiki/Cold_Takes/19_-_Game_Jams
HTTP HEADERS:
CONTENT-TYPE:
CONTENT-LENGTH: 0
X-ORIGINAL-URL: /mediawiki/Cold_Takes/19_-_Game_Jams
USER-AGENT: Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
HOST: zero-k.info
ACCEPT-ENCODING: gzip, br, zstd, deflate
ACCEPT: */*
CONNECTION: close[localisation] LocalisationCache: using store LCStoreDB
[session] SessionManager using store SqlBagOStuff
[DBReplication] Cannot use ChronologyProtector with EmptyBagOStuff
[DBReplication] Wikimedia\Rdbms\LBFactory::getChronologyProtector: request info {
"IPAddress": "18.222.61.232",
"UserAgent": "Mozilla\/5.0 AppleWebKit\/537.36 (KHTML, like Gecko; compatible; ClaudeBot\/1.0; +claudebot@anthropic.com)",
"ChronologyProtection": false,
"ChronologyPositionIndex": 0,
"ChronologyClientId": false
}[DBConnection] Wikimedia\Rdbms\LoadBalancer::lazyLoadReplicationPositions: executed chronology callback.
[DBConnection] Wikimedia\Rdbms\LoadBalancer::getLocalConnection: connected to database 0 at 'localhost'.
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[session] SessionBackend "5ojtpbomtmjlume7508n7acva25epbs9" is unsaved, marking dirty in constructor
[session] SessionBackend "5ojtpbomtmjlume7508n7acva25epbs9" save: dataDirty=1 metaDirty=1 forcePersist=0
[cookie] already deleted setcookie: "wikidb229_mw__session", "", "1710217408", "/", "", "", "1"
[cookie] already deleted setcookie: "wikidb229_mw_UserID", "", "1710217408", "/", "", "", "1"
[cookie] already deleted setcookie: "wikidb229_mw_Token", "", "1710217408", "/", "", "", "1"
[cookie] already deleted setcookie: "forceHTTPS", "", "1710217408", "/", "", "", "1"
[DBConnection] Wikimedia\Rdbms\LoadBalancer::getLocalConnection: connected to database 0 at 'localhost'.
Title::getRestrictionTypes: applicable restrictions to [[Cold Takes/19 - Game Jams]] are {edit,move}
[ContentHandler] Created handler for wikitext: WikitextContentHandler
[MessageCache] MessageCache using store SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] SqlBagOStuff::lock failed due to timeout for wikidb229-mw_:messages:en.
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[MessageCache] MessageCache::load: Loading en... local cache is empty, global cache is expired/volatile, loading from database
ParserFactory: using preprocessor: Preprocessor_Hash
Unstubbing $wgLang on call of $wgLang::_unstub from ParserOptions->__construct
[caches] parser: SqlBagOStuff
Article::view using parser cache: yes
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
Article::view: doing uncached parse
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[Preprocessor] Cached preprocessor output (key: wikidb229-mw_:preprocess-hash:b62cc6aacae733653e455a9550666310:0)
[Preprocessor] Cached preprocessor output (key: wikidb229-mw_:preprocess-hash:17aa7002849cc6ce738a62fa8e371aed:1)
[ContentHandler] Created handler for Scribunto: ScribuntoContentHandler
[Scribunto] Scribunto_LuaStandaloneInterpreter::__construct: creating interpreter: ""C:\Projekty\zero-k.info\www\mediawiki\extensions\Scribunto\includes\engines\LuaStandalone/binaries/lua5_1_5_Win64_bin/lua5.1.exe" "C:\Projekty\zero-k.info\www\mediawiki\extensions\Scribunto\includes\engines\LuaStandalone/mw_main.lua" "C:\Projekty\zero-k.info\www\mediawiki\extensions\Scribunto\includes" "0" "8""
[gitinfo] Candidate cacheFile=C:\Projekty\zero-k.info\www\mediawiki/gitinfo.json for C:\Projekty\zero-k.info\www\mediawiki
[gitinfo] Cache incomplete for C:\Projekty\zero-k.info\www\mediawiki
SiteStats::loadAndLazyInit: reading site_stats from replica DB
[Mime] MimeAnalyzer::loadFiles: loading mime types from C:\Projekty\zero-k.info\www\mediawiki\includes/libs/mime/mime.types
[Mime] MimeAnalyzer::loadFiles: loading mime info from C:\Projekty\zero-k.info\www\mediawiki\includes/libs/mime/mime.info
File::transform: Doing stat for mwstore://local-backend/local-thumb/a/a7/19_-_Game_Jams.jpg/1px-19_-_Game_Jams.jpg
TransformationalImageHandler::doTransform: creating 1x1 thumbnail at mwstore://local-backend/local-thumb/a/a7/19_-_Game_Jams.jpg/1px-19_-_Game_Jams.jpg using scaler im
TransformationalImageHandler::doTransform: Transforming later per flags.
File::transform: Doing stat for mwstore://local-backend/local-thumb/a/a7/19_-_Game_Jams.jpg/2px-19_-_Game_Jams.jpg
TransformationalImageHandler::doTransform: creating 2x1 thumbnail at mwstore://local-backend/local-thumb/a/a7/19_-_Game_Jams.jpg/2px-19_-_Game_Jams.jpg using scaler im
TransformationalImageHandler::doTransform: Transforming later per flags.
File::transform: Doing stat for mwstore://local-backend/local-thumb/a/a7/19_-_Game_Jams.jpg/2px-19_-_Game_Jams.jpg
TransformationalImageHandler::doTransform: creating 2x1 thumbnail at mwstore://local-backend/local-thumb/a/a7/19_-_Game_Jams.jpg/2px-19_-_Game_Jams.jpg using scaler im
TransformationalImageHandler::doTransform: Transforming later per flags.
File::transform: Doing stat for mwstore://local-backend/local-thumb/e/e5/LD_TtLD.png/800px-LD_TtLD.png
[FileOperation] FileBackendStore::ingestFreshFileStats: File mwstore://local-backend/local-thumb/e/e5/LD_TtLD.png/800px-LD_TtLD.png does not exist
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_d900c333a2e6.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
File::transform: Doing stat for mwstore://local-backend/local-thumb/e/e5/LD_TtLD.png/800px-LD_TtLD.png
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_c61e9efe28e2.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
File::transform: Doing stat for mwstore://local-backend/local-thumb/e/e5/LD_TtLD.png/800px-LD_TtLD.png
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_6005c329df0c.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
File::transform: Doing stat for mwstore://local-backend/local-thumb/c/c2/LD_list.png/800px-LD_list.png
[FileOperation] FileBackendStore::ingestFreshFileStats: File mwstore://local-backend/local-thumb/c/c2/LD_list.png/800px-LD_list.png does not exist
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_4f71f3c7b1b5.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
File::transform: Doing stat for mwstore://local-backend/local-thumb/c/c2/LD_list.png/800px-LD_list.png
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_1804be2c5937.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
File::transform: Doing stat for mwstore://local-backend/local-thumb/c/c2/LD_list.png/800px-LD_list.png
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_f27c01ac4915.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
[Preprocessor] Cached preprocessor output (key: wikidb229-mw_:preprocess-hash:b62cc6aacae733653e455a9550666310:0)
Saved in parser cache with key wikidb229-mw_:pcache:idhash:1975-0!dateformat=default and timestamp 20250312042328 and revision id 10025
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
MediaWiki::preOutputCommit: primary transaction round committed
MediaWiki::preOutputCommit: pre-send deferred updates completed
MediaWiki::preOutputCommit: session changes committed
MediaWiki::preOutputCommit: LBFactory shutdown completed
File::transform: Doing stat for mwstore://local-backend/local-thumb/e/e5/LD_TtLD.png/800px-LD_TtLD.png
TransformationalImageHandler::doTransform: creating 800x450 thumbnail at C:\Windows\TEMP\mwtmp-IUSR/transform_7cdd3bb77153.png using scaler im
TransformationalImageHandler::doTransform: returning unscaled image
Title::getRestrictionTypes: applicable restrictions to [[Cold Takes/19 - Game Jams]] are {edit,move}