I'm making a 2D RPG-type video game in C++ ver.17 using the SDL2 libraries for my own pleasure (I don't intend to sell the game yet, at most to make it public for free). I've reached a point where I want to display in the GUI window of the game a counter of keys collected by my 2D character (it's a sprite sheet with animations in 4 directions). Also I would also add a second message always to be displayed in the GUI during the game. I structured the project by creating a class named GamePanel which represents the manager of the entire game. This class has a method named run() which contains the game loop of the game. In it the usual methods are called cyclically (with an FPS of 60):
Events( ): for user events
Update( ): for updating game scene frame
Render( ): to draw and render the game scene frame.
Among other things, in the Update() method I update a counter of keys collected by the character and consequently update the message to be displayed on the scene. Then the Render() method renders it all to me, thus showing me the message. Now the problem that arises is the use of the text through the SDL2_ttf library and the consequent use of SDL_Surface, containing the message using the font of SDL2_ttf and then the transition to the SDL_Texture to then display it using SDL_RenderCopy( ) and SDL_RenderPresent( ) in the scene . These passages cannot be done in the game loop, because this would lead to a "fatigue" of the PC doing it at the speed of 60 frames per second.
Is there therefore a way to make the display of the message, which updates continuously (at the speed of 60 FPS) ?
Can you give me some advice on how to proceed or better if you have found some tutorials that explain it to me?
I created a class named TextureManager to handle most of the graphics with SDL2. I made this class singleton in order to have only one instance and to be able to use it anywhere in the code. In this class I created some methods:
TextureManager::LoadText(std::string* tex): for loading the SDL_Surface which are then transformed into SDL_Texture. Of course, the created SDL_Surface must be deleted via SDL_FreeSurface. This for each texture you want to create.
TextureManager::DrawText(std::string* text, int x, int y, SDL_Texture* image): for drawing the texture (which contains for example the text, or the character).
In the GamePanel class I render everything by calling the TextureManagers, then using SDL_RenderPresent(m_Renderer);
I created a UI class that contains the update() and draw() methods related to the message to be displayed (the count of collected keys), and other messages to be displayed in the game.
This class is created outside the game loop in GamePanel and more precisely in the GamePanel::Init( ) method, where I initialize all the SDL2 libraries, including SDL2_ttf (the character font to be displayed in the GUI environment).
So I have the following methods:
void GamePanel::update() {
float dt = Timer::GetInstance()->GetDeltaTime();
//player->Update(dt);
player->update();
// aggiorna l'interfaccia utente
ui->update();
}
void GamePanel::Render() {
SDL_SetRenderDrawColor(m_Renderer, 124, 218, 254, 255);
SDL_RenderClear(m_Renderer);
// Tiles
tileM->draw();
// Objects
for (int i = 0; i < obj.size(); i++)
{
if (!obj.at(i).name.empty())
{
obj.at(i).draw();
}
}
// Player
player->draw(m_Renderer);
// disegna interfaccia utente: testo indicante numero chiavi disponibili
ui->draw();
SDL_RenderPresent(m_Renderer);
}
The text rendering API of SDL_ttf isn't the best. It works, but it has some disadvantages, especially concerning the performance.
The speed of the text rendering really depends on the quality setting you use (i.e. the function you use). The old documentation of SDL_ttf states:
TTF_RenderUTF8_Solid): "Quick and Dirty: [...] render the given text at fast quality [...]. This is the fastest rendering speed of all the rendering modes. This results in no box around the text, but the text is not as smooth. [...] Use this mode for FPS and other fast changing updating text displays."TTF_RenderUTF8_Shaded): "Slow and Nice, but with a Solid Box: [...] The text is antialiased. This will render slower than Solid, but in about the same time as Blended mode."TTF_RenderUTF8_Blended): "Slow Slow Slow, but Ultra Nice over another image: [...] Use this when you want high quality, and the text isn't changing too fast."Furthermore you need to copy the image into the VRAM (this is, what you described as the transition to a
SDL_Texture). This operation is really slow. You should really minimize the use of this function during the game loop (maybe just for a FPS counter, but an external debug terminal would be definitely faster).For every static text (like conversations with NPCs) or any text, that won't change, you should render and copy them to VRAM during the loading phase of your game or scene. You probably want a good quality for such strings. Therefore you need to create all the
SDL_Textures during startup and cache them. Then you just need to callSDL_RenderCopyin yourGamePanel::render()method.Now I don't know, how fast your item counter changes. But probably it will be even on higher text quality levels sufficient, if you just render the text once when the counter updates (including the creation of the texture). And then you store the texture like the static strings until the counter changes. This shouldn't introduce a huge lag.
But if it does, you can cache the individual digits. Render all digits from 0-9 once in separate textures and store them. Now you just need to pick the right textures and render them to the number you want to display. But be aware, that you shouldn't do this with texts, as you then would need to apply proper kerning. For numbers this is negligible, but for texts not. Texts would not look nice if you do that with the individual glyphs.