Friday Facts #150 - New Terrain Experiments

Posted by Tomas on 2016-08-05

Hello all,

0.14 dreams

The 0.13 stabilisation process has been going on much smoother than expected. This was due to the focused bug fixing efforts of majority of the dev team. It seems realistic that we could have a stable 0.13 version within a week or two. Naturally this brings the question what will be in the box for 0.14. We have had a chat about this with kovarex and came up with a specific (yet very rough or high-level) road plan for 0.14 (and subsequently 0.15). However this will be the content of the next FFF where kovarex will present you a more detailed list of things that you might (most probably - as always) expect.

Apart from bugfixing not much has been happening lately. Rseding is getting ready for his way back home to the US (2 months flew by really fast). Kovarex is still deep down in Multiplayer rewrite. I have been refactoring our webpage from Clojure to Python (to make it more accessible and consistent with the rest of our web applications). In general there is a summer chill atmosphere in the office.

One of the bigger fixes in recent 0.13 releases has been the main loop. Robert (aka Twinsen) writes more about it:

Main Loop Again

Something that happened in 0.13.10 is that the game's main loop was partially rewritten. I'll explain a bit how our game loop works.

For those who don't know, a Game Loop, or Main Loop is a small part of the game's code that is responsible for telling the game when to process the user input, update the game state and render. It tracks the passage of time to control the rate of gameplay. In its simplest theoretical form it looks like this:

while (!exit)

And as usual, things in Factorio are more complicated. The way we do game updates and rendering is a bit different than most games. They way our previous game loop worked was explained briefly in FFF-70. Our main loop code is about 800 lines, but in it's simplest form the new and old game loops look something like this:

while (!exit)
  if (didGameUpdate)
    prepare(); //go though all the entities and collect draw orders
  start update thread(); //update all the entities: update the game state for 0, 1 or more ticks so we get 60 UPS
  if (didGameUpdate)
  wait for update thread to finish();

The nice thing about Factorio is that we can do the rendering in parallel to the game updates. The only thing that needs to be synchronized is what we call the prepare step, that goes though entities and asks what sprites need to be drawn (and where). Once that data is collected, the render step deals with the actual textures and tells the GPU what to do.

Another thing that is very different than most games and some people find it hard to understand is that there is at the moment no point in rendering more frames than game updates. This is because our animations and entity movements are part of the game state (for many reasons, but mostly for simplification). So if you call prepare() and render() two times in a row, you and up drawing two absolutely identical frames.

I won't go into deep technical details (that would mean talking about barriers, thread conditions, vsyncs, and many boring small problems), but our old game loop had a couple of problems:

  • it was trying to be too smart and solve too many different edge cases (what do we do when rendering takes too much time, what do we do when we have have update time spikes, how do we synchronise game updates with vsync, etc)
  • it did not work well with monitors that refresh faster than 60hz (144hz monitors) so some tweaks and hacks were put in place to make it sort of work
  • some of its timings were based on the precision of thread sleep
  • the update rates did not average out at a precise 60UPS even on small maps, meaning that in multiplayer games some players would run the game slightly faster than others
  • it was tweaked and changed over the years to fix various things to the point where no one could really understand what was happening anymore.

Although it still worked fine in most cases, I decided to rewrite it and address the issues above. And on top of that see if I can maybe squeeze more UPS(game updates per second). I took inspiration from some articles and also from Unity's game loop. In the end the simplified main loop made its way into 0.13.10.

The result is:

  • performance wise almost identical solution (or better - in some special cases we now get 20% more UPS)
  • above mentioned problems were solved
  • it leaves possibility to free the FPS and UPS, so we can have for example 144 FPS/60 UPS. For example we briefly talked about the possibility of interpolating the position of the player sprite and camera, so very high framerates could make sense sometime in the future.

To eat CPU or not to eat CPU

Because of the way it was written initially, the loop would eat up 100% of one CPU core. While it was quickly fixed for the headless server, our office was divided into two camps that would argue whether the main game should use as much CPU as possible to squeeze the best performance possible or sleep when possible to conserve CPU. You can read some more about this problematics. In 0.13.14 there will be a solution which uses much less CPU while sacrificing very little UPS.

New Terrain Experiments

At the moment, our art director (Albert) is back home in Spain, however the work still continues with him communicating with the rest of the team. One of the topics in the gfx world nowadays are new terrains. Jurek has been experimenting with these for some time. Basically we are looking into adding 2 new biomes each with 3 variations.

The biomes would be the tundra/taiga inspired environment with 3 different levels of "snowiness". And also the "red desert" with 3 different levels of "rockiness". The red desert can be considered a hybrid between the Australian outback and Martian landscapes.

Work on the biome contains working simultaneously on multiple elements, which need to work together giving a good impression:

  • tiles
  • doodads (plants, rocks)
  • tile patches (decals, holes, rigs, etc.) - these are technically doodads too
  • even specific trees for the terrain

The biggest challenge is to make the terrain look smooth and "authentic" in different zoom levels. A certain solution might look good when zoomed in, but it reveals repeating patterns when zoomed out. Creation of the terrain is kind of a permanent search for the best compromise among different zoom levels. Of course in the final "product" it all needs to come together (probabilities of tiles with different sizes, doodads and tile patches distribution, sizes of biomes, etc.) to give the best feeling. This requires cooperation with dev team as well.

Here you can see proposals for the tile patches in the red desert terrain:

And here you can see some proposed doodads for the same terrain:

The commenting thread is ready at our forums as usual.