0

I am programming a simple window-based game using SDL2 libraries in C++. With the help of the SDL2_ttf library, I have no trouble displaying static fonts to my SDL_Window like the name of the game. But I also want to display to the user how many lives they have and their score (which will be the duration in which the SDL window remains open).

Here are my sdlGame.h and sdlGame.cpp files:

#ifndef sdlGame_h
#define sdlGame_h
#include <SDL2/SDL.h>
#include <SDL2_image/SDL_image.h>
#include <SDL2_ttf/SDL_ttf.h>
/* FOR WINDOWS, UNCOMMENT THESE 3 LINES
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
 */
#include "Game.h"

class Image {

private:

    SDL_Surface * m_surface;
    SDL_Texture * m_texture;
    bool m_hasChanged;

public:
    Image () ;
    ~Image();
    void loadFromFile (const char* filename, SDL_Renderer * renderer);
    void loadFromCurrentSurface (SDL_Renderer * renderer);
    void draw (SDL_Renderer * renderer, int x, int y, int w=-1, int h=-1);
    SDL_Texture * getTexture() const;
    void setSurface(SDL_Surface * surf);
};

class SDLSimple {

private :

    Game game;

    SDL_Window * window;
    SDL_Renderer * renderer;

    TTF_Font * font;
    Image font_im,font_score,font_time;
    SDL_Color font_color;

    Image shipImg;
    Image astroImg;
    Image spaceImg;
    Image starImg;
    Image shieldImg;
    Image slowImg;
    Image clrImg;
    Image hpImg;
    Image im_fantome;

    bool mouse;
    bool key;
public :

    SDLSimple ();
    ~SDLSimple ();
    void sdlBoucle ();
    void sdlAff ();

};



#endif /* sdlGame_h */
#include <cassert>
#include <time.h>
#include "sdlGame.h"
#include <stdlib.h>
#include <string>

#include <iostream>
using namespace std;

const int TAILLE_SPRITE = 32;



float temps () {
    return float(SDL_GetTicks()) / CLOCKS_PER_SEC;  // conversion des ms en secondes en divisant par 1000
}

float timePassed = temps();


// ============= CLASS IMAGE =============== //

Image::Image () : m_surface(nullptr), m_texture(nullptr), m_hasChanged(false) {
}

Image::~Image()
{
    SDL_FreeSurface(m_surface);
    SDL_DestroyTexture(m_texture);

    m_surface = nullptr;
    m_texture = nullptr;
    m_hasChanged = false;
}

void Image::loadFromFile (const char* filename, SDL_Renderer * renderer) {
    m_surface = IMG_Load(filename);
    if (m_surface == nullptr) {
        string nfn = string("../") + filename;
        cout << "Error: cannot load "<< filename <<". Trying "<<nfn<<endl;
        m_surface = IMG_Load(nfn.c_str());
        if (m_surface == nullptr) {
            nfn = string("../") + nfn;
            m_surface = IMG_Load(nfn.c_str());
        }
    }
    if (m_surface == nullptr) {
        cout<<"Error: cannot load "<< filename <<endl;
        SDL_Quit();
        exit(1);
    }

    SDL_Surface * surfaceCorrectPixelFormat = SDL_ConvertSurfaceFormat(m_surface,SDL_PIXELFORMAT_ARGB8888,0);
    SDL_FreeSurface(m_surface);
    m_surface = surfaceCorrectPixelFormat;

    m_texture = SDL_CreateTextureFromSurface(renderer,surfaceCorrectPixelFormat);
    if (m_texture == NULL) {
        cout << "Error: problem to create the texture of "<< filename<< endl;
        SDL_Quit();
        exit(1);
    }
}

void Image::loadFromCurrentSurface (SDL_Renderer * renderer) {
    m_texture = SDL_CreateTextureFromSurface(renderer,m_surface);
    if (m_texture == nullptr) {
        cout << "Error: problem to create the texture from surface " << endl;
        SDL_Quit();
        exit(1);
    }
}

void Image::draw (SDL_Renderer * renderer, int x, int y, int w, int h) {
    int ok;
    SDL_Rect r;
    r.x = x;
    r.y = y;
    r.w = (w<0)?m_surface->w:w;
    r.h = (h<0)?m_surface->h:h;

    if (m_hasChanged) {
        ok = SDL_UpdateTexture(m_texture,nullptr,m_surface->pixels,m_surface->pitch);
        assert(ok == 0);
        m_hasChanged = false;
    }

    ok = SDL_RenderCopy(renderer,m_texture,nullptr,&r);
    assert(ok == 0);
}

SDL_Texture * Image::getTexture() const {return m_texture;}

void Image::setSurface(SDL_Surface * surf) {m_surface = surf;}










// ============= CLASS SDLJEU =============== //

SDLSimple::SDLSimple () : game() {
    // Initialisation de la SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        cout << "Erreur lors de l'initialisation de la SDL : " << SDL_GetError() << endl;
        SDL_Quit();
        exit(1);
    }

    if (TTF_Init() != 0) {
        cout << "Erreur lors de l'initialisation de la SDL_ttf : " << TTF_GetError() << endl;
        SDL_Quit();
        exit(1);
    }

    int imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
    if( !(IMG_Init(imgFlags) & imgFlags)) {
        cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << endl;
        SDL_Quit();
        exit(1);
    }

    int dimx, dimy;
    dimx = game.getTerrain().getDimX();
    dimy = game.getTerrain().getDimY();
    dimx = dimx * TAILLE_SPRITE;
    dimy = dimy * TAILLE_SPRITE;

    // Creation de la fenetre
    window = SDL_CreateWindow("Space Race", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 960, 960, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if (window == nullptr) {
        cout << "Erreur lors de la creation de la fenetre : " << SDL_GetError() << endl;
        SDL_Quit();
        exit(1);
    }

    renderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);

    // IMAGES (filepaths are placeholders)
    shipImg.loadFromFile("filepath", renderer);
    astroImg.loadFromFile("filepath", renderer);
    hpImg.loadFromFile("/filepath", renderer);
    shieldImg.loadFromFile("filepath", renderer);
    spaceImg.loadFromFile("filepath", renderer);
    starImg.loadFromFile("filepath", renderer);
    slowImg.loadFromFile("filepath", renderer);
    clrImg.loadFromFile("filepath", renderer);

    // FONTS
    font = TTF_OpenFont("filepathforfont",50);
    if (font == nullptr)
        font = TTF_OpenFont("filepathforfont",50);
    if (font == nullptr) {
            cout << "Failed to load DejaVuSansCondensed.ttf! SDL_TTF Error: " << TTF_GetError() << endl;
            SDL_Quit();
            exit(1);
    }
    
    
    font_color.r = 255;font_color.g = 255;font_color.b = 255;
    font_im.setSurface(TTF_RenderText_Solid(font,"Space Race",font_color));
    font_score.setSurface(TTF_RenderText_Solid(font,"Score",font_color));
    font_time.setSurface(TTF_RenderText_Solid(font, timePassed, font_color));
    font_im.loadFromCurrentSurface(renderer);
    font_score.loadFromCurrentSurface(renderer);
    font_time.loadFromCurrentSurface(renderer);
}

SDLSimple::~SDLSimple () {
    TTF_CloseFont(font);
    TTF_Quit();
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

void SDLSimple::sdlAff () {
    SDL_SetRenderDrawColor(renderer, 230, 240, 255, 255);
    SDL_RenderClear(renderer);

    int x,y;
    const Terrain& ter = game.getTerrain();
    const Ship& ship = game.getShip();

    for (x=0;x<ter.getDimY();x++){
        for (y=0;y<ter.getDimX();y++){
            if (ter.getXYasChar(x,y)==' '){
                spaceImg.draw(renderer,x*TAILLE_SPRITE,y*TAILLE_SPRITE,TAILLE_SPRITE,TAILLE_SPRITE);
                
            }
            else if (ter.getXYasChar(x,y)=='*'){
                starImg.draw(renderer,x*TAILLE_SPRITE,y*TAILLE_SPRITE,TAILLE_SPRITE,TAILLE_SPRITE);
            }
            
            else if (ter.getXYType(x,y)==Terrain::ASTRO){
                spaceImg.draw(renderer,x*TAILLE_SPRITE,y*TAILLE_SPRITE,TAILLE_SPRITE,TAILLE_SPRITE);
                astroImg.draw(renderer,x*TAILLE_SPRITE,y*TAILLE_SPRITE,TAILLE_SPRITE,TAILLE_SPRITE);
            }
        }
        
    }

    // Afficher le sprite de Pacman
    shipImg.draw(renderer,ship.getX()*TAILLE_SPRITE,ship.getY()*TAILLE_SPRITE,TAILLE_SPRITE,TAILLE_SPRITE);

    // Ecrire un titre par dessus
    SDL_Rect positionTitre;
    SDL_Rect positionScore;
    SDL_Rect positionTime;
    positionTitre.x = 480;positionTitre.y = 0;positionTitre.w = 100;positionTitre.h = 30;
    positionScore.x = 0; positionScore.y = 0; positionScore.w = 100; positionScore.h = 30;
    positionTime.x = 0; positionTime.y = 30; positionTime.w = 100; positionTime.h = 23;
    SDL_RenderCopy(renderer,font_im.getTexture(),nullptr,&positionTitre);
    SDL_RenderCopy(renderer,font_score.getTexture(),nullptr,&positionScore);
    SDL_RenderCopy(renderer,font_time.getTexture(),nullptr,&positionTime);

}

void SDLSimple::sdlBoucle () {
    SDL_Event events;
    bool quit = false;

    Uint32 t = SDL_GetTicks(), nt;

    // tant que ce n'est pas la fin ...
    while (!quit) {

        nt = SDL_GetTicks();
        if (nt-t>500) {
            game.actionsAuto();
            t = nt;
        }

        // tant qu'il y a des évenements à traiter (cette boucle n'est pas bloquante)
        while (SDL_PollEvent(&events)) {
            if (events.type == SDL_QUIT) quit = true;           // Si l'utilisateur a clique sur la croix de fermeture
            else if (events.type == SDL_KEYDOWN) {              // Si une touche est enfoncee
                bool deleteAstroCollided = false;
                switch (events.key.keysym.scancode) {
                case SDL_SCANCODE_W:
                        deleteAstroCollided = game.keyboardListener('s');    // car Y inverse
                    break;
                case SDL_SCANCODE_S:
                        deleteAstroCollided = game.keyboardListener('w');    // car Y inverse
                    break;
                case SDL_SCANCODE_A:
                        deleteAstroCollided = game.keyboardListener('a');
                    break;
                case SDL_SCANCODE_D:
                        deleteAstroCollided = game.keyboardListener('d');
                    break;
                case SDL_SCANCODE_ESCAPE:
                case SDL_SCANCODE_Q:
                    quit = true;
                    break;
                default: break;
                }
            }
        }
        if(game.getShip().getLifeCount() == 0){
            quit = true;
        }

        // on affiche le jeu sur le buffer cach�
        sdlAff();

        // on permute les deux buffers (cette fonction ne doit se faire qu'une seule fois dans la boucle)
        SDL_RenderPresent(renderer);
    }
}

You can see in the .h file that I have declared a font named "font_time" to represent the ttf_font for the time.

In the .cpp file, I have a global function named "temps()" which return the time spent using SDL_GetTicks() and divides it by 1000 to get the time in seconds. I have a float named "time Passed" which uses the temps() function to get the time passed.

In a line, I tried to render the font like I did with other fonts:

font_time.setSurface(TTF_RenderText_Solid(font, to_string(timePassed), font_color));

But, I had an error saying: No matching function for call to 'TTF_RenderText_Solid'

I read the documentation and I saw that the second parameter is const char pointer to a text, but I think it is only for static character arrays since it works fine when I pass strings which are not variables directly to the code.

Is there any way to display dynamic texts to the screen using just the member functions of SDL_ttf? If not, how can I create a dynamic text class which uses SDL rendering and etc.?

genpfault
  • 51,148
  • 11
  • 85
  • 139

1 Answers1

1
TTF_RenderText_Solid(font, to_string(timePassed), font_color)
                           ^^^^^^^^^^^^^^^^^^^^^ nope

TTF_RenderText_Solid() takes a const char * as its second parameter, not a std::string or float:

extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font,
                const char *text, SDL_Color fg);

Grab a C string from the std::string that std::to_string() returns via std::string::c_str() & use that instead:

TTF_RenderText_Solid(
    font,
    std::to_string(timePassed).c_str(),
    font_color)
genpfault
  • 51,148
  • 11
  • 85
  • 139