C++ SDL Font Install
This guide will provide step-by-step instructions on how to add True Type Font to the Simple DirectMedia Layer (SDL) library in a Visual Studio C++ project. This will enable the rendering of text (score, lives, …) in the application.
About True Type Font
TrueType is an outline font standard developed by Apple in the late 1980s as a competitor to Adobe’s Type 1 fonts used in PostScript. It has become the most common format for fonts on the MacOS and Microsoft Windows operating systems.
Download SDL True Type Font
- Download the latest SDL_ttf (True Type Font) library.
- https://github.com/libsdl-org/SDL_ttf/releases
- Download (or latest)
SDL3_ttf-devel-3.2.2-VC.zip
- Copy the .zip file to the ThirdParty folder and unzip the file.
- Copy the SDL3_TTF
include
andlib
directory to the existing ThirdParty/SDL3 folder.- This will add the SDL3 TTF files to SDL3. The files will share the same locations.
- The SDL3_TTF folder and .zip file can be deleted now.
Add SDL True Type Font to the Solution Project(s)
The SDL3 library should’ve already been setup with the include directories and library directories for the projects in a previous tutorial, the only step necessary is to add the SDL3_TTF library (.lib) to the Game project properties.
If the Solution contains multiple Projects, the following steps will need to be done for each project. This is because each project needs the path to the SDL3 TTF includes.
Add SDL True Type Font Library
The sdl3_ttf.lib only needs to be added to the project that is the application (Game). Do not add it to the library project (Engine). If it is added to both, there will be a warning reported when built.
- Open the Project Properties that SDL libray will be added to (Game).
- Right-click the Project and select Properties.
In Project Properties, ensure Configuration is set to All Configurations and Platform is set to All Platforms.
- Add the SDL library directory in Project Properties (Librarian > General or Linker > Input) to the Additional Library Directories:
$(SolutionDir)Source\ThirdParty\SDL3\lib\$(PlatformTarget)
- Add the SDL3_TTF.lib file.
- Additional Dependencies is located in Linker>Input.
- Add
sdl3_ttf.lib
1
sdl3_ttf.lib
Add SDL True Type Font Dynamic Link Library (dll)
The Build folder may have already been created in a previous tutorial.
- Create a folder in the Solution directory called “Build”.
- The Build folder will contain the SDL TTF dll (dynamic link library) files.
- Copy the sdl3_ttf.dll file from the ThirdParty\sdl3\lib\x64 directory to the Build folder.
- The project is a x64 project (64-bit application).
If SDL3_TT2 was properly added, building and running the project will result in no errors.
Add Fonts to the Build
- Find fonts to use in the program
- This site has free fonts that can be downloaded: https://www.1001freefonts.com/
- Place the .ttf font into the Build folder
Update Code for SDL TTF Fonts
Add TTF Fonts to the Renderer
- In the Renderer.h header, include the SDL_TTF header
1
#include <SDL3_ttf/SDL_ttf.h>
In the
Renderer
class, the private datam_renderer
needs to be accessible to theText
class. Afriend class
declaration allows that class to access the private members.
- Add the
Text
class as a friend of theRenderer
class. This was put in the private section of theRenderer
class in theRenderer.h
.1
friend class Text;
- Add the code to initialize and quit SDL_TTF in the Initialize() and Shutdown() method.
1
2
3
4
5
6
7
8
9
10
11
12
13
bool Renderer::Initialize() {
if (!SDL_Init(SDL_INIT_VIDEO)) {
std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl;
return false;
}
if (!TTF_Init()) {
std::cerr << "TTF_Init Error: " << SDL_GetError() << std::endl;
return false;
}
return true;
}
1
2
3
4
5
6
void Renderer::Shutdown() {
TTF_Quit();
SDL_DestroyRenderer(m_renderer);
SDL_DestroyWindow(m_window);
SDL_Quit();
}
Create Font Class
- Create a Font.h file in the Source/Engine/Renderer folder.
- Make the
Font
class a part of the engine namespace. - Include the necessary header files.
- TTF_Font* could be a forward declaration as it is a pointer.
- If using a forward declaraion, place the forward declaration at the top outside of the namespace. The TTF_Font is a struct.
1
struct TTF_Font;
In the
Font
class, the private datam_ttfFont
needs to be accessible to theText
class. Afriend class
declaration allows that class to access the private members.
1
2
3
4
5
6
7
8
9
10
11
12
class Font {
public:
Font() = default;
~Font();
bool Load(const std::string& name, float fontSize);
private:
friend class Text;
TTF_Font* m_ttfFont{ nullptr };
};
- Create a Font.cpp file in the Source/Engine/Renderer folder.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Font::~Font() {
if (m_ttfFont != nullptr) {
TTF_CloseFont(m_ttfFont);
}
}
bool Font::Load(const std::string& name, float fontSize) {
m_ttfFont = TTF_OpenFont(name.c_str(), fontSize);
if (m_ttfFont == nullptr) {
std::cerr << "Could not load font: " << name << std::endl;
return false;
}
return true;
}
Add comments to the methods in the .cpp by typing
///
above the method. The AI should fill in comments.
Create Text Class
- Create a Text.h file in the Source/Engine/Renderer folder.
- Include the necessary header files.
- Add it as part of the engine namespace.
- The classes that are references or pointers can be forward declarations.
- If using a forward declaraion, place the forward declaration of SDL classes/structs at the top outside of the namespace. The SDL_Texture is a struct.
1
struct SDL_Texture;
In the
Renderer
class, the private datam_renderer
needs to be accessible to theText
class. Afriend class
declaration allows that class to access the private members. Addfriend class Text
in the Renderer class declaration.
1
2
3
4
5
6
7
8
9
10
11
12
13
class Text {
public:
Text() = default;
Text(Font* font) : m_font{ font } {}
~Text();
bool Create(Renderer& renderer, const std::string& text, const vec3& color);
void Draw(Renderer& renderer, int x, int y);
private:
Font* m_font{ nullptr };
SDL_Texture* m_texture{ nullptr };
};
- Create a Text.cpp file in the Source/Engine/Renderer folder.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Text::~Text() {
if (m_texture != nullptr) {
SDL_DestroyTexture(m_texture);
}
}
bool Text::Create(Renderer& renderer, const std::string& text, const vec3& color) {
// create a surface using the font, text string and color
SDL_Color c{ (uint8_t)(color.r * 255), (uint8_t)(color.g * 255), (uint8_t)(color.b * 255), 255 };
SDL_Surface* surface = TTF_RenderText_Solid(m_font->m_ttfFont, text.c_str(), text.size(), c);
if (surface == nullptr) {
std::cerr << "Could not create surface.\n";
return false;
}
// create a texture from the surface, only textures can render to the renderer
m_texture = SDL_CreateTextureFromSurface(renderer.m_renderer, surface);
if (m_texture == nullptr) {
SDL_DestroySurface(surface);
std::cerr << "Could not create texture" << SDL_GetError() << std::endl;
return false;
}
// free the surface, no longer needed after creating the texture
SDL_DestroySurface(surface);
return true;
}
void Text::Draw(Renderer& renderer, float x, float y) {
// get the texture width and height
float width, height;
bool success = SDL_GetTextureSize(m_texture, &width, &height);
assert(success);
// set the texture into the renderer at rect
SDL_FRect rect{ x, y, width, height };
success = SDL_RenderTexture(renderer.m_renderer, m_texture, NULL, &rect);
assert(success);
}
Add comments to the methods in the .cpp by typing
///
above the method. The AI should fill in comments.
Create Text in Main
Make sure to include the necessary directories and add the namespace to the engine classes when using them in main. For example, namespace::font.
- In main() create and load a font. This should be after the renderer is created and before the main loop.
- Load(True Type Font Filename, Font Size)
1
2
Font* font = new Font();
font->Load("arcadeclassic.ttf", 20);
- After creating the font, create the text passing the font in the constructor. This should be after the font was created and loaded.
- Text(Font)
- Create(Renderer, Text String, vec3)
1
2
Text* text = new Text(font);
text->Create(g_engine.GetRenderer(), "Hello World", vec3{ 1, 1, 1, 1 });
- In the render section of the main loop, draw the text
- Draw(Renderer, X Position, Y Position)
1
text->Draw(g_engine.GetRenderer(), 40.0f, 40.0f);
Build and run the program and the text will show up on the screen at the set location. Multiple text objects can be created to draw text for player lives and game score.