The gnuplotiverse

I will say it outright, I have a fascination with space video games. Space ships, space lasers, space anything. If you took the most mundane game concept on earth, and added the word SPACE to it, I would probably start salivating.

So, let's hit up on a subject that I am interested, especially as an indie game developer: procedural data sets.

I mean, let's be honest, if you want to make a huge system of some sort, you do not have the time to hand-craft the data set required to fill it to the brim.

Some examples of procedural produced data sets:

Diablo I Minecraft Elite Eve-Online Borderlands Left 4 Dead Dwarf Fortress

And so many other games, if I were to continue, this post would probably become a list with a little flavor text.

While there's probably plenty of guides on how to procedurally generate a universe, I'd like to present my attempt at universe generation.

Gnuplotiverse Click to enlarge

Because my work flow is entirely Linux-driven, you may have issues running this project without the proper dependencies. When it comes to procedurally generated data, the most important thing happens to be the data that's generated. Regardless, I find the process to generate the information more important than the code itself, because this will allow you to create your own universes.

You can find the code for this blog post here.

The steps that are taken to produce this data set are:

  1. Universe Distribution Map
  2. Universe Generation Library
  3. Data Output in CSV
  4. Gnuplot Script
  5. Gnuplot
  6. Universe Plot PNG

Universe Distribution Map

Most successful games that I know of create universes that are flat. The simple examples are Elite, and Eve Online. With that, the important part of producing this data is that we start on in a 2D environment.

With that we create a basic statistical map on what we would like the universe to look like. In this case, I took the Milky Way, and removed the color.

The Milky Way Click to enlarge

From this we can statistically decide the probability of a randomly placed star occurring. Black (#000000) would have a very low chance and white (#FFFFFF) would have a very high chance.

Universe Generation Library

The Universe Generation Library (universegen.lua) is where the "data"-magic happens. The most important part of the code is the function universegen.attemptStep.

This library basically takes a number of steps that should be computed, and runs until the steps are taken, where each step is the possible placement of a "planet".

The first thing we need to do, to take a step, is to choose a possible location for a new planet. Instead of choosing the pixels in order, I chose one randomly to help increase the amount of noise and variance of each dataset. This line basically chooses a pixel from the distribution map.

local px,py = math.random(1,self.size-1),math.random(1,self.size-1)

Then, I set the z coordinate so that there is some vertical noise. I use math.sin here to ensure that the vertical part of the dataset is more dense in the middle, and less dense near the z-limits (defined by self.size * self.zMult)

local pz = math.floor(
  math.sin(math.random()) * math.random(-self.size*self.zMult,self.size*self.zMult)
)

I then store the actual pixel data from the distribution map and the location of the center of the "universe" so I can use this later.

local r,g,b = self.map:getPixel(px,py)
local rx,ry,rz = px-self.size/2,py-self.size/2,pz

I then calculate the magnitude of the current pixel from the center of the distribution map. I do this so I can make the center of the universe more dense than the edges.

local mag = math.sqrt( rx^2 + ry^2 + rz^2) / self.size

I then multiply the magnitude against the base chance of a star being successfully added. This is important, otherwise stars would never appear outside of the designated zones of the Universe Distribution Map. This extra noise helps it feel a little more realistic.

local calc_bchance = self.bchance * mag

I only care about the darkness of the pixel, so I take an average to create the map chance of how likely the current planet will be successful. This example is black and white, but with this, RGB color images can be used as well.

local map_chance = ((r+g+b)/3)/255

And now the actual chance is computed by using the base chance and the pixel map chance.

local chance = calc_bchance + map_chance*(1-calc_bchance)

Finally, I push the computed chance through math.cos to give the entire system a more rounded look. Then if it passes the threshold, it is added to the system.

if 1-math.cos(chance) > math.random() then
  return self:addPoint(rx,ry,rz)
end

Data Output in CSV

The generated data is then parsed into data.csv as the following so it can later be used in gnuplot to see what we have.

x,y,z

This step is rather straightforward I hope.

Gnuplot Script

I then have a gnuplot script, uni.plot, to render the data.

Here I identify how the data.csv dataset works

set datafile separator "," 

I set the 3D view so I can relate to the data when testing.

set view 30,60

I then plot the data from the CSV with specific settings that will output it to uniplot.png.

splot 'data.csv' with points palette pt 8
set term pngcairo mono enhanced
set xlabel "X"
set ylabel "Y"
set zlabel "Z"
set terminal png size 1680,1050
set mouse
set xrange [-512:512]
set yrange [-512:512]
set zrange [-512:512]
set out 'uniplot.png'

Then finally plot the data that you saw earlier.

replot

Gnuplot

Running the previous script is as easy as gnuplot uni.plot

Universe Plot PNG

Then finally comes the output.

Gnuplotiverse Click to enlarge

A fun part is that with high contrast images, such as a png of beartato, I can create different kinds of universes.

Beartato Click to enlage

I hope that this example encourages folks out there to create awesome universes for their own space games! Thanks for your time!

About Seppi

Howdy; my name's Seppi, and I'm an indie game developer. I am an active member of the LÖVE community. I've been making games for years now, and I'm always interested in helping prospective indie developers out.

One of the paradoxes I've learned over the years, especially as a software developer, is the more I know, the more I realize I don't know. I quote;

Only a fool would take anything posted here as fact.

Questions, comments or insults? Feel free to leave in the comments section, or contact me; you can hit me up on twitter @josefnpat