Displaying Items as Liquids in Containers Is Not a Boring Topic

Victor told me that talking about liquid levels was boring.

Well, Victor was wrong.


Items are things like ore, water, metal, biomass, and fuel.

Items can be stored on objects, like shelves.

Items can be converted into other items, like when the ore refinery pulverizes ore and creates metal.

Items can be carried and transported from one object to another (taking ore from a shelf to a refinery)

Displaying Items

Most items are displayed as crates. Crates are what colonists carry when they transport items. They’re also how items are typically displayed in the world.

Here’s a really up-scaled example of a few crates. From left to right it’s: fuel, ore, biomass, potato, and fish.


These crates are then placed and positioned appropriately on each object.

Here’s a shelf with a colorful assortment of crates:

shelf smaller.png

How It Works

I don’t want to describe how we position crates in great detail, but the quick version is something like:

  1. Item is deposited in object

  2. Item is visually merged with existing crates

  3. If required, a new crate is created

  4. New crate is positioned correctly

It’s a bit more complicated in practice. Most objects have different areas that they display crates. For example, an Ore Refinery displays ore in an “input area” and it displays metal in an “output area”. So there’s an extra step when we want to figure out where to position a new crate.

In this screenshot you can see that the ore crate is to the left (on the input belt), and the metal crate is to the right (on the output slot)


Internally, we have a class named “CrateDisplayTransform”, it’s used for Displaying Crates on Transforms (transforms are what Unity calls the component that handles positioning, scale, and rotation). We can specify the local scale of each crate, the item types to use, and a constant position offset, if we need to.

We can also set the priority of where to display each item. This is used when a crate can be displayed in multiple locations. The shelf uses this so that items are always placed on the bottom before the top (the bottom has a higher priority).

Crates also have a maximum amount of items that they can store. This allows us to consolidate small amounts of the same item type together into a single crate. Right now most crates can hold 100 items. Some crates, like food dishes and body bags, can only hold a single item.

But What About Fluids!

Ok, the crate display system works fine for most objects, but some objects are special. They can visually store items, but not as crates.

The two most obvious examples are the Water Recycler and Water Storage Tank.

Special little snowflakes

Special little snowflakes

The Water Recyler converts Dirty Water (from toilets, crop irrigation, whatever) into tasty Clean Water. Clean water is conveniently stored in Water Storage Tanks until it’s ready for use.

Ok, so we want to display an item, but not as a crate. The basics are almost the same:

  1. Item is deposited into object

  2. Item is visually merged

  3. If required, a new crate is created

  4. New crate is positioned correctly

Only this time, we’re not using a crate, so we can skip steps 2 and 3. We still need to position something, though. The new sequence of events might be:

  1. Water is deposited into object

  2. Water level is positioned correctly

Water Levels

So that’s simple enough. We just need to position a water line based on how much water is in a container.

More abstractly, we need to position an object based on how much item is in a container.

Here’s the code that implements it:

// Assume that these are defined somewhere
float minPosition = 2;
float maxPosition = 13;
float maxItemsToDisplay = 100;

void OnItemDeposited(Item item)
    if(item.itemType != itemTypeThatWeCareAbout)
    float ratio = item.amount / maxItemsToDisplay;
    float newPosition = Mathf.Lerp(minPosition, maxPosition, ratio);

It’s a bit more complicated, because we allow for multiple item types to be displayed together, so we have to get a sum of all the counts. We also change the liquid level slowly instead of instantly.

But the above code is basically how it works.


You may not know what Lerp is.

Lerp (Linearly intERPolate) is a function that takes three arguments:

  1. Starting Value

  2. Ending Value

  3. Percentage

Lerp returns a value that is somewhere between starting and ending, based on the percentage that you pass. If you pass a percentage of 1.0 (100%) it will return the ending value. If you pass a percentage of .5 it will return a value that is halfway between starting value and ending value.

Lerping is useful for when you have a min and max value and you want to return a value that is somewhere between the two.

In our case, we had a liquid position when empty and a liquid position when the container was completely full.

Visual Demonstration

Here’s a gif of it in action. We use a separate GameObject for the liquid level:

the best gif youll ever see.gif

(You can see that a teeny little bit of the clean water level object is peeking out of the Clean Water Tank, but it’s been fixed since this gif was recorded)

Other Uses

The “LiquidLevelComponent” is flexible enough that we can use it for all sorts of things.

The Nutrition Brick Creator has a very similar liquid level, only it cares about Biomass instead of water.

It might not seem all that interesting, but we’re able to use the exact same components on 2 almost unrelated objects, and nothing breaks:

(You can’t tell, but behind the scenes I’m adding biomass to both of the nutrition brick creators)

biomass is people.gif

Behind the Scenes

It might look like I’m faking these gifs or something. I’m not. I just placed all of the objects on top of floors with no other floors present, so that you get to see a cool space background. And so that you don’t have to be distracted with other objects in the images.

Then, I force added the appropriate item to the objects.

Here’s a gif of me making the gifs

such wow.gif

Very Interesting

So that’s liquid levels in containers.

Victor said it couldn’t be interesting. Victor didn’t believe in me.

I know all of you found this interesting, so go to the Discord and say “Thanks Victor”.

- Tyler