Drawing text through bge module - Drawing several words
Managing multiple text fragments

I suggest to place the text attributes into a container such such as a class:

class Text():
    def __init__(self, text, x, y, color, font_id):
        self.text = text
        self.x = x
        self.y = y
        self.color = color
        self.font_id = font_id


You can use a list or dict too, but I think this is easier to understand as you see the structure of the data.

Then place modify your code that it can work with the above text container:

blf.position(text.font_id, (width * text.x), (height * text.y), 0)
blf.size(text.font_id, 50, 72)
bgl.glColor4f(*text.color)
blf.draw(text.font_id, text.text)


This still allows just one text to be drawn (as there is just one text container). But now it is pretty easy for us to create a container that can contain any number of texts:

texts = []


You create that once in your drawtext.py replacing font_id, chain, positionx, positiony.

Make sure your write() can deal with more than one text container:

for text in texts:
    blf.position(text.font_id, (width * text.x), (height * text.y), 0)
    blf.size(text.font_id, 50, 72)
    bgl.glColor4f(*text.color)
    blf.draw(text.font_id, text.text)


Now you can add some comfort to your module, by providing a function that let you easily add new texts:

def add(text, x, y, color, font_id=0):
    texts.append(Text(text, x, y, color, font_id))


Your using script can look like that:

import drawtext
import bge

COLOR_GREEN = 0.0, 1.0, 0.0, 1.0

if all(sensor.positive for sensor in bge.logic.getCurrentController().sensors):
    drawtext.add("Hello Green World!", 0, 0.15, COLOR_GREEN)


The complete drawwtext.py can look like that:

import bge
import bgl, blf

texts = []           

class Text():
    def __init__(self, text, x, y, color, font_id):
        self.text = text
        self.x = x
        self.y = y
        self.color = color
        self.font_id = font_id

def add(text, x, y, color, font_id=0):
    return texts.append(Text(text, x, y, color, font_id))

def write():
    """write on screen"""
    width = bge.render.getWindowWidth()
    height = bge.render.getWindowHeight()
    # OpenGL setup
    bgl.glMatrixMode(bgl.GL_PROJECTION)
    bgl.glLoadIdentity()
    bgl.gluOrtho2D(0, width, 0, height)
    bgl.glMatrixMode(bgl.GL_MODELVIEW)
    bgl.glLoadIdentity()

    # BLF drawing routine
    for text in texts:
        blf.position(text.font_id, (width * text.x), (height * text.y), 0)
        blf.size(text.font_id, 50, 72)
        bgl.glColor4f(*text.color)
        blf.draw(text.font_id, text.text)

bge.logic.getCurrentScene().post_draw = [write]


Font Management

You might have noticed that I skipped fonts in the above code by making them optional. I recommend to manage them separately. The blf-module is the font container already. But you need a way to provide easy access to the loaded fonts without dealing with cryptic numbers. I suggest to create a new module fonts.py encapsulating the load operation:

import blf

def load(file):
    return blf.load(bge.logic.expandPath(file))


this enables you to declare font "constants" right at the beginning of drawtext.py:

FONT_ID_ARIAL = fonts.load("//arial.ttf")
FONT_ID_AGENCYR = fonts.load("//AGENCYR.TTF")


Which on the other hand enables you to use them when defining your text fragments:

import drawtext
import bge

COLOR_GREEN = 0.0, 1.0, 0.0, 1.0

if all(sensor.positive for sensor in bge.logic.getCurrentController().sensors):
    drawtext.add("Hello Green World!", 0, 0.15, COLOR_GREEN, drawtext.FONT_ID_AGENCYR)


The reason why load() is placed in a new module is simply to avoid defining this function before you define the constants, which let the constants be a the pretty top of drawtext.py

Color Management

You can do a similar method with colors. Right now each user script adds his own color. You can provide a few color constants right at the beginning of the module:

COLOR_GREEN = 0.0, 1.0, 0.0, 1.0
COLOR_RED = 1.0, 0.0, 0.0, 1.0
COLOR_BLUE = 0.0, 0.0, 1.0, 1.0
COLOR_BLACK = 0.0, 0.0, 0.0, 1.0
COLOR_WHITE = 1.0, 1.0, 1.0, 1.0


this allows to use them wherever this module is used:

import drawtext
import bge

if all(sensor.positive for sensor in bge.logic.getCurrentController().sensors):
    drawtext.add("Hello Green World!", 0, 0.15, drawtext.COLOR_GREEN, drawtext.FONT_ID_AGENCYR)


Complete Code

fonts.py

import bge
import blf

def load(file):
    return blf.load(bge.logic.expandPath(file))


drawtext.py

import bge
import bgl, blf
import fonts

FONT_ID_ARIAL = fonts.load("//arial.ttf")
FONT_ID_AGENCYR = fonts.load("//AGENCYR.TTF")

COLOR_GREEN = 0.0, 1.0, 0.0, 1.0
COLOR_RED = 1.0, 0.0, 0.0, 1.0
COLOR_BLUE = 0.0, 0.0, 1.0, 1.0
COLOR_BLACK = 0.0, 0.0, 0.0, 1.0
COLOR_WHITE = 1.0, 1.0, 1.0, 1.0

texts = []           

class Text():
    def __init__(self, text, x, y, color, font_id):
        self.text = text
        self.x = x
        self.y = y
        self.color = color
        self.font_id = font_id

def add(text, x, y, color, font_id=0):
    return texts.append(Text(text, x, y, color, font_id))

def write():
    """write on screen"""
    width = bge.render.getWindowWidth()
    height = bge.render.getWindowHeight()
    # OpenGL setup
    bgl.glMatrixMode(bgl.GL_PROJECTION)
    bgl.glLoadIdentity()
    bgl.gluOrtho2D(0, width, 0, height)
    bgl.glMatrixMode(bgl.GL_MODELVIEW)
    bgl.glLoadIdentity()

    # BLF drawing routine
    for text in texts:
        blf.position(text.font_id, (width * text.x), (height * text.y), 0)
        blf.size(text.font_id, 50, 72)
        bgl.glColor4f(*text.color)
        blf.draw(text.font_id, text.text)

bge.logic.getCurrentScene().post_draw = [write]


Show Green Text

import drawtext
import bge

if all(sensor.positive for sensor in bge.logic.getCurrentController().sensors):
    drawtext.add("Hello Green World!", 0, 0.15, drawtext.COLOR_GREEN, drawtext.FONT_ID_AGENCYR)


Show Red Text

import drawtext

    if all(sensor.positive for sensor in bge.logic.getCurrentController().sensors):
    drawtext.add("Hello Red World!",0,0, drawtext.COLOR_RED)


Excercise

I suggest to allow customizing the size of the text fragment. How would you do that?