Jump to content
  • Advertisement
Sign in to follow this  
Diath

[SDL] Problem with text output using SDL_ttf (probably).

This topic is 2635 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey there
I have recently decided to make some simple game and browsing through internet I found this library - SDL. There were a lot of opinions about SDL and I think it has enough functions to realize my 2D project.
So, I have main application loop, which draws surface from Engine class onto main surface - the screen. The Engine class has a function named "drawText" which draws given string onto Engine's surface using SDL_ttf function. Though, after I run the application the screen itself is whole black. First I found out that I need to use SDL_Flip function to update the screen but it didn't result in anything. Below I am going to post my code, hopefully someone could re-view it and give me some tips how can I make it work properly.

main.cpp:
#include <stdio.h>
#include <SDL/SDL.h>

#include "../headers/engine.h"
#include "../headers/game.h"
#include "../headers/player.h"

const int TICKS = (int) 1000 / 60;

int main(int argc, char* argv[])
{
SDL_Surface *screen;
SDL_Event event;

Engine *engine = Engine::getInstance();
Game *game = Game::getInstance();
Player *player = Player::getInstance();

int keypress = 0;
if(SDL_Init(SDL_INIT_VIDEO) < 0)
return 1;

if(!(screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE)))
{
SDL_Quit();
return 1;
}

SDL_WM_SetCaption("Testing Caption", "");
if(!engine->load())
{
SDL_Quit();
return 1;
}
engine->drawText("Herro, I am test message!", 100, 100, {0, 255, 0, 100}, 12);
SDL_Rect position = {0, 0, 800, 600};

int32_t ticks = SDL_GetTicks(), sleepTime = 0;
while(game->isRunning())
{
while(SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
game->setRunning(false);
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
game->setRunning(false);
break;
}
break;
}
}

SDL_BlitSurface(engine->getSurface(), &position, screen, &position);
SDL_Flip(screen);

ticks += TICKS;
sleepTime = ticks - SDL_GetTicks();
if(sleepTime > 0)
SDL_Delay(sleepTime);
}

SDL_Quit();
return 0;
}


engine.h:
#ifndef __ENGINE__
#define __ENGINE__

#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <iostream>
#include <string>

class Engine
{
public:
Engine();
~Engine();

static Engine *getInstance()
{
static Engine engine;
return &engine;
}

bool load();
SDL_Surface *getSurface() { return surface; }
void drawText(std::string, int x, int y, SDL_Color, int);

private:
SDL_Surface *surface;
SDL_Surface *textSurface;
TTF_Font *textFont[12];
};

#endif


#include "../headers/engine.h"

Engine::Engine()
{
TTF_Init();
surface = new SDL_Surface;
}

Engine::~Engine()
{
for(int i = 1; i <= 18; ++i)
TTF_CloseFont(textFont);

SDL_FreeSurface(textSurface);
SDL_FreeSurface(surface);
}

bool Engine::load()
{
for(int i = 1; i <= 18; ++i)
{
textFont = TTF_OpenFont("./resources/fonts/arial.ttf", i);
if(!textFont)
return false;
}

return true;
}

void Engine::drawText(std::string text, int x, int y, SDL_Color color, int fontSize)
{
if(fontSize < 1 || fontSize > 18)
return;

textSurface = TTF_RenderText_Shaded(textFont[fontSize], text.c_str(), color, {0, 0, 0});
SDL_Rect pos = {x, y, textSurface->w, textSurface->h};
SDL_BlitSurface(textSurface, &pos, surface, &pos);
}


I am not going to post Player and Game classes as Player class is actually empty and game contains about 2 functions which change/return one boolean variable, so I don't think it does matter in this case.

Regards,
Diath.

Share this post


Link to post
Share on other sites
Advertisement
Don't create SDL_Surfaces with new. Use SDL_CreateRGBSurface if you want to create a new empty surface.

If you call drawText more than once you will have a memory leak.

Share this post


Link to post
Share on other sites
Sorry for the late answer, it's been late evening my time when I made the thread.

So I replaced the "new" method with SDL_CreateRGBSurface and added SDL_FreeSurface in drawText on textSurface. Though, it still doesn't output anything.

This is my current code:
#include "../headers/engine.h"

Engine::Engine()
{
TTF_Init();
surface = SDL_CreateRGBSurface(SDL_HWSURFACE, 800, 600, 32, 0, 0, 0, 0);
}

Engine::~Engine()
{
for(int i = 1; i <= 18; ++i)
TTF_CloseFont(textFont);

//SDL_FreeSurface(textSurface);
SDL_FreeSurface(surface);
}

bool Engine::load()
{
for(int i = 1; i <= 18; ++i)
{
textFont = TTF_OpenFont("./resources/fonts/arial.ttf", i);
if(!textFont)
return false;
}

return true;
}

void Engine::drawText(std::string text, int x, int y, SDL_Color color, int fontSize)
{
if(fontSize < 1 || fontSize > 18)
return;

textSurface = TTF_RenderText_Shaded(textFont[fontSize], text.c_str(), color, {0, 0, 0});
SDL_Rect pos = {x, y, textSurface->w, textSurface->h};
SDL_BlitSurface(textSurface, &pos, surface, &pos);
SDL_FreeSurface(textSurface);
}

Share this post


Link to post
Share on other sites
You're not testing the return values of TTF_Init() or SDL_CreateRGBSurface(). These are actually called before SDL_Init() in main(), which is a problem.

I suggest either encapsulating SDL initialisation completely in a class that is constructed at the start of main(), or doing it all in a free function that is called at the start of main(). Essentially, either tie SDL/TTF initialisation to the lifetime of a specific object (not necessarily the engine, but that is an option) or don't, but certainly don't do both.

The identifier __ENGINE__ is reserved for the compiler, as are any that begin with two underscores, an underscore with a capital letter. Identifiers beginning with a single underscore are reserved in the global scope. I'd recommend using ENGINE_H instead.

Your Engine constructor should zero initialise all variables, in case the destructor needs to run before you get a change to call init(). Another option is to move the "init" logic into the constructor. Remember you can use exceptions to signal errors from a constructor.

For performance reasons, you should probably avoid a software back-buffer in your engine class unless you have a good reason. Instead, use SDL_GetVideoSurface() or pass the pointer returned by SDL_SetVideoMode() to the constructor of the Engine class, so it can draw "directly" to the screen.

Your font array is too small, you overrun the bounds in the Engine constructor. I'd recommend using a named constant for the size, or ditching the size and using std::vector or std::map to handle fonts. std::map is a nice solution, you can maintain a "sparse" mapping, only loading fonts sizes when they are actually requested.

The variable "textSurface" should be local to drawText.

I would recommend against using singletons in this code, there is no need for them.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!