TextServer in Godot – Complete Guide

When venturing into the world of game development, engaging visuals and animations often steal the show, but equally crucial is the handling of text and fonts—an area where the Godot Engine shines with its powerful TextServer class. In Godot 4’s robust development framework, TextServer is a vital interface that manages font resources and text rendering, ensuring your game’s textual content appears just as impressive as the rest of your assets.

What is TextServer?
TextServer in Godot 4 is the core class responsible for all text-related operations in the engine. It offers developers the capability to create and manage fonts, implement text shaping, rendering, and handle various text-related tasks, such as language-specific rendering and font fallbacks.

What is it for?
The primary purpose of TextServer is to bridge the gap between the game’s font assets and the visual output on the screen. It facilitates:

  • Font creation and configuration.
  • Shaping of text to accommodate complex scripts and characters.
  • Drawing of glyphs and text on the canvas for in-game displays.
  • Management of font caches and glyph textures.

Why should I learn it?
Understanding TextServer and its capabilities will empower you to fully harness Godot’s text rendering functionality, allowing you to:

  • Ensure legibility and visual consistency in your game’s text.
  • Support multiple languages and writing systems efficiently.
  • Personalize and stylize text to fit your game’s aesthetic.
  • Optimize text rendering for performance.

Engaging with TextServer’s features will elevate your game’s quality and player experience, especially when dealing with interface design and in-game narratives. Let’s jump into how to use TextServer with some practical examples that will bring these concepts to life!

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Creating and Configuring Fonts

To begin utilizing the TextServer in Godot 4, we need to start by loading or creating a font. We’ll use a DynamicFont in this example, but you can also use BitmapFont or any other supported font resource.

var font = DynamicFont.new()
font.font_data = load("res://path_to_font.ttf")
font.size = 20

We create a new DynamicFont instance and then load our TrueType font file into it. Next, we set the desired font size. Now, this font is ready to be used with a Label node or any other GUI control that displays text.

var label = Label.new()
label.font = font
label.text = "Hello, Godot 4!"
add_child(label)

In this snippet, we initialize a new Label, assign our previously loaded font to it, and set the label’s text. Finally, we add the label as a child to the current scene, ensuring it’s visible in our game.

Text Shaping and BiDi Support

TextServer also supports Bi-directional (BiDi) text and shaping, which is essential for languages like Arabic and Hebrew. Text shaping is the process of transforming a string of characters into glyph representations, while BiDi support allows the combination of different text directions in a single paragraph.

To handle a text with complex shaping and BiDi, Godot 4’s TextServer provides the following:

var shaped_string = TextServer.shape_string(font.get_ascent(), "رمزی پارسی", font)

This line creates a `ShapedString` based on our specified font’s ascent, handling the shaping of non-Latin characters correctly.

var bidi_override = TextServer.OVERRIDE_LTR
var shaped_line = TextServer.process_bidi_override(shaped_string, font.get_ascent(), font, bidi_override)

Here, we pass our shaped string to `process_bidi_override` with an override direction (in this case, left-to-right). The method returns a `ShapedAttributedString`, ready for rendering.

Drawing Glyphs and Text

Once we have our font and shaped text, we can proceed to draw text on the canvas. In Godot 4, you draw on the canvas using the `draw_string` method in the `_draw` callback.

func _draw():
    draw_string(font, Vector2(10, 50), "Drawn Text", Color.white, -1, shaped_line)

This method will draw “Drawn Text” at the position `(10, 50)` on the canvas using our font, colored white, with the maximum width set to `-1` (meaning no width restriction), and uses our previously shaped line.

Optimizing Text Rendering

Godot’s TextServer allows for optimization by caching glyphs to avoid unnecessary processing. Associated with each DynamicFont, there is a method to update the font’s cache.

font.update_changes()

Call this method whenever you change properties of the font, such as the size, outline, or fallbacks. This ensures that all the glyphs are correctly cached and ready for efficient rendering.

By understanding these basic operations provided by Godot 4’s TextServer, you’ll be set to create immersive and visually consistent text elements in your games. Stay tuned for additional examples that will delve further into font fallbacks, custom drawing routines, and text-based interactions.When working with TextServer, an advanced feature we often take advantage of is custom drawing routines. This allows us to do more than just put plaintext on the screen; we can create animations, highlight specific words, or change styles dynamically.

func _draw():
    var start_pos = Vector2(10, 10)
    var end_pos = Vector2(200, 10)
    draw_line(start_pos, end_pos, Color.red, 2)
    draw_string(font, start_pos, "Custom Draw", Color.blue)

Here we’re drawing a line from `start_pos` to `end_pos` in red and then adding our string directly below it in blue, showing how we can combine different drawing elements to create a stylized text display.

Another powerful feature is the implementation of font fallbacks. This is crucial when dealing with multiple languages or special characters.

var primary_font = DynamicFont.new()
var fallback_font = DynamicFont.new()

primary_font.font_data = load("res://primary_font.ttf")
fallback_font.font_data = load("res://fallback_font.ttf")

primary_font.add_fallback(fallback_font)

We’re setting up a primary font and specifying another font as a fallback. This way, if a character is not found in `primary_font`, `fallback_font` will be checked before giving up on rendering the character.

For handling user input and rendering it in real-time, we can leverage TextServer’s processing capabilities:

func _on_TextEdit_text_changed(new_text):
    var shaped_string = TextServer.shape_string(font.get_ascent(), new_text, font)
    update()  # To trigger the _draw() with the updated text

func _draw():
    draw_string(font, Vector2(10, 80), shaped_string, Color.green)

This code snippet hooks onto a TextEdit node’s `text_changed` signal. Whenever the user types something, we re-shape the string and update the canvas to reflect these changes.

TextServer can also handle advanced text alignment options:

var text_to_draw = "Align me center"
var text_width = TextServer.get_string_width(text_to_draw, font)

func _draw():
    var canvas_width = get_viewport_rect().size.x
    var x_pos = (canvas_width - text_width) / 2
    draw_string(font, Vector2(x_pos, 120), text_to_draw, Color.black)

Here we calculate the width of our text and use it alongside the width of the viewport to center the text horizontally on the canvas. The `x_pos` variable determines where the text will start drawing on the `x` axis.

Finally, let’s look at how to animate text by changing its opacity over time:

var text_opacity = 1.0
var fading_out = true

func _process(delta):
    if fading_out:
        text_opacity -= delta
    else:
        text_opacity += delta
    
    text_opacity = clamp(text_opacity, 0.0, 1.0)
    update()

func _draw():
    var alpha_color = Color(1, 1, 1, text_opacity)
    draw_string(font, Vector2(10, 160), "Fading Text", alpha_color)

We set up a `text_opacity` variable and flip its direction with a `fading_out` boolean. In the `_process` function, we modify the opacity and clamp its value between 0 and 1. The `_draw` function then renders the text with the updated opacity, creating a fading effect.

By integrating the snippets provided into your Godot projects, you can create text with dynamic behaviors, styles, and fallback options, which are essential for crafting a polished and professional gaming experience. The versatility of Godot’s TextServer brings a myriad of possibilities to the realm of text rendering in game development.Continuing with our exploration of the TextServer’s more advanced features in Godot 4, let’s experiment with text interaction. Games often require more from text than static display—think clickable words, mouse-over effects, or even in-game text-based puzzles.

Let’s create a clickable word within a sentence:

var is_mouse_over_word = false

func _ready():
    set_process_input(true)  # To allow input processing

func _input(event):
    if event is InputEventMouseMotion and _is_mouse_over_word(event.position):
        is_mouse_over_word = true
        update()
    elif event is InputEventMouseMotion:
        is_mouse_over_word = false
        update()
    elif event is InputEventMouseButton and event.pressed and is_mouse_over_word:
        _on_word_clicked()

func _draw():
    var phrase = "Click here!"
    var color = is_mouse_over_word ? Color.red : Color.white
    draw_string(font, Vector2(10, 200), phrase, color)

func _is_mouse_over_word(mouse_position):
    var rect = Rect2(Vector2(10, 200), Vector2(font.get_string_width("Click here!"), font.get_height()))
    return rect.has_point(mouse_position)

func _on_word_clicked():
    print("Word clicked!")

Here we have a conditional statement that checks if the mouse is over the word “Click here!”. If so, it changes color and prints “Word clicked!” in the console when clicked, demonstrating how text can be interactive.

Now let’s add a mouse-over effect for an entire sentence:

var sentence = "Hover over me to see magic."
var hovered_word_index = -1

func _process(delta):
    var mouse_pos = get_local_mouse_position()
    hovered_word_index = _get_word_under_mouse(mouse_pos)
    update()

func _draw():
    var words = sentence.split(" ")
    var position = Vector2(10, 240)
    for i in range(words.size()):
        var word = words[i] + " "
        var color = i == hovered_word_index ? Color.yellow : Color.white
        draw_string(font, position, word, color)
        position.x += font.get_string_width(word)

func _get_word_under_mouse(mouse_position):
    var pos = 10
    for i in range(sentence.split(" ").size()):
        var word = sentence.split(" ")[i] + " "
        var rect = Rect2(Vector2(pos, 240), Vector2(font.get_string_width(word), font.get_height()))
        if rect.has_point(mouse_position):
            return i
        pos += font.get_string_width(word)
    return -1

The code above highlights each word in the sentence as the mouse hovers over it, illustrating how to handle complex mouse-over interactions with text.

Next, let’s animate a label that scales up and down over time—a common effect for game UI:

var scale_factor = 1.0
var scaling_up = true

func _process(delta):
    scale_factor += (scaling_up ? 1 : -1) * delta
    if scale_factor > 1.5 or scale_factor < 1.0:
        scaling_up = !scaling_up
    update()

func _draw():
    var transform = Transform2D().scaled(Vector2(scale_factor, scale_factor))
    draw_set_transform_matrix(transform)
    draw_string(font, Vector2(10, 280), "Scaling Text", Color.white)
    draw_set_transform_matrix(Transform2D())  # Reset transform

This snippet uses a scaling transform to increase and decrease the size of the “Scaling Text” string, providing a flashy animated effect for emphasis or attention-grabbing UI components.

Now for a bit of text-based gameplay, let’s create a reaction timer game where a label prompts the player to press a key as fast as they can after the text appears:

var timer_started = false
var start_time = 0.0

func _ready():
    set_process_input(true)
    $Timer.start(rand_range(1.0, 3.0))  # Start a random timer

func _input(event):
    if event is InputEventKey and timer_started:
        var reaction_time = OS.get_ticks_msec() - start_time
        $ReactionTimeLabel.text = str(reaction_time) + " ms!"
        timer_started = false
        $Timer.start(rand_range(1.0, 3.0))  # Start a new random timer

func _on_Timer_timeout():
    $PressKeyLabel.visible = true
    timer_started = true
    start_time = OS.get_ticks_msec()

When the timer times out, the “Press Key” label becomes visible, and the player must hit any key as fast as possible. Once they do, the reaction time is displayed on screen.

These examples showcase the broad range of possibilities for dynamic text in Godot 4. Whether for user interfaces, engaging game mechanics, or simply to offer a more polished and interactive experience for players, mastering the TextServer class will significantly benefit game development.

Where to Go Next with Your Godot Learning Journey

Learning Godot 4 and its intricacies, such as the TextServer, is just the beginning. The path to mastering game development is continuous and filled with new challenges and skills to acquire. We at Zenva encourage you to keep forging ahead on your journey and expand your knowledge through practice and further education.

If you’ve enjoyed learning about Godot 4 and want to dive deeper into creating your own games, the Godot Game Development Mini-Degree is your next big step. This comprehensive course collection is designed to take beginners with no prior coding experience through the paces of game creation. The courses are provided online and are self-paced, making learning flexible and adaptable to your schedule. You’ll tackle 2D and 3D game development, learn the GDScript programming language, and create a portfolio of real Godot projects.

For those looking for a broad range of Godot tutorials, our Godot courses provide a wealth of knowledge to tap into. From the basics to more advanced topics, boost your skills at your own pace, building a strong foundation for your career in game development.

Remember, learning is a journey, not a destination, and we’re here to support you every step of the way. Happy coding!

Conclusion

The power of Godot 4’s TextServer extends far beyond simple text display, providing an avenue for creativity and interactivity in your games. As you’ve seen through our various examples, whether it’s custom drawing routines or creating interactive text elements, the possibilities are virtually limitless. Mastery of these features is just the first step in elevating the quality and player engagement of your projects.

As we wrap up this tutorial, don’t hesitate to step further into the expansive world of game development with our Godot Game Development Mini-Degree. Enhance your skill set, build your portfolio, and bring your unique game ideas to life with confidence. Keep experimenting, keep learning, and most importantly, keep creating. We at Zenva can’t wait to see where your game development journey takes you next!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.