Website powered by

BattleTrain - Anatomy of the Spline Route System

Battle Train's Spline Route System was one of the first major features that are a core part of the gameplay loop I was entrusted with. Before this point I wasn't too familiar with how a lot of the gameplay systems worked on a technical level, and our tech director Zuko saw that this was a great opportunity for me to learn a lot about that within an isolated system. While working on the spline system I learned a lot of key fundamental concepts that have been instrumental in all sorts of systems I work on now, such as iterating through arrays of structs to filter for the data that I need.

Here's an example of the spline system in action. When the player clicks on an outpost that's connected to something attackable, the spline routes get created.

The trains during the attack also make use of the splines, conveniently knocking 2 birds with 1 stone.

To start off, I cycle through an a map of all the existing "attack routes" that exist in the gameboard's logic. The goal is to find the starting point that was clicked on, then populate the splines based on the already created attack routes.

To start off, I cycle through an a map of all the existing "attack routes" that exist in the gameboard's logic. The goal is to find the starting point that was clicked on, then populate the splines based on the already created attack routes.

Now that we have all the attack routes, we want to filter down which ones match our starting point, and also want to figure out which one will be our main "selected" spline.

Now that we have all the attack routes, we want to filter down which ones match our starting point, and also want to figure out which one will be our main "selected" spline.

Now that we have each individual attack route from a station, we can start populating the spline actors with data. Note that I don't want to spawn new splines, I want to re-use existing ones since instancing a new spline is very costly performance-wise.

Now that we have each individual attack route from a station, we can start populating the spline actors with data. Note that I don't want to spawn new splines, I want to re-use existing ones since instancing a new spline is very costly performance-wise.

The spline actors are a separate blueprint class, and within is a function in which we can start feeding data and putting spline points down in the world!

The spline actors are a separate blueprint class, and within is a function in which we can start feeding data and putting spline points down in the world!

For each set of route tiles, we have to calculate whether the next tile is part of the route and which direction it's in. We basically have to sift through the connecting tiles and make sure we find the location of the right one.

For each set of route tiles, we have to calculate whether the next tile is part of the route and which direction it's in. We basically have to sift through the connecting tiles and make sure we find the location of the right one.

Once we find the connecting tile we want, we can calculate the location of the spline point, as well as the tangents for the point.

Once we find the connecting tile we want, we can calculate the location of the spline point, as well as the tangents for the point.

The first step for the spline route visuals, is figuring out whether the individual spline is the target spline or if it's one of the other possible routes that can be cycled to.

The first step for the spline route visuals, is figuring out whether the individual spline is the target spline or if it's one of the other possible routes that can be cycled to.

With the spline actor knowing whether it's the target spline or not, we can determine which visuals to enable and disable.

With the spline actor knowing whether it's the target spline or not, we can determine which visuals to enable and disable.

On the particle side, niagara makes it super easy to play an effect along a spline. The splines are essentially a set of closely distanced circle sprites

On the particle side, niagara makes it super easy to play an effect along a spline. The splines are essentially a set of closely distanced circle sprites

In Niagara, hooking up a particle to a spline is as simple as creating a module that takes in a spline and then configuring

In Niagara, hooking up a particle to a spline is as simple as creating a module that takes in a spline and then configuring

For the main target spline, we can fine tune the parameters as need be, including any additional logic we might need, for example the particle lifetime needs to be based on the distance of the spline.

For the main target spline, we can fine tune the parameters as need be, including any additional logic we might need, for example the particle lifetime needs to be based on the distance of the spline.

For the ghost splines, there's less tuning needed since they shouldn't stand out as much compared to the main spline.

For the ghost splines, there's less tuning needed since they shouldn't stand out as much compared to the main spline.

Originally, there was no logic for filtering out attack routes. Every possible iteration of a tile route would be a separate attack route, which would mean that our logic above would generate a spline for each possibility.

Originally, there was no logic for filtering out attack routes. Every possible iteration of a tile route would be a separate attack route, which would mean that our logic above would generate a spline for each possibility.

To optimize, for each route I build an array of the TileModifiers on the route, including their position on the gameboard. If the arrays of modifiers perfectly match, it's safe to remove the route from being generated during spline creation.

To optimize, for each route I build an array of the TileModifiers on the route, including their position on the gameboard. If the arrays of modifiers perfectly match, it's safe to remove the route from being generated during spline creation.