Website powered by

BattleTrain - Anatomy of the Trinket Train

One of my favorite parts of roguelikes is when you accumulate powers over the course of a run, there's a tangible and visual difference in your character, or in our case, the train. At first, we didn't really intend for the train trinket's visual component to play a big role in the game, the train would just sort of appear with the different parts attached. When I first started, I had no experience working with UIs or Widgets, however, as I worked on the system more and more, we realized that my initial socket system for the train parts was actually super easy to keep expanding on, and people loved the visual flavor we were able to incorporate.

The premise of the system is simple. The train is made up of a set of sub-widgets. Inside of each widget I store a vector 2D which will serve as the offset needed to attach the train parts together.

The premise of the system is simple. The train is made up of a set of sub-widgets. Inside of each widget I store a vector 2D which will serve as the offset needed to attach the train parts together.

As we iterated on the system, it grew more and more robust with features. We added slots so you can have an absurd amount of hats, sticker slots that let you have a gold star, animations, and a whole lot more. This was a proof of concept for the theory.

Trains are broken up into two main categories of parts. "Main" parts and "Ornaments". We have a struct named "Player Train Trinkets" which holds all the different trinkets the player has acquired to this point.

Trains are broken up into two main categories of parts. "Main" parts and "Ornaments". We have a struct named "Player Train Trinkets" which holds all the different trinkets the player has acquired to this point.

I have fallen in love with data asset based workflows. The array of Player trinkets is made up of data assets that each hold whatever data I or anyone else might need to access.

I have fallen in love with data asset based workflows. The array of Player trinkets is made up of data assets that each hold whatever data I or anyone else might need to access.

The math for any part that doesn't rely on offsets is super simple to position with the socket system!

The math for any part that doesn't rely on offsets is super simple to position with the socket system!

However it gets a little more complicated when offsets are introduced... Thankfully we only had to worry about offsets for the cabin so I didn't have to come up with a universal solution.

However it gets a little more complicated when offsets are introduced... Thankfully we only had to worry about offsets for the cabin so I didn't have to come up with a universal solution.

What's cool is that since the train's widget logic is all self-contained, it can be used anywhere easily simply by referencing the player's equipped trinkets!

What's cool is that since the train's widget logic is all self-contained, it can be used anywhere easily simply by referencing the player's equipped trinkets!

So whether it's the customization screen or the attack screen, all that needs to happen is to call the build train function and it'll appear.

So whether it's the customization screen or the attack screen, all that needs to happen is to call the build train function and it'll appear.

Here's an example of what swapping train parts looks like.

To animate the train parts, we have them split into groups in the widget hierarchy, and then we can simply animate the group in and out of frame.

After the out animation, we can update the player's currently equipped trinkets, build the train with the new set of socket offsets while the widget is off-screen, and then re-animate it in with everything in its new spots.

After the out animation, we can update the player's currently equipped trinkets, build the train with the new set of socket offsets while the widget is off-screen, and then re-animate it in with everything in its new spots.

With so many different possibilities of Train Cars and wide varieties in proportions, it was important to have a system in place that made it easy to test different configurations easily. It's honestly really fun just messing around with different pieces.