Drawing Text
Loading a Typeface
Before any text appears on the screen you need a Font object. There are two common ways to obtain one:
- Load a
.ttfor.otffile that you ship with your game (recommended for portability). - Ask the operating system for an installed face via
SysFont.
import pygame as pg
my_font = pg.font.Font("assets/myfont.ttf", 24) # from file
fallback = pg.font.SysFont("Arial", 24) # from OS
Rasterising Strings
Turn a Python string into a Surface with Font.render:
label = my_font.render("Hello pygame-ce", True, (255, 255, 255), (30, 30, 30))
The second argument toggles anti-aliasing; the third is the foreground colour; the optional fourth is the background colour (transparent if omitted). The resulting surface can be blitted like any other image.
Thread Safety Warning
Each Font instance is not thread-safe. If you need to render from multiple threads, create one Font object per thread.
Decorative Styles
Bold, italic, underline and strikethrough can be toggled on the fly:
my_font.bold = True
my_font.italic = True
Automatic Word-Wrap (≥ 2.3.0)
Pass wraplength (in pixels) to render and optionally set font.align:
my_font.align = pg.FONT_CENTER
wrapped = my_font.render("A very long sentence …", True, (0, 0, 0), wraplength=200)
Writing Direction (≥ 2.1.4)
Use set_direction for RTL, TTB or BTT layouts:
my_font.set_direction(pg.DIRECTION_RTL)
rtl = my_font.render("مرحبا", True, (0, 0, 0))
Font Module Quick-Reference
Extended Typography with freetype
Import from pygame import freetype for kerning, rotatino, and sub-pixel positioning. The API mirrors font but adds attributes such as Font.rotation, Font.kerning, and Font.render_to for direct blitting.
Keyboard Handling
Polling Keystate
pg.key.get_pressed() returns a sequence indexed by key constants; a value of 1 means the key is currently held down.
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
player.move_left()
Modifier Keys
Query the state of Shift, Ctrl, Alt, etc. with pg.key.get_mods():
if pg.key.get_mods() & pg.KMOD_CTRL:
fire_rocket()
Repeat & Delay
Control how often KEYDOWN events are generated while a key is held:
pg.key.set_repeat(500, 50) # 500 ms delay, then 50 ms repeat
Text Input & IME
Enable the platform input method:
os.environ["SDL_IME_SHOW_UI"] = "1"
pg.key.start_text_input()
pg.key.set_text_input_rect(pg.Rect(cursor_x, cursor_y, 0, 0))
Listen for TEXTINPUT events to receive fully composed characters.
Mouse Support
Position & Buttons
x, y = pg.mouse.get_pos()
left, middle, right = pg.mouse.get_pressed()
Hiding & Custom Cursors
pg.mouse.set_visible(False) # hide
pg.mouse.set_cursor(pg.SYSTEM_CURSOR_HAND) # system hand
pg.mouse.set_cursor((16, 16), hotspot, *pg.cursors.compile(cursor_bits))
Relative Mode
For first-person cameras:
pg.mouse.set_relative_mode(True) # hides cursor & locks to window
Practical Example: Mini Text Editor
The following single-file program demonstrates everything discussed above: loading a font, handling keyboard input, displaying an IME candidate window, and wrapping long lines.
import os, pygame as pg
from pygame.locals import *
os.environ["SDL_IME_SHOW_UI"] = "1"
pg.init()
screen = pg.display.set_mode((400, 300), RESIZABLE)
font = pg.font.SysFont("consolas", 22)
clock = pg.time.Clock()
text_lines = [""]
cursor = (0, 0) # (line, col)
blink = True
pg.time.set_timer(pg.USEREVENT, 500)
pg.key.set_repeat(500, 30)
def wrap(txt, max_w):
"""Naïve word-wrapper."""
words, line, lines = txt.split(" "), "", []
for w in words:
if font.size(line + w)[0] > max_w:
lines.append(line)
line = w + " "
else:
line += w + " "
lines.append(line)
return lines or [""]
running = True
while running:
for ev in pg.event.get():
if ev.type == QUIT:
running = False
elif ev.type == pg.USEREVENT:
blink = not blink
elif ev.type == TEXTINPUT:
y, x = cursor
text_lines[y] = text_lines[y][:x] + ev.text + text_lines[y][x:]
cursor = (y, x + len(ev.text))
elif ev.type == KEYDOWN:
y, x = cursor
if ev.key == K_BACKSPACE and x > 0:
text_lines[y] = text_lines[y][:x-1] + text_lines[y][x:]
cursor = (y, x-1)
elif ev.key == K_DELETE and x < len(text_lines[y]):
text_lines[y] = text_lines[y][:x] + text_lines[y][x+1:]
elif ev.key == K_LEFT and x > 0:
cursor = (y, x-1)
elif ev.key == K_RIGHT and x < len(text_lines[y]):
cursor = (y, x+1)
elif ev.key == K_RETURN:
text_lines.insert(y+1, text_lines[y][x:])
text_lines[y] = text_lines[y][:x]
cursor = (y+1, 0)
screen.fill((25, 25, 25))
for i, line in enumerate(text_lines):
screen.blit(font.render(line, True, (220, 220, 220)), (10, 10 + i*26))
# draw blinking caret
if blink:
y, x = cursor
caret_x = 10 + font.size(text_lines[y][:x])[0]
caret_y = 10 + y*26
pg.draw.rect(screen, (255, 255, 255), (caret_x, caret_y, 2, 26))
pg.display.flip()
clock.tick(60)
pg.quit()