Train Jam is exactly what it sounds like: A Game Jam that takes place on a train. In particular, the Amtrak California Zephyr — which travels all the way from Chicago to San Francisco. Roughly 380 game developers descend on Chicago ready to board this train with laptops, cameras, snacks, game engines, and some serious passion — perhaps even a bit of crazy. On the 52 hour train ride, these developers are tasked with a single goal: create a game that can be shown off at GDC — the final destination of the trip. This year I was one of those developers.
My team and I fly out of Boston Logan International Airport in the late afternoon. There’s four of us, each with 3 days worth of snacks, clothes, game documentation, and prototyping materials in tow, and I’ve got a broken leg. Surprisingly the leg isn’t much of an issue, and we make it through security in plenty of time. Sitting by the gate we’re buzzing with excitement. As this is our first Train Jam, and some of our first Game Jam we’re not quite sure what to expect.
“I hope I downloaded everything we’ll need. I have Unity, its documentation, and some assets. That should be enough, right?”
Everyone laughs nervously, knowing that we haven’t been in a situation quite like this before. See, Train Jam doesn’t have any connectivity — no wifi. Sure, you might catch a cell signal here and there along the journey, but in general there’s no way to access the web. That means if we’re stuck on an issue, we can’t take to StackOverflow to self-correct. We’re stranded there with just our brains and collective knowledge. The idea is kinda scary. Eventually everyone confirms that this, in fact, is likely all we’ll need. We board the plane to Chicago and the flight is quick. Before I know it we’ve landed at O’Hare.
When we touch down we hop in a Lyft and head directly to BitBash Chicago, a party at a popular bar with some games set up for play. When we arrive we meet lots of game industry folks all mulling about. Everyone is talking about the impending trip out to GDC. For many, this will be a part of Train Jam. For others, they’ll be flying out. My leg being broken makes it hard to walk around and chat, but my awesome coworkers Adina and Sarah introduce me to some very cool folks that are working on amazing things. I’m super excited to hear about people’s backgrounds and projects, but tire quickly and sit down to rest and grab some food. I take it easy for the rest of the evening, knowing full-well that I should pace myself — we’ve got a long journey ahead. After saying goodnight to some friends, we head to our hotel and call it a night.
I wake up at 6 AM. Despite traveling fairly often for work I’m still terrible at adjusting to timezones, and any change totally disrupts my sleep schedule. Once I’m up, I’m up for good, so I shower, get dressed, and head down to the hotel kitchen to grab some breakfast and tea. I power through some email since it’s a business day and folks are still working back home — I know there won’t be time later. In a couple hours my team meets up with me and we head to Chicago’s Union Station to check in. When we get there everyone is hyped up. Train Jam has an entire room at the station taken over, and we get to start meeting people! They give us nametags that we get to customize and awesome T-shirts. There’s a bunch of camera crews around as well, which definitely adds to the hype. When everyone settles in we prepare to learn about the GameJam theme. This will be super critical, as our games are expected to represent the theme in one shape or another.
“This year’s theme is Odyssey! An epic journey or long wandering voyage.”
Adriel, the founder of Train Jam, steps down off the literal chair from which she was announcing things. We all cheer, and a buzz of chatter spreads throughout the crowd as everyone starts discussing ideas. My team and I start thinking about narratives that might make sense, but quickly pivot toward some non-traditional ideas. You see, we enjoy messing with our players and making projects that are comical. Here’s a quick list of some of our ideas:
- Dumb quest — A dungeon crawler with lots of rooms, tons of attributes that you can build up, and various point systems that give you random awards. However, none of those impact the game whatsoever, and the objective can be found in the room the player starts in.
- Columns — Play as a large temple or room in which heroes pass through. Your job is to hold the building up while the heroes take their sweet time reading dialog and exploring. Why won’t they just leave?!
- Minimap — Play as the minimap in an exploration game. It’s your job to keep the minimap up to date with the world, place waypoints, tag enemies and more. If you make a mistake, your hero may end up in an undesirable spot.
- 100 Year Sleep — Play as a protective castle, in which a princess is trying to take a nap. However, suitors constantly knock on the door, barge in, and awake the princess. Keep them away for 100 years by scrolling through time and modifying the world. She just wants to sleep, dammit.
We decide that we really like the idea of messing with time, and want to learn how to build a cool mechanic that uses that. We’re aware that time can be a tricky concept, and building games that mess with it is often quite a task. However, it’s something we think would be really impressive, so we decide to give 100 Year Nap a go.
We decide to scope the experience to a single level, where-in you can see 100 years play out in one room. If you let the level play through time in order, the princess is awoken by the first suitor that comes along and you lose the game. However, if you scrub forward in time, choose to drop a chandelier on the second suitor (killing him) you’re able to take his items and store them in your inventory. This inventory is impervious to time, and allows bringing items back to the past from the future. Using a lighter that was found in this second suitors inventory you’re able to rewind time and light the rug on fire, preventing the first suitor from waking the princess. Since both suitors that approach in those 100 years are now killed, the princess is able to sleep through the night and you win the level. Here’s our first draft of such a level:
We think that we’ll be able to achieve a 2D game with a medieval style using some of the assets we’ve brought along to prototype with, which is great because we know we’ll have to spend a ton of time on the core mechanic, which will take away from visual design time. Finally it’s time to board the train, and we have a decent plan of attack.
We board, and find our quarters. It takes a while because of my leg, and we actually get lost between the meeting room and the train because I’m so slow. But finally we settle in and look at the space we’ll be living in for 3 days. It’s super small, but actually really impressive — whomever designed it was able to fit so many things into such a small space.
At ~2PM we pull out of the station and are in full hype-mode. There’s cheering as we take off, and the Jam officially begins. We start investigating the technology we can use to make this work. Since we have experience with Unity, we decided previously that we’d be using that for the Jam. Immediately, a new Unity feature jumps out at me as a possible solution. That’s Timeline — a feature Unity built to make it easy to define sequenced actions much like you’d want when building out a movie. We’re hoping to be able to build our levels using that technology, and use the built in fast-forward/rewind mechanics. I start digging through the docs to build a quick proof of concept. Meanwhile the rest of the team starts investigating other approaches.
Timeline is pretty interesting. It appears to be great at taking built animations for objects and allowing you to order them, blend them together, and control their playback. However, it’s fairly complicated under the hood. When I eventually got something working, it was pretty convoluted, and didn’t feel like it should be modified at runtime. Since we needed to modify events in the past and future, we were convinced we needed this runtime modification ability. As a workaround I devised a system where-in we’d create a Timeline with branching clips — that is, clips for the same object that take different actions. Then I verified that we could selectively disable one of those clips at runtime, effectively choosing a branch permanently. It worked! But it wasn’t very elegant and didn’t scale super well. Such a solution also meant that all our game interactions that needed to support time scrubbing would need to be implemented as animations, or other events that timeline can control. To be fair, perhaps it was because we were using offline docs…Regardless, we weren’t convinced this was flexible enough and decided to see if we could use a different approach.
We kept thinking of ways to design a system for rewinding time, using positional recording or some representation of commands that could be played forward or backward. Eventually, it was 5PM. We went and had dinner while we kept thinking. This was our first introduction to Train Food. Train Food is amazing.
Maybe it’s because we had worked up an appetite, or because we didn’t have enough water, but Train meals were amazing. We had the best side salads. In retrospect it was just lettuce, a tomato or two, some onions and some carrots. But in the moment, I kid you not these salads were the best thing ever created. And the rolls. Each dinner we got a basket with a roll for each person. They were just normal rolls, and they weren’t even warm. However, we ate them as if we’d never had bread before. When it came to main courses, options were really great as well! Though not as great as the salads. We ate, discussed next steps, and enjoyed the view.
After dinner we decided we needed to further spec our level, as we figured if this was going to work we’d need to cheat a bit and make time modification work just enough for our interactions to behave correctly. We started thinking about position tracking systems that simply scrubbed though object locations over time. We thought about collisions. Eventually we tabled the conversation and paired off — one pair started building the 2D level, and another started a prototype time scrubber. 2 hours passed. We reconvened.
“I’m not sure we can make time scrubbing work and create all the level content we need to have the mechanic shine.”
Eric raised this point first. We’d hit another wall in the time mechanic design, and while level building was progressing, it was clear that it would be a lot of work to design the core scenario we’d imagined. This prompted a huge late night discussion that I don’t recall all the details of. Basically, we realized this was likely the case. We spent a few hours thinking through ways we could keep the time scrubbing mechanic but reduce scope elsewhere. “Maybe a text based game, where you can rewind story” suggested Molly. We thought it through, though personally I wasn’t super passionate about working on a text-based game. Ultimately we decided that some holes in the gameplay mechanics coupled with that the lack of passion on my behalf made it not worth pursuing. We thought about more time ideas. We couldn’t think of anything compelling and in scope. We started pitching ideas to pivot to.
Eventually at around 11PM we landed on an idea. We’d use our existing 2D level building work to build a Robot Vacuum simulator with an intentionally complicated control mechanic. This aligned with our initial ideas of messing with our players, and we framed it such that the Vacuum was cleaning Odysseus’ apartment while he was out. We scrapped the time mechanic and started playing with power ups. Somewhere around midnight I decided I wanted there to be a layer of dust on the floor. I started prototyping it. I fell asleep.
Sleeping on a train is not particularly enjoyable. Everything rattled. Lights flashed by. We convinced ourselves that the driver drives much faster at night. We felt every turn. I woke up at 8AM — we were in Denver. I grabbed my laptop and finished prototyping the dust layer. The team woke up. We got breakfast.
Breakfast was amazing. We had crazy views. We had pancakes. We had caffeine. Despite the rough nights sleep we all bounced back, and got excited about our new idea. We started thinking about power ups that we could build:
- Hermes — Borrow the wings of Hermes to speed clean the floor.
- Zeues — Shoot lighting bolts directly in front for a long-range cleaning attack.
- Poseidon — Upgrade to mopping to protect tiles as you move across from future stains.
- Athena — Call upon Athena’s wisdom to place you in the dirtiest area.
- Narcissus — Spawn an uncontrollable clone of yourself to help you clean.
When we finished eating we were eager to finish our basic robot movement (without control) and start messing with dust and power ups. Molly started to build some of the game lore and our game menu flow. Eric began working on our robot control system. Ben and I teamed up to build a flexible power up system, and some power ups atop it. We worked hard until lunch.
We got salads again with lunch! I can’t stress how amazing these little salads were. Especially covered in Italian dressing. mmhmmm. Salad. And the views! We were passing through a valley, along the Colorado river. The scenery was beautiful — here’s a taste:
At lunch, we discussed blockers and next steps. We had some issues with the math related to determining a forward direction for our robot and it was slowing progress of our control system. We concluded we’d put our heads together for some math and see if we could reach a solution. We discussed the framework we’d use to build our newly defined game menu flow. We took a break to look out the window at the amazing views and talked about random stuff.
When we got back to the rooms we smashed our heads against some math for a while. No real progress was made; we were dealing with some weird Rigidbody2D behaviors that we weren’t familiar with. Eric decided to go and try to find a Train Jam mentor to ask some questions. I worked with Molly to leverage Unity-MenuStack to build out our menus. Then Ben and I split off to build some power ups individually. We hit a bunch of blockers when coding power ups — our game was using Unity 2D Tilemaps and we ran into a number of issues converting from world coordinates to tile coordinates accurately. This impacted our ability to have our robot vacuum up objects properly, as well as our ability to spawn items in positions on the grid. We invented workarounds, using the limited documentation we had. Eric returned; we made further progress. It was dinner time.
THE SALADS ARE STILL AMAZING AND WE GET ROLLS TOO?!?!?!?!!!?!?!
It’s clear at this point that we’re going to have a hard time building some of the power ups that we want, because of our tilemap layout, tilemap coordinate conversions, and Rigidbody2D issues. We set a deadline for ourselves that we’ll ship whatever power ups make it in by 10PM. Eric had some great ideas on how to do the math to make our control system work. We finish up our meals and head back, heads down ready to make some progress.
10PM arrives and we have 2 power ups completed. We’ve gotten stuck on two others and are trying all sorts of strange hacks to get things to work. It’s worth mentioning at this point that we’re pretty exhausted. At some point I start humming the Overwatch theme song. And yelling at my computer.
“Daaaa duh duh da, duhhhh daa daa daaaaaaaa. Dammit!”
My team is unphased for the most part. 11PM arrives and we have 3 power ups, and a basic version of controls. We’ve tried to get 4 power ups but the 4th requires a pretty major redesign of the tilemap stuff and we’re already an hour past our deadline. We cut the other power ups, even though we really want nothing more than to push ourselves to complete them. There’s too much left to do — we haven’t even thought about sounds, we need a ton of logic to wire our game code to our UI, and we need win and loss conditions. We collectively decide we’re too exhausted to go on and get ready for bed. I decide to try and learn more about tilemaps by playing with the APIs. I fall asleep without making any progress.
We wake up early and grab food quickly. We know we’ll have a ton of work to do today to bring everything together, so we’re feeling the pressure a bit — rushing through breakfast.
Breakfast doesn’t come with salad. I am disappoint.
Eric designs a system to help manage the state changes that occur during the game. We have some similar systems in Skorecery (the game I’m currently working on) and have learned a lot. Our plan is to use this to wire data up to the UI and to pass data off to the win/loss condition overlays. While that system is being built we’re working to wire up a quick tutorial and guide. All of a sudden it’s lunch time.
YOU GUYS DO YOU KNOW WHAT COMES WITH LUNCH!?!?!?!
We talk about the remaining work over lunch and the train gets stuck due to stormy weather ahead. This extra time is actually a god-send; we’re feeling optimistic that we’ll be able to get the basic end-to-end wired together. Our estimated arrival time is around 4PM. That leaves us ~4 hours.
When we get back to the room we finish wiring up the menus and tutorials that don’t interact directly with the game logic. We turn all the menus into prefabs and start thinking about integration. From this point forward, we’re going to try and be aware of how all the different systems will come together. Power ups come in hot — we landed some last minute changes to make spawning them more reliable and merged that with the previous work we’d done. They work great! We start thinking about balance — like how often these should spawn. We get control logic merged as well, without issue. The final puzzle piece is win/lose state and game reset. There’s quite a bit of glue code here and we end up trying to take some shortcuts to hack it together to meet the deadline. But it isn’t quite right.
3:30PM hits and an announcement goes out train-wide that we should start cleaning our spaces. It’s re-enforced that our games aren’t due until midnight, so we take the time to tidy up — tabling the win/loss problems. We get excited for GDC and getting off the train. We’ve been on the train for forever. I don’t remember ever not being on the train. We’re pretty sleep deprived. We have most of a game working. We’re not fully convinced anything that’s happening is real life.
When we arrive at the station we take a huge group picture and say goodbye to friends! Train Jam is over and folks are starting to think about GDC. I’m excited to be in SF, even if I am super exhausted. We grab a Lyft to our Airbnb and decide that we’ll work on the game until 9PM — then we’ll upload our work and celebrate completing a Game Jam!
9PM — We’re still fighting with the win/loss states. Our systems aren’t coming together correctly and we’re super tired. We decide to give it another hour.
10PM — We start filling out the itch.io page for our project, so that we’re ready to submit the second we’re done with win/loss stuff. But we’re still stuck there. We decide that we’ll give it ‘til 11. At this point we’re all crowded around one computer, but more brainpower isn’t the solution. We try lots of hacky solutions, but none are quite right. At 11PM we decide to cut the data in win/loss screens. We refactor the menus and remove all the game data we wanted to communicate to players to rate their success. We decide that in it’s place we’ll show a simple “you win” or “you lose” message. We package the game and upload it to itch. We crack open the beers.
It’s at this point that we realize we haven’t run the game as a standalone throughout the entire GameJam. We should know better, but we don’t or we’ve forgotten. We boot it up. Resolution is locked at 500x600. Upscaling produces this weird bug wherein the robot moves at a constant 3x speed. We have to laugh. At this point it’s really too late to fix it, and we’re too fried to debug the issue in the first place. We decide to add a note to our submission page:
Runs best at 600x800. For an added bonus, try speed mode by picking a higher resolution.
We decide that we’ll do a postmortem in the morning. We try and stay positive, because we completed a thing all on our own in tough conditions! But it’s a struggle — I go to bed with mixed emotions. I’m glad we accomplished something and I had a ton of fun with friends. I had an awesome experience. I saw cool places. But, there are things I wish we were better prepared for and issues that I thought I would’ve been able to overcome that I wasn’t.
I wake up at 8AM with an idea. I open the game and force perspective to 16:9, and resolution to 1920x1080. I make a few small changes to the UI so that things appear visually correct. I re-upload the build. I go back to bed.
When the team gets up I share my “solution” — they chuckle but ultimately we decide that the GameJam is over and we’re not going to add any more advanced fixes, so we’re happy with that. We chalk it up to a success, tweet stuff out, and get ready for GDC in a few days. Here’s some pictures of the final result:
Train Jam was loads of fun, and ultimately a success. We learned a ton of stuff, and completed a game that we call Meanwhile — check it out for yourself!
Should you do Train Jam? Absolutely — but you’ve got to want it. 52 hours on a train is really testing. Building software without internet connectivity is tricky. That said, I think I like it — I like the challenge, I like the focus, I like the people, I like the industry. Would I do it next year? Yeah, I think I might. And if you decide you want to experience the chaos and community of Train Jam yourself, maybe I’ll see you there! 😸
- Meanwhile on itch.io 🎮
- Skorecery by Grapplehook Games ✨
- Timeline by Unity
- Tilemaps by Unity
- Unity-Menustack by Me ✨
- Prototyping assets by Kenney
If you liked this article, you’ll love my other posts. Further, if systems level thinking and product design is interesting to you (especially as they pertain to the games industry), follow me on twitter and catch me live-streaming game development Sundays at 2PM EST on twitch!🚀