SDL - Printing text like a console? - c++

I've got some code (below) that uses SDL_ttf and would like to:
Be able to render text (from the TTF) in alignment (to a buffer or array as well) like a console would (having each character printed in it's individual cell).
Be able to utilize a blinking cursor (possibly render and un-render an underscore, maybe?)
Be able to let the user input text from the keyboard and render each char on the screen right when it is typed (using SDLK_charhere).
Getting back to #1: I'm thinking about getting the width of the previous character printed on the screen (from the TTF) and using its width (in pixels) to print the next character right after the previous character, plus 2 pixels. <-- PLEASE TELL ME IF THE SPACING BETWEEN CHARS IN A REGULAR WIN32 CONSOLE IS A DIFFERENT SIZE IN PIXELS.
Here's the code that needs to be modified:
#include "include/SDL/SDL.h"
#include "include/SDL/SDL_ttf.h"
int currentX = 0;
int currentY = 0;
int newW;
int newH;
SDL_Surface* screen;
SDL_Surface* fontSurface;
SDL_Color fColor;
SDL_Rect fontRect;
SDL_Event event;
TTF_Font* font;
//Initialize the font, set to white
void fontInit(){
TTF_Init();
font = TTF_OpenFont("dos.ttf", 12);
fColor.r = 0; // 255
fColor.g = 204; // 255
fColor.b = 0; //255
}
//Print the designated string at the specified coordinates
void PrintStr(char *c, int x, int y){
fontSurface = TTF_RenderText_Solid(font, c, fColor);
fontRect.x = x;
fontRect.y = y;
SDL_BlitSurface(fontSurface, NULL, screen, &fontRect);
SDL_Flip(screen);
}
int main(int argc, char** argv)
{
// Initialize the SDL library with the Video subsystem
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
//Create the screen
screen = SDL_SetVideoMode(320, 480, 0, SDL_SWSURFACE);
//Initialize fonts
fontInit();
PrintStr("", 0, 0);
do {
// Process the events
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
// Escape forces us to quit the app
case SDLK_ESCAPE:
event.type = SDL_QUIT;
break;
default:
break;
}
break;
default:
break;
}
}
SDL_Delay(10);
} while (event.type != SDL_QUIT);
// Cleanup
SDL_Quit();
return 0;
}

This isn't a trivial thing to do, but it sounds like a good learning project! You're maybe going to need a few thousand lines of code rather than the few tens you have up there. Perhaps start by considering these questions and their answers.
Do you want a fixed width font, or a variable width font?
How can you buffer the rendered text in an intelligent way? (glyphs or lines for eg.)
How can you buffer the text itself in an intelligent way?
How can you translate key presses into text?
What is translating and driving all this?
All these will need to be considered along with the most important question of all:
Do I want to do this?
If you do do this, it will teach you a lot about programming, but it might not give you the best full screen console you're looking for.

Related

How can I get smoother rotation when space is pressed

I am making a game where a nozzle of a tank rotates around when space is pressed to shoot enemies. However, right in the beginning when the space is pressed, it seems to stop for a few milliseconds and then continues without any problems. How can I make it so that the rotations is smooth and consistent as soon as the space is pressed, right from the start? Here is a minimal reproducible example:
#include "SDL.h"
#include <iostream>
class Nozzle
{
public:
void draw(SDL_Renderer* renderer, int cx, int cy, int l)
{
float x = ((float)cos(angle) * l) + cx;
float y = ((float)sin(angle) * l) + cy;
SDL_RenderDrawLine(renderer, cx, cy, (int)x, (int)y);
}
void plusAngle(float a)
{
angle += a;
}
private:
float angle = 0.0f;
};
int main(int argc, char* argv[])
{
SDL_Window* window = SDL_CreateWindow("RGame", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1200, 600, false);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Event event;
Nozzle nozzle;
bool running = true;
while (running)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
running = false;
if (event.type == SDL_KEYDOWN)
{
if (event.key.keysym.sym == SDLK_SPACE)
nozzle.plusAngle(0.1f);
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
nozzle.draw(renderer, 600, 300, 70);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
if (event.type == SDL_KEYDOWN)
{
if (event.key.keysym.sym == SDLK_SPACE)
nozzle.plusAngle(0.1f);
}
This is not how you do controls in a game.
If you open a text editor and hold a key, you'll see one letter being typed, then, after a delay, a steady stream of the same repeated letter. And SDL does the same thing, it gives you fake repeated "key down" events in this manner. This is normally used for editing text, not for game controls. (Those repeated events are marked by event.key.repeat == true).
What you should do is to create something like bool space_key_down, set it to true when you get SDL_KEYDOWN for the space key, and to false when you get SDL_KEYUP for the same key. Then, outside of the event loop, if the variable is set, you rotate your nozzle.
Or you can use SDL_GetKeyboardState. SDL does this thing automatically for every key, and you can access the list of flags it maintains using this function.
Also, while we're at it, you normally don't want to use keycodes (.sym == SDLK_SPACE) for game controls. Prefer scancodes (.scancode == SDL_SCANCODE_SPACE). The difference only becomes apparent on exotic layouts (e.g. AZERTY): keycodes represent the letters printed on the keycaps, while scancodes represent physical key locations. For example, on AZERTY you want to use ZQSD instead of WASD. If you use scancodes, it will happen automatically (SDL_SCANCODE_W will represent Z, and so on).
scaling rotation with frame-rate isn't really what I am looking for. Its a different problem
You need to solve this problem too. If you don't want the rotation speed to depend on FPS (bad thing), you must either mutliply the rotation angle by the frame length (it works, but it's easy to make mistakes this way), or make sure your game logic runs the fixed amount of times per second regardless of the FPS (I prefer this solution). See Fix Your Timestep!.

cannot display texture in SDL 2

I'm trying to display a texture onto the screen but all I'm getting is a black window.
No SDL Errors are being reported. There's a good chance that I'm missing something stupid, but I can't see it. Hopefully another set of eyes will help. Feel free to ask for more code/info.
main.cpp
SDL_Window * window;
SDL_Renderer * renderer;
SDL_Texture * grass;
SDL_Rect g_dst;
SDL_Event event;
Game app;
SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow("tmp", 100, 100, 640, 480, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
g_dst.x = g_dst.y = 0;
g_dst.w = 640;
g_dst.h = 480;
grass = IMG_LoadTexture(renderer, "grass.bmp");
while (app.isRunning()) {
app.pollEvents(&event);
app.render_init();
app.render(grass, NULL, &g_dst);
app.render_end();
}
//SDL_Quit() is handled by the Game class' destructor
Game.cpp
//Only functions used for rendering are shown
void render_init(Uint8 red=0, Uint8 green=0, Uint8 blue=0, Uint8 alpha=255)
{
SDL_SetRenderDrawColor(renderer, red, green, blue, alpha);
SDL_RenderClear(renderer);
}
void render(SDL_Texture * texture, SDL_Rect * src, SDL_Rect * dest) {
SDL_RenderCopy(renderer, texture, src, dest);
}
void render_end() { SDL_RenderPresent(renderer); }
First of all, you're initializing everything? please don't do that frequently, mind you that you're also initializing MANY unnecessary stuffs like for game controllers, etc. if the app gets bigger then the efficiency and the possibility of this app running at a smoot speed is at stake.
I also noticed that you are declaring variables in the .cpp file, do that in the header file and just recall the header to the cpp file that will be using it.
You want to render the grass right? and render it as much as the screens size.
(I'll just assume that you used this in the game.cpp part, which is the very first file, thus, not regarding any classes made)
int winWidth = 680; //The reason for this is just in case you make the window resizable
int winHeight = 480; //then the texture would also resize along with the window
SDL_Window *window = window = SDL_CreateWindow("The Space Project", 100, 100, winWidth, winHeight, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = NULL; //I've set this to NULL so that we can know if
the reason as to why your image is not rendering is because the renderer is not properly working.
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED);
if(renderer == NULL)
{
cout >> "Renderer is not working" >> endl;
//This shows a line at the command prompt that your renderer doesn't have any output, thus, only having a NULL as an equivalent
}
SDL_Texture* grass= NULL;
grass= IMG_LoadTexture(renderer, "grass.bmp"); //As you can see, I've set the grass to Null again
if(grass == NULL)
{
cout >> "Grass have failed to initialize" >> endl;
/*I don't normally do this but it's very important if you really need trouble shooting guides
but this time, were here to check IF the grass.bmp entered the SDL_Texture grass, so if the system can't find the .bmp file then it would show this error
since the grass (SDL_Texture) still doesn't have anything inside it (NULL)*/
}
SDL_Rect grass_rect;
grass_rect.x = 0;
grass_rect.y = 0;
grass_rect.w = winWidth;
grass_rect.h = winHeight;
//Loop part, I'll skip some of it
while (!quit && mainEvent->type != SDL_QUIT) //!quit is just an imaginary Boolean I've typed)
{
SDL_PollEvent(mainEvent); //Let's say you created the event already
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, grass, NULL, &grass_rect);
//The NULL part is also similar to a rect but it's a limiting type, we didn't assign anything to it
since I assumed that you wanted the whole image to be rendered
SDL_RenderPresent(renderer);
}
I revised the code and made it more efficient since your code called for useless extras which might result in lower performance.
I also notice that you tried calling for color changes?
use this
SDL_SetTextureColorMod(texture, red-value, green-value, blue-value);
and put it in the loops part under the render present of the same texture.
SDL_SetTextureColorMod(grass, 250, 250, 250);
Doing this would set all color values to 250, thus, having a white color, this change your texture color to white.
You're also wasting space on making the app.is running(), you could easily replace it with a boolean, which consumes much less space or you could omit it if you don't have an exit button inside the application and just make your loop read the SDL_QUIT, this saves space for the file, mind the efficiency.
If this still doesn't work then try replacing the image your using, make a simple one on paint name it something like "grass.png" or anything then try it again.
Don't forget to put the file in the proper folder, in the DEBUG folder if you haven't specified a folder, and also put it in the app folder so it would also read it when it executes as an .exe file and not as part of the debug command.

How to update text in a loop?

I'm basically a beginner in C++ but was looking into how to update a string that is in a while loop?
Currently every iteration of the loop it writes the text on top of the previous still its basically a blur of white colour.
This is my loop that im testing with:
while(!quit){
while( SDL_PollEvent( &event ) ){
switch(event.type){
case SDL_QUIT: quit = true; break;
case SDL_MOUSEMOTION: handle_mouse_position();
}
}
SDL_Rect offset;
offset.x = 400;
offset.y = 290;
std::stringstream s;
s << "Mouse xpos: " << mouseX << " Mouse ypos: " << mouseY;
font_surface = TTF_RenderText_Solid(font,s.str().c_str(),font_color);
SDL_BlitSurface(font_surface, NULL, screen, &offset);
//Update the screen
if( SDL_Flip( screen ) == -1 ) {
return 1;
}
}
Is there some way to clear the previous text output and update it each loop so that it will display mouse position clearly?
You are not clearing the screen between redraws, so it just paints over the already painted text (or anything), and it becomes "blur of white" as you say.
For example, try fillrect on the screen every frame.
Extra note: it seems you are not freeing the font_surface --> memory leak.
You can do that redrawing everything each time, or if your background is a solid color, paint a rectangle and draw the text above it.
The second one is more efficient. But if you have a complex scenario it is better to work with layers. First you render the bottom layer, then the second, and so on... By layers I mean, you can have a background() which draws the background and foreground() which, obviously, draws the foreground. So what you will have is something like:
while (main_loop) {
background();
foreground();
}
So you can easily handle more complex scenarios.

SDL loading my image messed up

I'm attemting to load an image that I exported from flash CS3 it's a very cute face but it loads very weird it loads on a blueish way this is the code for the two files:
//main.cpp
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include "test.hpp"
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO);
// Activamos modo de video
screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE | SDL_DOUBLEBUF);
image = IMG_Load("face.bmp");
dest.x = 200;
dest.y = 200;
//Main Loop
while(Abierto)
{
//We Draw
Draw();
//Events
while( SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)
Abierto = false;
}
}
// We free the image
SDL_FreeSurface(image);
SDL_Quit();
return 0;
}
Now the other one the;
//test.hpp
DL_Surface *image = NULL, *screen = NULL;
SDL_Rect dest;
SDL_Event event;
bool Abierto = true;
float PlaneX = 300, PlaneY = 200;
float velX = 0.1, velY = 0.1;
void Draw()
{
Uint32 color;
// Black Background is created
color = SDL_MapRGB (screen -> format, 0, 0, 0);
SDL_FillRect (screen, NULL, color);
SDL_DisplayFormatAlpha(image);
SDL_BlitSurface(image, NULL, screen, &dest);
// Flip the working image buffer with the screen buffer
SDL_Flip (screen);
}
I need help with this please Im not that experienced on SDL stuff oh and if you want to take a closer look I uplaoded the project here.
Oh my bad I must add the image is 32 pixels with alpha according to flash exporting options
According to docs, SDL_DisplayFormatAlpha returns a new image and keeps the original intact.
So, try in the first part, when you load the image:
SDL_Surface *origImage = IMG_Load("face.bmp");
image = SDL_DisplayFormatAlpha(origImage);
SDL_FreeSurface(origImage)
As there is no need to call SDL_DisplayFormatAlpha each frame.
Then in the second part, just blit image, without calling SDL_DisplayFormatAlpha.
UPDATE
I've just checked your picture, and it looks like it is a weird bmp. I've seen that before: BMP format is such a mess that if you don't keep to the basics chances are that different programs will interpret the data differently.
In your case:
display face.bmp shows correctly.
gthumb face.bmp shows nothing.
eog face.bmp says "bogus header data".
I strongly recommend using PNG files for all your game cartoon-like pictures and JPG for all the photo-like ones.
So run
$ convert face.bmp face.png
And use the PNG file. I'll will work better and you will have a file 20% the size of the original.

C++ Semi-Transparent Window SDL

I wish to have a semi-transparent SDL background (nothing to do with sub-surfaces or images), such that instead of having a black background it is actually transparent, but the other things I draw are not. My current code is a slightly modified copy of Code::Blocks' SDL project, similar to how various applications have rounded borders or odd shapes besides rectangles.
#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif
int main ( int argc, char** argv )
{
putenv("SDL_VIDEO_WINDOW_POS");
putenv("SDL_VIDEO_CENTERED=1");
// initialize SDL video
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "Unable to init SDL: %s\n", SDL_GetError() );
return 1;
}
// make sure SDL cleans up before exit
atexit(SDL_Quit);
// create a new window
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 16,
SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_NOFRAME);
if ( !screen )
{
printf("Unable to set 640x480 video: %s\n", SDL_GetError());
return 1;
}
// load an image
SDL_Surface* bmp = SDL_LoadBMP("cb.bmp");
if (!bmp)
{
printf("Unable to load bitmap: %s\n", SDL_GetError());
return 1;
}
// centre the bitmap on screen
SDL_Rect dstrect;
dstrect.x = (screen->w - bmp->w) / 2;
dstrect.y = (screen->h - bmp->h) / 2;
// program main loop
bool done = false;
while (!done)
{
// message processing loop
SDL_Event event;
while (SDL_PollEvent(&event))
{
// check for messages
switch (event.type)
{
// exit if the window is closed
case SDL_QUIT:
done = true;
break;
// check for keypresses
case SDL_KEYDOWN:
{
// exit if ESCAPE is pressed
if (event.key.keysym.sym == SDLK_ESCAPE)
done = true;
break;
}
} // end switch
} // end of message processing
// DRAWING STARTS HERE
// clear screen
SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
// draw bitmap
SDL_BlitSurface(bmp, 0, screen, &dstrect);
// DRAWING ENDS HERE
// finally, update the screen :)
SDL_Flip(screen);
} // end main loop
// free loaded bitmap
SDL_FreeSurface(bmp);
// all is well ;)
printf("Exited cleanly\n");
return 0;
}
I think what you're trying to do is in fact a shaped window (parts of the window are transparent depending on a mask that you provide). It seems there's no way to do that with SDL 1.2, however there is a SDL_SetWindowShape function just for this in SDL 1.3 for which you can find a pre-release snapshot here but it's not even in beta yet so I suggest waiting until it's officialy released :)
this is a link to a pretty neat article about development of an older application for Mac OS 9, which did not have support for shaped windows, either.
It's actually a neat article in general about software development.
But the idea seems pretty smart, and I wonder if you might be able to get it working here, too. Instead of trying to make a transparent background, they actually take a screen-shot of the computer right where their window is going to go, and then use that screen shot for their background. When the user drags the window around on the screen, they continue to update the background with new screen-shots. I think this might be more complicated than you were hoping for, but it's certainly an interesting idea.