C++ SDL2, How to regularly update a renderered text? (ttf)

偶尔善良 提交于 2020-05-27 06:54:18

问题


So I've been practicing/making a quick game for the past 6 hours, then something stumped me. The game had an integer, Score, which would be added up with one every time an ammo hits an alien.

int Score;
stringstream sstr;
sstr << Score;
string str1 = sstr.str();

TTF_Font* Sans = NULL;
Sans = TTF_OpenFont("Sans.ttf", 24);
SDL_Color White = {255, 255, 255};  
SDL_Surface* surfaceMessage = NULL;
surfaceMessage = TTF_RenderText_Solid(Sans, str1.c_str(), White);
SDL_Texture* Message = NULL;
Message = SDL_CreateTextureFromSurface(renderer, surfaceMessage);

SDL_Rect Message_rect;
Message_rect.x = 0;
Message_rect.y = 0;
Message_rect.w = 100;
Message_rect.h = 100;

//UPDATE/GAMELOOP AREA, I DIDN'T REALLY PASTE THE WHOLE PART
SDL_RenderCopy(renderer, Message, NULL, &Message_rect);

Now I've been trying different roundabouts as to how to update the texture, Message. I made a cout check to check if I did hit an alien and what my current score is, it appears perfectly fine, but the rendered texture, Message won't move from 0.

I created a texture from the surface (the message) because I mostly prefer textures and I don't have any surface since in my current knowledge, you'd at least need a filled surface where you could blitz this

And another question, I'm planning to make a dialogue heavy game, is there another way of doing the texts? I've got a strong feeling that I'm doing it wrong.


回答1:


Minimal runnable example

The counter gets updated every second.

Ubuntu 16.10, SDL 2.0.4:

sudo apt-get install libsdl2-dev libsdl2-ttf-dev
./main /path/to/my.ttf

This method is easy to integrate, but not very efficient as it re-rasters and re-creates textures all the time. If you also want efficiency, see: Rendering fonts and text with SDL2 efficiently I get 4k FPS, so it might be fine for simple applications.

GitHub upstream with a ttf file to test with: https://github.com/cirosantilli/cpp-cheat/blob/d36527fe4977bb9ef4b885b1ec92bd0cd3444a98/sdl/ttf.c:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#define COMMON_COLOR_MAX 255
#define COMMON_WINDOW_WIDTH 500
#define COMMON_WINDOW_HEIGHT (COMMON_WINDOW_WIDTH)

double common_get_secs(void) {
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);
    return ts.tv_sec + (1e-9 * ts.tv_nsec);
}
const double COMMON_FPS_GRANULARITY_S = 0.5;
double common_fps_last_time_s;
unsigned int common_fps_nframes;
void common_fps_init() {
    common_fps_nframes = 0;
    common_fps_last_time_s = common_get_secs();
}
void common_fps_update_and_print() {
    double dt, current_time_s;
    current_time_s = common_get_secs();
    common_fps_nframes++;
    dt = current_time_s - common_fps_last_time_s;
    if (dt > COMMON_FPS_GRANULARITY_S) {
        printf("FPS = %f\n", common_fps_nframes / dt);
        common_fps_last_time_s = current_time_s;
        common_fps_nframes = 0;
    }
}

#define MAX_STRING_LEN 4

/*
- x, y: upper left corner of string
- rect output Width and height contain rendered dimensions.
*/
void render_text(
    SDL_Renderer *renderer,
    int x,
    int y,
    const char *text,
    TTF_Font *font,
    SDL_Rect *rect,
    SDL_Color *color
) {
    SDL_Surface *surface;
    SDL_Texture *texture;

    surface = TTF_RenderText_Solid(font, text, *color);
    texture = SDL_CreateTextureFromSurface(renderer, surface);
    rect->x = x;
    rect->y = y;
    rect->w = surface->w;
    rect->h = surface->h;
    /* This is wasteful for textures that stay the same.
     * But makes things less stateful and easier to use.
     * Not going to code an atlas solution here... are we? */
    SDL_FreeSurface(surface);
    SDL_RenderCopy(renderer, texture, NULL, rect);
    SDL_DestroyTexture(texture);
}

int main(int argc, char **argv) {
    SDL_Color color;
    SDL_Event event;
    SDL_Rect rect;
    SDL_Renderer *renderer;
    SDL_Window *window;
    char *font_path, text[MAX_STRING_LEN];

    /* CLI arguments. */
    if (argc == 1) {
        font_path = "FreeSans.ttf";
    } else if (argc == 2) {
        font_path = argv[1];
    } else {
        fprintf(stderr, "error: too many arguments\n");
        exit(EXIT_FAILURE);
    }

    /* initialize variables. */
    color.r = COMMON_COLOR_MAX;
    color.g = COMMON_COLOR_MAX;
    color.b = COMMON_COLOR_MAX;
    color.a = COMMON_COLOR_MAX;

    /* Init window. */
    SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
    SDL_CreateWindowAndRenderer(
        COMMON_WINDOW_WIDTH,
        COMMON_WINDOW_WIDTH,
        0,
        &window,
        &renderer
    );

    /* Init TTF. */
    TTF_Init();
    TTF_Font *font = TTF_OpenFont(font_path, 24);
    if (font == NULL) {
        fprintf(stderr, "error: font not found\n");
        exit(EXIT_FAILURE);
    }

    /* Main loop. */
    common_fps_init();
    while (1) {
        if (SDL_PollEvent(&event) && event.type == SDL_QUIT) {
            break;
        }

        /* Use TTF. */
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
        SDL_RenderClear(renderer);
        render_text(renderer, 0, 0,               "hello", font, &rect, &color);
        render_text(renderer, 0, rect.y + rect.h, "world", font, &rect, &color);
        snprintf(text, MAX_STRING_LEN, "%u", (unsigned int)(time(NULL) % 1000));
        render_text(renderer, 0, rect.y + rect.h, text, font, &rect, &color);
        SDL_RenderPresent(renderer);

        common_fps_update_and_print();
    }

    /* Cleanup. */
    TTF_Quit();
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return EXIT_SUCCESS;
}



回答2:


Well, obviously you need to recreate texture from surface with new text each time your score changes. That is not very efficient for texts that change frequently (since you create/destroy a lot of surfaces/textures), but can be fine for small games (since modern computers are very powerful).

But generally, as mentioned in comments, for this case font atlases are used with combination of custom text renderers. The trick is to store all characters in one texture and render its regions multiple times to produce necessary text. The AngelCode BMFont is popuar tool for creating font atlases.

For maximum performance both approaches are used in combination: precreated textures for static text, and font atlases for dynamic text.



来源:https://stackoverflow.com/questions/22852226/c-sdl2-how-to-regularly-update-a-renderered-text-ttf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!