1. FOUNDRY
  2. News

FOUNDRY News

Dev Blog #32 | Foundry Fridays: Curve Appeal


Foundry has been undergoing some massive changes over the past few years, bringing on developers from across the industry to create a unique factory game like no other. Myself (MarkL) and Cheerio were previously at a game studio called Klei, where we regularly worked with traditional animators and artists from the world of television cartoons. I thought for this Foundry Friday I would share some secrets they taught me, and what I find to be the most useful curve in all of game development.

Traditional animation training can impart numerous lessons, including Disney’s famous “Twelve Principles of Animation”.

Foundry is very much not a Disney cartoon, but many of these principles are still useful. We have examples of most of the twelve principles in Foundry, but in this post I chose to focus on the ones I, as an engineer/designer, reach for the most. After learning about these principles in Foundry you may start to see them in many of your favorite games.

Slow in, slow out


Many 3D animation packages use this curve on movements by default. This creates a more natural motion because it mimics the need to accelerate and decelerate. But there is another place you will find this curve, in graphics!

The shader function: smoothstep()

If you were to peruse the Foundry code base you would find this function used liberally. It’s a great way to create a visually pleasing falloff or blend. Recently I have been working on a new grid visualization shader to help you place objects, and as the grid approaches an edge we use the smoothstep to blend it off, instead of a jarring hard cut.



Searching through Foundry’s shaders it looks like we have used this to “smooth things out” 103 times, from lighting falloffs to chroma keying.

Exaggeration

Now on to my absolute favorite curve and animation curve of all time. This curve will come in handy no matter what you are working on and make everything feel more “intentional” and exciting.



We have placement helpers when building objects that we need to show, let's first take a look at linearly scaling them in and see how that feels.

I slowed the animation down so it shows up on GIF better

That feels cold and mechanical. Now what if we apply that exaggeration curve?



Now it feels more Impactful, playful and intentional! Truly the easiest improvement you can make to any linear animation. This is the first curve I reach for in Foundry whenever any animation doesn’t feel “right”. Heck, here it is when displaying UI options:



This almost cartoony exaggeration feels pleasant and playful. It’s such a simple concept, a curve that goes a bit too far and comes back.

Rewind

I feel like the curve is basically communicating to the user that "hey, we mean to be doing this thing". With that knowledge you can now apply these curves in reverse, like when we remove things.

Reverse exaggeration curve

When objects or terrain shatters we despawn it over time. Previously we simply popped it off, but that was jarring and unpleasant. Here I've added a despawn over time AND an immediate despawn when the objects get close to the camera, so they don't clutter your vision.



The effect is subtle, but satisfying.

In Summary

Video games (even 3d ones) can borrow a lot of cartoons and traditional animation. There is no reason the to reinvent the wheel. When you look outside your own "bubble" you can find helpful lessons that might apply in your situation, even outside of games.

Cheers,
-MarkL

Dev Blog #31 | Foundry Fridays: Smokin’ It Up In Foundry



Welcome to ‘Foundry Fridays’

Hello! My name is Jason, the art director of Foundry. I have the honour of writing this instalment of our ‘Foundry Fridays’ dev diary, a place for different members of the Foundry team to share what they’ve been working on. Today I’m going to talk about smoke VFX.

But wait!! …before we dive into that, let’s first talk a little about our art style.

Finding Our Style

If you’ve been reading our previous dev diaries and seeing our recent posts on Discord, you can see we’re going with a stylized look for Foundry. Considering we have a procedurally generated voxel world, we felt a stylized look was best suited for our game.

But how stylized?!

If we looked at a spectrum with a “photoreal” look on the far left and a “very stylized” look on the far right, we’re targeting something that’s somewhere in between, but leaning more towards stylized.

Our art style can be described as ‘soft, vibrant and cleanly illustrated’. The weathered, chipped edges of metals on factory machines are ‘clean’ rather than ‘dirty’ with grunge and high frequency noise. Biomes provide a soft and welcoming backdrop that contrasts nicely with the heavy, bulky, industrial look of the factory machines. The ‘clean’ and ‘illustrated’ look is what unifies everything together to create a cohesive and consistent art style across the game.

The world of Foundry should be a place you want to stay and play in for hours and hours.



Now For Making Stylized Smoke

As with all the other visuals across the game, we wanted to create a look for our smoke VFX (along with all other VFX in the game) that fit nicely within our art style. Smoke is obviously a big part of the visuals in a factory building game, so we wanted to put in the effort to come up with something very appealing to look at.

Our first attempt was leaning a little too far into the ‘very stylized’ end of the spectrum. It was a more 2d animated look that you often see in cartoon anime. Although moving in the right direction, it looked too flat and felt a little disconnected from the other visuals in the game. Here’s what that looked like…

[previewyoutube][/previewyoutube]
We then came up with a look that brought a little of that 2d animated look and combined it with soft, fluffy, thick volumetric shapes. So delish - I wanted to eat it like cotton candy. By using normal maps as well, we were able to get our directional sun and moon light to highlight the puffs of smoke. This gave it even more shape and made it feel part of the surrounding environment with our dynamic time of day lighting.

[previewyoutube][/previewyoutube]
This brought joy to our hearts 😄

The smoke now felt part of our stylized world and also felt unique and signature to Foundry.

Final Thoughts

Finding an art style is often a daunting task. It takes A LOT of iteration and fast prototyping to finally achieve the direction that's right for the game.

Our stylized smoke is just one part of the overall aesthetics of the game and is serving as a guide for all VFX in Foundry.

Till Next Time!

I hope you enjoyed this Foundry Friday. As always, if you have any questions, please hit us up on Discord.

Thanks for reading!

Dev Blog #30 | Foundry Fridays: Everything in Moderation

A First look at the Foundry Mod Kit

Today I wanted to talk about one of the ways that you’ll be able to create mods in Foundry. This talk will be mostly focused on how you can script new components and systems to enhance existing buildings with new functionality.

More specifically, we’ll be looking at taking one of my favorites: The Logistic Container



And turning it into: The Planter Box



Modest Beginnings

To make this new planter building, I don’t want to start from scratch. So the first thing I’m going to do is open the Foundry Asset Library and create a clone of the Logistic Container I:



This sets me up with a copy of all the files I need and after buying a cool set of plants from the asset store, I can quickly setup my new planter box prefab:



It’s probably worth mentioning at this point that I’m a programmer by trade so if you’re looking at this planter box and thinking “this doesn’t look too good”, you would be correct. That’s ok though because I wanted to make sure I went through all the steps of creating a mod myself, similar to how awesome people like you will be doing it at home.

Now on to the scripting!

One Component at a Time

The first thing I want to do is create some components to store all the data I’m going to need. If you’re familiar with Unity/MonoBehaviours, these are quite similar:

[expand]

// A component that represents a planter
// box which can have a plant growing in it
public class PlanterBox : IModComponent
{
// The plant growing in this planter box
[Save] public ulong plantId;
}

// A component that represents a plant that
// grows in a planter box
public class Plant : IModComponent
{
// How many sim ticks till the plant is fully grown
public int totalGrowthTimeInTicks = 100;

// What item template spawns this plant
public string itemTemplateId;

// How long has this plant been growing for
[Save] public int currentGrowthInTicks;
}


[/expand]

Once you've created the script for your components, we can go ahead and add them to our Planter Box prefab:



Now on to the systems!

Systematic Behaviour

Systems are how you can add new behaviour to Foundry. First let's start with our PlantSimSystem. The only thing this system does is slowly grow our plants over time:

[expand]

// A system which handles the growth of plants
//
// [AddSystemToGameSimulation] automatically starts
// this system when a game is started

[AddSystemToGameSimulation]
public class PlantSimSystem : SystemManager.System
{
// [LockstepTickRate(60)] means that this system is updated every
// 60 simulation ticks. It's an easy way to reduce the cost of your
// update.
[LockstepTickRate(60)]
public override void pstLockstep_tickUpdate(ulong tickId, DesyncHasher hasher)
{
// Here we iterate over all the Plant components
foreach(var kvp in Mods.Components.getAll())
{
// First we get the plant component
var plant = kvp.Value;

// Then we check to see if the plant is fully grown,
// and if so, then we skip the rest of the update.
bool isPlantFullyGrown = plant.currentGrowthInTicks >= plant.totalGrowthTimeInTicks;
if (isPlantFullyGrown) continue;

// And if we finally get to this point,
// then we increase how many ticks this
// plant has been growing for.
plant.currentGrowthInTicks++;
}
}
}

[/expand]

Next let's handle the PlanterBoxSimSystem. For this there are two things we need to take care of. We need to gather a mapping of item templates to plants so that we know what plant to spawn when we place an item into the logistic container and then we have to do the fun bit which is actually spawning a plant once we've found a valid seed in store:

[expand]

// A system which handles the spawning of plants
// when the appropriate item has been placed in
// storage.

[AddSystemToGameSimulation]
public class PlanterBoxSimSystem : SystemManager.System
{
// onAddedToManager() is called once when the system is first created
// and added to the SystemManager
public override void onAddedToManager()
{
// On startup we create a map of ItemTemplates->Plant Prototypes
createItemTemplateToPlantPrototypeMap();
}

// create the ItemTemplate->Plant Prototype Map
void createItemTemplateToPlantPrototypeMap()
{
// Iterate all plant prototypes.
//
// A prototype is similar to a Unity GameObject.
// It has a list of components and you can use it
// to spawn entities in the simulation.
foreach (var plantPrototype in Mods.Prototypes.getAllPrototypesWithComponent().Values)
{
// get the Plant component
var plant = plantPrototype.getComponent();

// get the associated ItemTemplate
var itemTemplate = AssetManager.getAsset(plant.itemTemplateId);

// Map the item template to the specified plant prototype
plantPrototypes[itemTemplate] = plantPrototype;
}
}

// Update the simulation
//
// [LockstepTickRate(60)] tells the system manager to only update this system
// every 60 ticks.
[LockstepTickRate(60)]
public override void pstLockstep_tickUpdate(ulong tickId, DesyncHasher hasher)
{
// Iterate all PlanterBox components
foreach(var kvp in Mods.Components.getAll())
{
// Get the planter box component
var planterBox = kvp.Value;

// If the planter box already has a valid plant, then
// skip the rest of the update
if (EntityManager.isValid(planterBox.plantId)) continue;

// Get the planter box id
var planterBoxId = kvp.Key;

// Get a handle to the planter box inventory.
//
// Handles are used to interface with the native Foundry
// entities that exist in the native C++ plugin.
var planterBoxInventory = new StorageEntityHandle(planterBoxId).inventory;

// Iterate all items in the planter box inventory
foreach (var item in planterBoxInventory.items)
{
// If the item is not a plant, then skip it
if (!plantPrototypes.TryGetValue(item.template, out var plantPrototype)) continue;

// Remove the item from the inventory
planterBoxInventory.tryRemoveItem(item.template.id);

// Spawn a new plant from it's prototype
var plantId = Mods.Entities.spawn(plantPrototype.id);

// Have the planter box keep track of it's newly spawned plant
planterBox.plantId = plantId;

// Trigger an event that will be handled by the PlanterBoxRenderSystem
// or any other mods who want to know when a plant has been planted.
trigger(new OnEntityPlanted
{
plantId = plantId,
planterBoxId = planterBoxId
});

// If we've found a valid plant then we
// can exit out of this loop
break;
}
}
}

Dictionary plantPrototypes = new Dictionary();
}

[/expand]

Finally there's one last thing we're going to do which is to make a PlanterBoxRenderSystem which will spawn the plants once they get planted. For this we need to handle when the planter box is streamed in/out and when a new plant is planted. We're also going to scale up the plants as they grow.

I know this has already been quite technical up till this point and though I can't promise you that this is going to be any less technical, I can promise you that this is the last piece of code.

Promise.

[expand]

// A system which handles the rendering of plants when they're planted in a planter box

// [AddSystemToGameClient] automatically adds the system to the game except when
// the game is running in dedicated server mode.

[AddSystemToGameClient]
public class PlanterBoxRenderSystem : SystemManager.System
{
// Handle when the planter box GameObject is streamed in
[EventHandler]
public void handle(OnEntityStreamedIn evt)
{
// Keep track of the newly streamed in GameObject
streamedInPlanterBoxes[evt.entityId] = evt.cmp;

// Check to see if there's a plant we should be
// visualizing
trySpawnPlantVisualizer(evt.entityId);
}

// Handle when the planter box gets streamed out
[EventHandler]
public void handle(OnEntityStreamedOut evt)
{
// Remove the streamed out planter box.
streamedInPlanterBoxes.Remove(evt.entityId);

// Get the planter box component
var planterBox = Mods.Components.get(evt.entityId);

// Check to see if there's a plant GameObject that needs to be destroyed
if(plantVisualizers.TryGetValue(planterBox.plantId, out var plantVisualizer))
{
// If there is, then destroy it
GameObject.Destroy(plantVisualizer.gameObject);

// And remove the reference we had to it
plantVisualizers.Remove(evt.entityId);
}
}


// Handle the case where we've planted a new plant
[EventHandler]
public void handle(OnEntityPlanted evt)
{
// Spawn the plant visualizer if necessary
trySpawnPlantVisualizer(evt.planterBoxId);
}

// This handles spawning the plant GameObject.
// It will only be spawned if there's a valid plant
// and if the planter box is streamed in.
void trySpawnPlantVisualizer(ulong planterBoxId)
{
// If the planter box isn't streamed in, we can just skip the rest of this function
if (!streamedInPlanterBoxes.TryGetValue(planterBoxId, out var planterBoxGO)) return;

// Get the planter box component
var planterBox = Mods.Components.get(planterBoxId);

// If it does not have a plant then just skip the rest.
if (!EntityManager.isValid(planterBox.plantId)) return;

// Get the plant prefab to instantiate
var plantPrefab = Mods.Entities.getEntityPrototype(planterBox.plantId).moddedGameObject;

// Instantiate the prefab
var plantGO = GameObject.Instantiate(plantPrefab);

// Place the prefab at the plantLocator position of the planter box
plantGO.transform.position = planterBoxGO.plantLocator.transform.position;

// Keep track of our newly spawned plant.
plantVisualizers.Add(planterBox.plantId, plantGO.gameObject);
}

// Handle the updating of the plant scales
//
// [RenderTickRate(1.0f)] means that this function will only be updated once
// a second

[RenderTickRate(1.0f)]
public override void LateUpdate()
{
// Iterate all plant visualizers
foreach(var kvp in plantVisualizers)
{
// Get the plant id
var plantId = kvp.Key;

// Get the plant component
var plant = Mods.Components.get(plantId);

// Calculate the new plant scale
float plantScale = 0.1f + 0.9f * (float)plant.currentGrowthInTicks / (float)plant.totalGrowthTimeInTicks;

// Adjust the plant's scale
kvp.Value.transform.localScale = new Vector3(plantScale, plantScale, plantScale);
}
}

// PlanterBox id to GameObject
Dictionary streamedInPlanterBoxes = new Dictionary();

// Plant id to GameObject
Dictionary plantVisualizers = new Dictionary();
}


[/expand]

And that's it! That's basically all the code necessary to have a plant, place it in a planter box and have it grow over time, all done inside of a mod and as a thank you for sticking around this far, here's a look at the PlanterBox mod working along side a Sprinkler mod that was made from one of the in game Pumps:

[previewyoutube][/previewyoutube]

Future Work

This is was just one of the ways that we're looking at making Foundry moddable. We're also working on Harmony support for those of you who want to hook into any C# functions we have. And for those of you who who are just looking to add/tweak/create crafting recipes/research/buildings, we're working on making it so you can do that by modifying data files, no programming necessary!

That's all I have for this Foundry Friday. Once again...

Thanks for listening!





Dev Blog #29 | Foundry Fridays: Underground Mining Rework

Hello everyone,

Today it’s my turn to write our FOUNDRY Friday blog post and I will cover the underground mining rework in detail. Our previous posts were more on the technical side, so this one will be about gameplay and presenting what’s coming once we release the game in Early Access on Steam.

[h3]Why the rework?[/h3]
Before I cover the new system I want to talk about the old version of underground mining and why I wanted this change and what I felt needs improvement. Previously we had what we called world layers, different voxel blocks depending on the depth. Regarding depth, they had a bit of a noise-based variation but roughly they were locked in place and filled the world along all cardinal directions. So wherever you dug down, you found the same voxel types (ores) at the same depths.


Some of those voxel layers could be mined with a rail mining depot, a machine that horizontally digs endlessly in one direction, excavating everything in its path. So the workflow for mining ore was to dig down at the right depth level and place as many rail miners as you needed. As they were digging forever, every railminer was an infinite source of ore.


Here are the key problems I identified:
  • Infinite ore without cost: Finite ore sources always felt wrong to me as it means you need to dismantle everything once the ore is gone, however having them be infinite isn’t right either, it is too simplistic.
  • No exploration for ore: All you had to do was remember the according depth level per ore and then dig down with an elevator. Ore was always guaranteed to be there. In a world where you can dig and explore, this feels like wasted potential.
  • Unwieldy system: The overall system felt cumbersome, we had different rail miner items to assign per depot, they needed a constant supply of new tracks, once they dug far enough they might block large areas of the world and many more not so ideal features. I just felt that this isn’t as polished as it could be.

[h3]The new Underground Mining[/h3]
In the new system we have ore veins randomly generated throughout the world. An ore vein is a large vertical shape made out of indestructible blocks. They range from the surface till far down into the ground.


To find such an ore vein, you can use our new ore scanning tool. Using the tool will start a scan ping which expands through the world, notifying you about all ore veins in the vicinity. This tool is also able to track regular ore patches for your drone miners. This part is still work in progress but we plan to let you configure what you’re looking for, at the moment it scans for everything near you.



Once you have located an ore vein, you need to dig to the mineable parts, they are also highlighted by the scanner. The amount of mineable parts is randomized, so some ore veins have more yield while others have less.
Additionally we introduced a new concept, called ”Mining hardness”. At the start of the game you can only dig down until you hit a certain rock layer which is too hard to mine. By progressing through research you will be able to dig down further, allowing you to access more mineable parts per ore vein.

Once you excavated a mineable part, you can place a machine very similar to our original rail mining depot. It will expand rails and start digging into the ore vein. On its way into the vein all the blocks will yield a large amount of ore. For me it was important to keep the digging process, and let the machine drill into the vein, showing some visual voxel changes and progress.

[previewyoutube][/previewyoutube]

After a certain distance it will hit what we call the ore vein core. Those blocks are indestructible again and don’t yield ore the same way as the previous ones, however your mining depots can continue mining as long as an extra item, a so-called mining fluid, is supplied. While in reality almost exclusively used for shale gas extraction, we are trying to emulate a process called “fracking”, extraction of resources by cracking up rock formation by pressure. The mining fluid isn’t supplied by the mining depot itself, but by a large unique (per vein) building that can be placed on top of the ore vein, the Fracking Tower. The Fracking Tower is a modular building and making it larger allows it to support a larger amount of mining depots.


Some ore veins are under water, causing the fracking tower to look like an oil rig.


Given its size and potential difficulty to place it on rough terrain we tried to make sure to take some actions making it easier. Most notably the struts are automatically built once the building is placed down, but I’m skipping the details for this post.


[h3]Conclusion[/h3]
Let’s have a look at the identified issues before and how they are solved:
  • Infinite ore without cost: Now you get the best of both worlds, they are finite at first and easy to use, and with some additional effort (building the Fracking Tower and supplying it with mining fluid) you can convert it into an infinite source of ore.
  • No exploration for ore: You need to explore the world and use your scanner to find ore veins, once you locate a vein the scanner helps you find the mineable parts. This requires a bit of exploration and digging up the right spots, but it’s straight-forward enough to not be frustrating. It requires more digging than before, to compensate we made sure that explosives are available before accessing the first ore vein.
  • Unwieldy system: The rail mining depot has been greatly simplified, gone are the rail miner items and the need to supply tracks. You need to supply a mining fluid once you hit the core, but instead of requiring to lay pipes far into the underground we made this more accessible with the Fracking Tower on top. We made sure all info texts make the system easy to understand and debug in case of issues, also we disallow invalid placement of any of the related buildings.

I’m sure it will need a little more polishing but looking at the points above I’m very confident that the new system is an improvement and I can’t wait to hear your thoughts and in-game-feedback once it’s on the experimental branch.

That’s it for today's FOUNDRY Friday post. See you in two weeks!
-mrmcduck

Dev Blog #28 | Foundry Fridays: Light at the End of the Tunnel

Light at the End of the Tunnel


Hey Founders, My name is Mark and I'm thrilled to continue our series of ‘Foundry Fridays’ developer diaries, where we - the team behind Foundry - get to pull back the curtain and give you a glimpse into our process and progress.

Each entry is a deep dive into a specific aspect of the game, ranging from art, design, user experience, and more. Today I’ll be speaking on my role as a shader specialist on the Foundry team, so you can expect today’s entry to be more on the technical side. If you enjoy these more technically focused posts, please let us know on our Discord channel, and we'll to create more in the future.

In this week's edition, we're shedding some “light” on an intricate aspect of our game - the rendering of our volumetric lighting. Volumetric ambient lighting creates realistically dark caves and crevices in Foundry, but we've hit a bit of a snag. An annoying blue fog shows up when our tunnels get too large. In order to explain how we tackled the blue fog issue, we need to give you a little backstory on how our game's lighting works.

Get ready to step out of the fog and into the light!



Where Does Light Come From?

A game like Foundry is rendered with something called “deferred” lighting. In a deferred rendering project all geometry is written to a series of graphics buffers (GBuffers) that contain information like, albedo (color), specular, glossyness and normal. After all objects are drawn we run a single pass over the buffers to calculate the direct lighting information for each pixel. This creates nice highlights and directional lighting information that we are used to in modern games. But it doesn’t accurately simulate how lighting works; for that we need something called “bounce” (or ambient) lighting.



Volumetric Bounce Lighting

When a light ray hits a surface it reflects and scatters around the area. This bounce is what fills a real room with light when you turn on a simple lamp. The deferred step I mentioned before does not handle any of these “bounces” and results in areas not directly lit by lights being pitch black. If you wanted to render all these bounces you would be doing something called ray tracing- a feature that is prohibitively costly performance-wise.
Normally games fill in a best guess for the bounce lighting with an ambient term, or even by baking the bounces into something called a lightmap texture. But Foundry is a fully dynamic world so this just won’t work. We instead create our own approximation of a volumetric ambient lightmap that updates in real time as the world changes. This structure is computed on the CPU and then the volumetric texture is shared with the GPU. We can simply use this value per-voxel to mask an ambient term to ensure caves are properly darkened while keeping our outdoor scenes nice and bright.

Fixing the Blue

If you went deep into our itch.io demo you would know that large caves had a sky colored fog in the distance. This is because, although the lighting system knew about our volumetric ambient data- the fog did not!


To solve this, when we compute the fog value at a given location we mask it by the volumetric lighting. This helps keep the darkness “dark”. But when you make a truly MASSIVE cave it still breaks because cameras have something called a “clip distance”, a distance away from the camera where no more content can be rendered. At this distance we no longer have any cave walls to sample the ambient lightmap at- just a big void. The solution here was to take the ray projected by the camera to that pixel and “clamp” its length by the max draw distance and sample the lightmap at that location. Then if this value is fully bright, we draw the regular sky/clouds/fog, and if it’s fully dark we render black… and voila! We have massive (dark) caves:


Obviously this system is more complicated and has many more parts than what we spoke about here- but this was a particular improvement that was always on my list to fix, and I think it improves the experience for those players hoping to spend their time in an underground voxel factory.

Until next time Founders.
-Mark