Rendering an 8x8 font on the map in Factorio

One task I had recently was to render a text message onto an in-game map in Factorio. Factorio is a video game played on a two dimensional grid. Each grid position has a tile that is the ground and possibly an entity in it. What I wanted to do was to use entities to spell out a message on the the map. This should be simple, since a 2 dimensional grid is basically a bitmap. If I can find a font that has each character stored as a bitmap, I should be able to manipulate the in-game map to show each character. Almost any aspect of the game can be manipulated by writing lua code and running it in game, this is how mods for the game are created.

I started searching around for "8x8 font" and "bitmap font". This returns a huge number of results, some freely available, some paid. In all cases what I found was that these are TrueType fonts designed to emulate the appearance of the fonts of older, retro computing systems but on a modern computer system. This isn't helpful as each character in the font is not stored exclusively as a bitmap, but instead as a series of curves. I can't easily convert that to something that can be rendered in game.

I did more searching, eventually finding a font meant for use in microcontrollers. It is common to have a bitmap style LCD attached to a microcontroller that is used to show some type of user interface. Since a microcontroller has no rendering hardware of any kind, the application code is responsible for displaying any messages. In that application a simple font is needed that can be rendered quickly. I found the font8x8 project on GitHub by Daniel Hepper. This is a complete 8x8 font with each character stored as a bitmap in the C programming language. The font is defined a two dimensional array of type char.

char font8x8_basic[128][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0000 (nul)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0001
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0002
    /** many more entries here **/
    { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00},   // U+0041 (A)

Original GitHub project here

In the example above the character "A" is stored as 8 values of type char. Each char is 8 bits. This means there is a total of 64 bits. Since this font is supposed to be an 8x8 font, I was able to guess that the 64 bits make up the 64 pixels of the character as as bitmap. This works great in the C programming language. But in order to use this in Factorio, I needed to port it to Lua.

The font8x8 project includes an example render.c that shows correct usage of the 64 bits for each character.

void render(char *bitmap) {
    int x,y;
    int set;
    int mask;
    for (x=0; x < 8; x++) {
        for (y=0; y < 8; y++) {
            set = bitmap[x] & 1 << y;
            printf("%c", set ? 'X' : ' ');
        }
        printf("\n");
    }
}

Rendering any single character is performed by iterating over the 64 bits & checking each one. If the bit is set to 1 then the pixel is turned on, otherwise nothing is done. The inner loop in this code iterates with y from 0 to 7. Each iteration it checks a successive bit in the char. Since this code is rendering the character as ASCII Art I determined that each array of 8 char represents horizontal strips of the character starting at the top and then working its way down to the bottom. This should be easy to work with, since the map grid in Factorio has a coordinate system that starts in the top-left and increases as you can go down and to the right.

Using this code as an example, I was able to write a C program that constructs a Lua array for each character

#include <stdio.h>
#include "font8x8_basic.h"

int main(int argc, char **argv) {
  printf("local %s = {\n", "font");
  int i;
  int x;
  int y;
  int v;
  int all_zero;
  char const * msg;  
  for(i = 0; i != 128; i++) {
    all_zero = 1;
    for (x = 0; x != 8; x++) {
      if (font8x8_basic[i][x] != 0) {
        all_zero = 0;      
        break;
      }
    }
    if (all_zero == 1) { 
      continue;
    }
    printf("[%d]='",i);    
    for(x = 0; x != 8; x++){
      for( y = 0; y != 8; y++){
        v = font8x8_basic[i][x] & 1 << y;
        if (v != 0) {
          msg = "X";
        } else {
          msg = " ";
        }
        printf("%s",msg);
      }
    }
    if (i == 127) {
      printf("'\n");
    } else {
      printf("',\n");
    }
  }
  printf("};\n");
  return 0;
}

This writes to standard out a Lua array statement. Since Lua supports sparse arrays, I was able to write out entries only for the printable characters in the ASCII character set. Each character is written out as a string of 64 characters. If the pixel at the position should be turned on an X is stored in the string. If the pixel should be off a space is stored in the string.

The output of that program looks like this

local font = {
[33]='   XX     XXXX    XXXX     XX      XX              XX           ',
[34]=' XX XX   XX XX                                                  ',
[35]=' XX XX   XX XX  XXXXXXX  XX XX  XXXXXXX  XX XX   XX XX          ',
[36]='  XX     XXXXX  XX       XXXX       XX  XXXXX     XX            ',
[37]='        XX   XX XX  XX     XX     XX     XX  XX XX   XX         ',

Now that I had a Lua representation of a complete 8x8 font I needed to render it onto the map. I do not commonly write Lua code, but I was able to use the string.byte built-in function to convert each character of a string into it's numerical ASCII value. If the font array has an entry at that numerical position then it should be printed to the map. Each character is "printed" by placing selecting an entity from a listed of available entities and then placing it on the map where the pixel would be. Before any characiter is "printed", I destroy all the entities in the area. This gives a clear area to write the message onto.

This is the Lua code I used

function render(msg, font, player, destX, destY, itemNames)
  local existing = player.surface.find_entities({{destX,destY}, {destX * (8*#msg),destY + 8}});
  for i = 1, #existing do
    local exists = existing[i];
    if exists.can_be_destroyed() then
      exists.destroy();
    end
  end

  local nameIndex = 1;
  for i = 1, #msg do
    local c = msg:sub(i,i);
    local ord = string.byte(c);
    local glyph = font[ord];
    if glyph ~= nil then
      for x=0, 8 do
        for y=0, 8 do
          local pixelIndex = x * 8 + y;
          local pixel = glyph:sub(pixelIndex,pixelIndex);          
          if pixel == 'X' then
            local placeX = destX + (i*8) + y;
            local placeY = destY + x;

            local chest = player.surface.create_entity{name=itemNames[nameIndex], position={x=placeX,y=placeY}, force=game.forces['neutral']}
            nameIndex = nameIndex + 1;
            if nameIndex > #itemNames then
              nameIndex = 1;
            end
          end          
          y = y + 1;
        end
        x = x + 1;
      end
    end
  end
end

My first attempt at this yielded this

Each character got rendered rotated, making it appear as if the character was laying on its side. This was easy enough to fix by just transposing the X & Y coordinates when placing items on the map. After that change I got this

Success! This was a simple project I used to print some messages on the in-game map. Hopefully someone else finds this informative and helpful. You can download the complete source code on GitHub.


Copyright Eric Urban 2022, or the respective entity where indicated