How make BitMapFont with script?

Godot Version

4.5.1.stable

Question

How make BitMapFont in Godot Engine 4.x? In 3.x version it was pretty straight forward:

  1. make new bitmapfont resource
  2. set bitmapfont height
  3. set bitmapfont texture
  4. loop over unicode charaster codes and add bunch of rects to bitmapfont texture coordinate.
  5. save bitmapfont

However, in Godot Engine 4, there a lot of methods:

set_texture_image()
set_cache_ascent()
set_cache_descent()
set_glyph_uv_rect()
set_glyph_texture_idx()
set_glyph_offset()
set_glyph_size()
set_glyph_advance()

Does any have example?

It’s pretty much the same but each glyph property is its own function now.

Dirty example loading a BMFont xml file:

extends Node


@onready var label: Label = $Label


func _ready() -> void:
	var font = FontFile.new()
	var font_image = preload("res://assets/bmfont/Unnamed.png").get_image()
	var font_size_vector = Vector2i.ZERO

	var xml = XMLParser.new()
	xml.open("res://assets/bmfont/Unnamed.xml")

	while xml.read() != ERR_FILE_EOF:

		if xml.get_node_name() == "info":
			for i in xml.get_attribute_count():
				var attr = xml.get_attribute_name(i)
				if attr == "size":
					font_size_vector.x = int(xml.get_attribute_value(i))

		if xml.get_node_name() == "char":
			var uvs = Rect2i()
			var advance: int
			var offset = Vector2i.ZERO
			var glyph_idx: int
			var page: int = 0
			for i in xml.get_attribute_count():
				var attr = xml.get_attribute_name(i)
				var value = int(xml.get_attribute_value(i))

				match attr:
					"id": glyph_idx = value
					"x": uvs.position.x = value
					"y": uvs.position.y = value
					"width": uvs.size.x = value
					"height": uvs.size.y = value
					"xoffset": offset.x = value
					"yoffset": offset.y = value
					"xadvance": advance = value
					"page": page = value

			font.set_glyph_advance(0, font_size_vector.x, glyph_idx, Vector2(advance, 0))
			font.set_glyph_offset(0, font_size_vector, glyph_idx, offset)
			font.set_glyph_size(0, font_size_vector, glyph_idx, uvs.size)
			font.set_glyph_texture_idx(0, font_size_vector, glyph_idx, page)
			font.set_glyph_uv_rect(0, font_size_vector, glyph_idx, uvs)

	font.set_texture_image(0, font_size_vector, 0, font_image)

	label.add_theme_font_override(&"font", font)
	label.add_theme_font_size_override(&"font_size", font_size_vector.x)
	label.text = "Hello world"
1 Like

I’m doing something wrong, font doesn’t seems appear correctly:

Here’s monospaced font 30x5, each char size is 3x5

example

#!/usr/bin/env -S godot -s --no-window
extends SceneTree

func _init() -> void:
	var image := load("res://example.png").get_image() as Image
	var glyphs := "0123456789"
	var font := FontFile.new()
	font.allow_system_fallback = false
	font.set_texture_image(0, image.get_size(), 0, image)

	for i in glyphs.length():
		var glyph := ord(glyphs[i])
		var glyph_size := Vector2i(3, 5)
		font.set_glyph_size(0, glyph_size, glyph, glyph_size)
		font.set_glyph_texture_idx(0, glyph_size, glyph, 0)
		font.set_glyph_uv_rect(0, glyph_size, glyph, Rect2i(Vector2i(i * glyph_size.x, glyph_size.y), glyph_size))

	ResourceSaver.save(font, "res://example.tres")
	printt(get_script().resource_path.get_file(), "done!")
	quit()

Yes, the size parameter in these methods isn’t refering to the texture size or the glyph size but to the font size. It’s a Vector2i where x is the font size and y a variant like the outline. You should keep y at 0 in your case. In the example I posted above it’s the font_size_vector variable.

still nonsense.

#!/usr/bin/env -S godot -s --no-window
extends SceneTree

func _init() -> void:
	var image := load("res://example.png").get_image() as Image
	var glyphs := "0123456789"
	var font := FontFile.new()
	var font_size := Vector2i(3, 0)
	font.set_texture_image(0, font_size, 0, image)

	for i in glyphs.length():
		var glyph := ord(glyphs[i])
		font.set_glyph_size(0, font_size, glyph, Vector2(3, 5))
		font.set_glyph_texture_idx(0, font_size, glyph, 0)
		font.set_glyph_uv_rect(0, font_size, glyph, Rect2i(Vector2i(i * 3, 0), Vector2i(3, 5)))

	ResourceSaver.save(font, "res://example.tres")
	printt(get_script().resource_path.get_file(), "done!")
	quit()

undocumented cluster**** happened here:

Font size in script must match font size in LABEL PROPERTIES!

brb

Your font_size should be Vector2i(5, 0)

It works fine:

extends Node


@onready var label: Label = $Label


func _ready() -> void:
	var font = FontFile.new()
	var font_image = preload("res://assets/bmfont/font_image.png").get_image()
	var font_size = Vector2i(5, 0)
	font.set_texture_image(0, font_size, 0, font_image)

	var glyphs = "0123456789".split()

	for i in glyphs.size():
		var glyph_idx = ord(glyphs[i])
		font.set_glyph_advance(0, font_size.x, glyph_idx, Vector2(4, 0))
		font.set_glyph_offset(0, font_size, glyph_idx, Vector2.ZERO)
		font.set_glyph_size(0, font_size, glyph_idx, Vector2(3, 5))
		font.set_glyph_texture_idx(0, font_size, glyph_idx, 0)
		font.set_glyph_uv_rect(0, font_size, glyph_idx, Rect2(i * 3, 0, 3, 5))

	label.add_theme_font_override(&"font", font)
	label.add_theme_font_size_override(&"font_size", font_size.x)
	label.text = "100398"
	label.scale *= 10

Result:

1 Like

thanks!