I have no idea what is happening here, and although it's cool and oddly creepy, it's not really what I had in mind.
Basically; I'm trying to achieve a program that slow types text upon a black interface. I'm using SDL2 and SDL2_TTF to accomplish this, and things have gone well up to this point.
Currently, I'm having it type "Hey ;)" onto a black screen, but here's what's happening:
Ignore the FPS counter, that's just Nvidia.
I honestly have no idea what's happening, the font I'm using is "Hack-Regular.ttf".
Onto the code:
#include<iostream>
#include<SDL.h>
#include<string>
#include<SDL_ttf.h>
void handleEvents(SDL_Event e, bool* quit){
while(SDL_PollEvent(&e) > 0){
if(e.type == SDL_QUIT){
*quit = true;
}
}
}
void render(SDL_Renderer* renderer, SDL_Texture* textToRender, SDL_Rect srcrect, SDL_Rect dstrect){
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, textToRender, &srcrect, &dstrect);
SDL_RenderPresent(renderer);
}
void printToConsole(std::string message, char* text){
for(int i = 0; i < message.length(); i++){
*text = *text + message.at(i);
SDL_Delay(30);
}
}
void start(char text){
printToConsole("Hey ;)", &text);
}
int main( int argc, char *argv[] ) {
SDL_Init(SDL_INIT_EVERYTHING);
TTF_Init();
SDL_Window* window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 600, 600, SDL_RENDERER_ACCELERATED);
SDL_Renderer* renderer = SDL_CreateRenderer(window, 0, 0);
char text = 'asdf'; //This is the text that has been rendered.
bool quit = false;
SDL_Event e;
TTF_Font* font = TTF_OpenFont("Hack-Regular.ttf", 28);
SDL_Color color = {255, 255, 255};
SDL_Surface* textSurface;
SDL_Texture* textTexture;
SDL_Rect srcrect;
SDL_Rect dstrect;
srcrect.x = 0;
srcrect.y = 0;
srcrect.w = 100;
srcrect.h = 32;
dstrect.x = 640/2;
dstrect.y = 480/2;
dstrect.w = 100;
dstrect.h = 32;
while(!quit){
handleEvents(e, &quit);
render(renderer, textTexture, srcrect, dstrect);
start(text);
textSurface = TTF_RenderText_Solid(font, &text, color);
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
window = NULL;
renderer = NULL;
TTF_Quit();
SDL_Quit();
return 0;
}
My apologies if it is difficult to read, I didn't feel the need to use multiple classes.
something to note, the "f" is the last letter of 'asdf' I define the text as initially, no idea why it starts there.
In the following statement, you define a variable that occupies 1 byte of memory, and stores the integer value 102, aka 'f' in ascii:
char text = 'asdf'; //This is the text that has been rendered.
There are several problems with the above statement:
char
variable can store only one character, one byte of memory. You're trying to assign 4. Only the last value stays.You then pass that value to a rendering routine, by taking the address of the variable and passing it as a char*
type:
textSurface = TTF_RenderText_Solid(font, &text, color);
However, in C/C++, a char*
is idiomatically used to store a pointer to a 'c-string' - that is, a sequence of characters with a final null terminator. In APIs that accept char*
arguments as c-strings, you're expected to pass a pointer to an sequence of characters with a null terminator at the end.
Since it expects the passed value to be a c-string, it'll blindly read from memory until it sees a byte with value 0. Since it's reading past the end of your the memory set aside for that variable (which is only a single byte), it's going to read arbitrary memory until it gets lucky and finds a zero somewhere - hence why it is showing garbage.
...
How do you fix this? Well, that depends on how you're going to be getting your strings.
With the code you've provided, there's no real reason to be allocating your own c-strings. If you have string
objects, you can access a char*
c-string compatible buffer using the c_str()
method; the returned pointer is valid as long as you have not modified the string
, and since the pointer returned is owned by string
, you don't need to worry about cleaning it up.
// Use the string class's implicit conversion from a c-string literal to
// a `std::string` object.
std::string text = "hello world";
...
while(!quit){
handleEvents(e, &quit);
render(renderer, textTexture, srcrect, dstrect);
// Note the lack of a call to the `start` method here.
textSurface = TTF_RenderText_Solid(font, text.c_str(), color);
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
}
If you want to have reference semantics - that is, have a text
variable that you declare in your main, pass around to let other parts of your program modify it, and then have your main print whatever value is set, you could do something like the following:
#include <stdlib.h>
#include <stdio.h>
#include <string>
using namespace std;
void start(string& text );
int main() {
// Create an empty 'string' object', invoking the default constructor;
// This gives us a valid empty string object.
string text;
...
while(true) {
...
// The `text` variable is passed by reference.
start(text);
textSurface = TTF_RenderText_Solid(font, text.c_str(), color);
...
}
}
// Takes a string object by reference, and modifies it to fill it
// with our desired text.
void start( string& text ) {
text += "Hello";
text += " ";
text += "World";
}