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.
Related
I am building a 6502 emulator, and I wish to represent the cpu and memory states visually.
I am using SDL2 for this purpose. I have to render text on the SDL window as the 6502 cpu or memory changes states.
i.e I wish to show the entire memory content, current instruction being executed, previous cpu state, current cpu state in form of texts and numbers.
Here is my attempt to render a text using a font already present in my linux system.
Later I wish to render dynamic text and numbers instead of a static string.
#include<SDL2/SDL.h>
#include<SDL2/SDL_ttf.h>
#define SCREEN_HEIGHT 640
#define SCREEN_WIDTH 480
int quit=false;
SDL_Window *window;
SDL_Renderer *renderer;
int initializeDrawing(int argc,char** argv){
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
window = SDL_CreateWindow("6502 cpu display!", 100, 100, SCREEN_HEIGHT, SCREEN_WIDTH, SDL_WINDOW_SHOWN);
if (window == nullptr){
std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == nullptr){
SDL_DestroyWindow(window);
std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
if (TTF_Init() != 0){
SDL_Quit();
return 1;
}
return 0;
}
void loop(){
TTF_Font* Sans = TTF_OpenFont("./ttf/LH.ttf", 13);
SDL_Color White = {255,255,255};
SDL_Surface* surfaceMessage = TTF_RenderText_Solid(Sans, "0xABCEDFGHIJKLMNOPQRSTUVWXYZ", White);
SDL_Texture* Message = SDL_CreateTextureFromSurface(renderer, surfaceMessage);
SDL_Rect Message_rect;
Message_rect.x = 0;
Message_rect.y = 0;
Message_rect.w = surfaceMessage->w;
Message_rect.h = surfaceMessage->h;
//loop
SDL_Event e;
while(!quit){
SDL_PollEvent(&e);
//If user closes the window
if (e.type == SDL_QUIT){
quit = true;
}
//First clear the renderer
SDL_RenderClear(renderer);
//Draw the texture
SDL_RenderCopy(renderer, Message, NULL, &Message_rect);
//Update the screen
SDL_RenderPresent(renderer);
//Take a quick break after all that hard work
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
Here is the output
I wish to make the text more smaller and smoother.
I am looking for ideas on how to efficiently display numbers and text dynamically.
SDL2_ttf has several different text rendering modes.
You are using the mode Solid which the documentation describes as "Quick and Dirty":
There are three modes of rendering:
Solid: Quick and Dirty
Create an 8-bit palettized surface and render the given text at fast quality with the given font and color. The pixel value of 0 is the colorkey, giving a transparent background when blitted. Pixel and colormap value 1 is set to the text foreground color. This allows you to change the color without having to render the text again. Palette index 0 is of course not drawn when blitted to another surface, since it is the colorkey, and thus transparent, though its actual color is 255 minus each of the RGB components of the foreground color. This is the fastest rendering speed of all the rendering modes. This results in no box around the text, but the text is not as smooth. The resulting surface should blit faster than the Blended one. Use this mode for FPS and other fast changing updating text displays.
Shaded: Slow and Nice, but with a Solid Box
Create an 8-bit palettized surface and render the given text at high quality with the given font and colors. The 0 pixel value is background, while other pixels have varying degrees of the foreground color from the background color. This results in a box of the background color around the text in the foreground color. The text is antialiased. This will render slower than Solid, but in about the same time as Blended mode. The resulting surface should blit as fast as Solid, once it is made. Use this when you need nice text, and can live with a box.
Blended: Slow Slow Slow, but Ultra Nice over another image
Create a 32-bit ARGB surface and render the given text at high quality, using alpha blending to dither the font with the given color. This results in a surface with alpha transparency, so you don't have a solid colored box around the text. The text is antialiased. This will render slower than Solid, but in about the same time as Shaded mode. The resulting surface will blit slower than if you had used Solid or Shaded. Use this when you want high quality, and the text isn't changing too fast.
If you want a higher quality rendering, you should try the *_Shaded or *_Blended functions.
Also note that you almost certainly want to use TTF_RenderUTF8 family of functions to make sure non-Latin characters are rendered correctly. (This may not be immediately relevant to your 6502 emulator, but it does not hurt and is good practice to do anyway.) More information: https://wiki.libsdl.org/SDL_ttf/TTF_RenderUTF8_Blended
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm trying to create a scribble clone for a uni project.
My lines are basically a lot of dots. If I draw too quick, the line breaks and I have single dots.
Also, I can't figure out how to draw lines ONLY when a mouse button is pressed.
I tried to put mouseMoved into a while loop until mouseButton is released but that didn't seem to work. I ended up in an infinite loop.
Here is my code so far:
while (window.isOpen())
{
while (window.pollEvent(event))
{
int mouseButtonX = event.mouseButton.x;
int mouseButtonY = event.mouseButton.y;
int mouseMoveX = event.mouseMove.x;
int mouseMoveY = event.mouseMove.y;
setBrushSize(5);
brush.setRadius(brushSize);
brush.setPosition(mouseMoveX - brushSize, mouseMoveY - brushSize);
brush.setFillColor(sf::Color::Transparent);
brush.setOutlineColor(sf::Color::Green);
brush.setOutlineThickness(2);
switch (event.type) {
case (sf::Event::Closed):
window.close();
break;
case (sf::Event::KeyPressed):
if (event.key.control && event.key.code == sf::Keyboard::X) {
cout << "closing";
window.close();
}
if (event.key.code == sf::Keyboard::R) {
cout << "printed";
brushColor = setBrushColor(255, 0, 0);
}
if (event.key.code == sf::Keyboard::G) {
brushColor = setBrushColor(0, 255, 0);
}
if (event.key.code == sf::Keyboard::B) {
brushColor = setBrushColor(0, 0, 255);
}
if (event.key.code == sf::Keyboard::C) {
for (int i = 0; i < points.size(); i++) {
points.clear();
}
it = 0;
}
break;
case(sf::Event::MouseButtonPressed):
points.push_back(point);
points[it].setRadius(brushSize);
points[it].setFillColor(brushColor);
points[it].setPosition(mouseButtonX - brushSize, mouseButtonY - brushSize);
it++;
cout << "drawPoint: Pressed X = " << mouseButtonX << " Y = " << mouseButtonY << endl;
break;
case(sf::Event::MouseMoved):
points.push_back(point);
points[it].setRadius(brushSize);
points[it].setFillColor(brushColor);
points[it].setPosition(mouseMoveX - brushSize, mouseMoveY - brushSize);
it++;
cout << "drawPoint: Moved X = " << mouseMoveX << " Y = " << mouseMoveY << endl;
break;
}
}
window.clear(sf::Color(255, 247, 204));
window.draw(SkechyT);
window.draw(close);
window.draw(brush);
window.draw(color);
window.draw(clear);
for (int i = 0; i < points.size(); i++) {
window.draw(points[i]);
}
//window.draw(point);
window.display();
}
}
int getBrushSize() {
return brushSize;
}
void setBrushSize(int num) {
brushSize = num;
}
sf::Color setBrushColor(int r, int g, int b) {
return sf::Color(r, g, b);
}
~Visualizer();
};
While you can modify a sf::VertexArray on the fly (basically building a vector drawing app), you can also use a sf::RenderTexture as an actual drawing canvas.
Considering you tried drawing lots of small points, I'd assume you're aiming for the latter. What's important here is the fact that you don't necessarily have to clear render textures between drawing calls and can therefore preserve whatever has been drawn before.
Combined with the original goal – drawing – this becomes very easy.
All you have to do is draw the changes (e.g. when moving the cursor), finalize the render texture (by calling display()), and then presenting it using any drawable (like sf::Sprite.
I've quickly scribbled together the following example, which should show the concept rather well (and you won't run into an endless loop other than the actual main loop):
#include <SFML/Graphics.hpp>
#include <vector>
int main(int argc, char **argv) {
sf::RenderWindow window(sf::VideoMode(800, 600), L"SFML Drawing – C to clear, PageUp/PageDown to pick colors", sf::Style::Default);
// Set a specific frame rate, since we don't want to
// worry about vsync or the time between drawing iterations
window.setVerticalSyncEnabled(false);
window.setFramerateLimit(100);
// First we'll use a canvas to basically store our image
sf::RenderTexture canvas;
canvas.create(800, 600);
canvas.clear(sf::Color::White);
// Next we'll need a sprite as a helper to draw our canvas
sf::Sprite sprite;
sprite.setTexture(canvas.getTexture(), true);
// Define some colors to use
// These are all with very low alpha so we
// can (over-)draw based on how fast we move the cursor
const std::vector<sf::Color> colors = {
sf::Color(255, 0, 0, 8),
sf::Color(255, 255, 0, 8),
sf::Color(0, 255, 0, 8),
sf::Color(0, 255, 255, 8),
sf::Color(0, 0, 255, 8),
sf::Color(255, 0, 255, 8)
};
// We'll need something to actually draw
// For simplicity, I'm just drawing a circle shape
// but you could also draw a line, rectangle, or something more complex
const float brush_size = 25;
sf::CircleShape brush(brush_size, 24);
brush.setOrigin(brush_size, brush_size); // Center on the circle's center
sf::Vector2f lastPos;
bool isDrawing = false;
unsigned int color = 0;
// Apply some default color
brush.setFillColor(colors[color]);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
switch (event.key.code) {
case sf::Keyboard::C:
// Clear our canvas
canvas.clear(sf::Color::White);
canvas.display();
break;
case sf::Keyboard::PageUp:
// Get next color
color = (color + 1) % colors.size();
// Apply it
brush.setFillColor(colors[color]);
break;
case sf::Keyboard::PageDown:
// Get previous color
color = (color - 1) % colors.size();
// Apply it
brush.setFillColor(colors[color]);
break;
}
break;
case sf::Event::Resized:
{
// Window got resized, update the view to the new size
sf::View view(window.getView());
const sf::Vector2f size(window.getSize().x, window.getSize().y);
view.setSize(size); // Set the size
view.setCenter(size / 2.f); // Set the center, moving our drawing to the top left
window.setView(view); // Apply the view
break;
}
case sf::Event::MouseButtonPressed:
// Only care for the left button
if (event.mouseButton.button == sf::Mouse::Left) {
isDrawing = true;
// Store the cursor position relative to the canvas
lastPos = window.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
// Now let's draw our brush once, so we can
// draw dots without actually draging the mouse
brush.setPosition(lastPos);
// Draw our "brush"
canvas.draw(brush);
// Finalize the texture
canvas.display();
}
break;
case sf::Event::MouseButtonReleased:
// Only care for the left button
if (event.mouseButton.button == sf::Mouse::Left)
isDrawing = false;
break;
case sf::Event::MouseMoved:
if (isDrawing)
{
// Calculate the cursor position relative to the canvas
const sf::Vector2f newPos(window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y)));
// I'm only using the new position here
// but you could also use `lastPos` to draw a
// line or rectangle instead
brush.setPosition(newPos);
// Draw our "brush"
canvas.draw(brush);
// Finalize the texture
canvas.display();
break;
}
}
}
// Clear the window
window.clear(sf::Color(64, 64, 64));
// Draw our canvas
window.draw(sprite);
// Show the window
window.display();
}
return 0;
}
Once running, you can start drawing using the left mouse button. C will clear the canvas and Page Up and Page Down allow you to pick a different color:
Edit: And just to mention it, in the example above, rather than drawing a circle, you can just draw a sf::VertexArray with sf::Lines and two vertices: lastPos and newPos. This way you'll always draw a continuous line. (But you'd obviously have to save/update lastPos with the value of newPos once you're done.)
Well, I've been watching a tutorial on how to use SFML. I'm currently learning to move a sprite in the screen. Before adding window.clear(); every time I moved the sprite it left like a trail, like if the sprite was a brush. Then the tutorial man said to add window.clear BEFORE window.draw(player);
Could you please explain the logic behind that? Like, the window gets cleared, then draws the character and the displays it. Here is the code:
#include <SFML/Graphics.hpp>
#include <iostream>
int main() {
sf::RenderWindow window(sf::VideoMode(1920, 1080), "Screen", sf::Style::Default);
sf::RectangleShape player(sf::Vector2f(100.0f, 100.0f));
player.setFillColor(sf::Color::Green);
//run as long as the window is open
while (window.isOpen()) {
// check all the window's events that were triggered since the last iteration of the loop
sf::Event evnt;
while (window.pollEvent(evnt)) {
switch (evnt.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::Resized:
printf("New window width: %i New window height: %i\n", evnt.size.width, evnt.size.height);
break;
case sf::Event::TextEntered:
if (evnt.text.unicode < 128) {
printf("%c", evnt.text.unicode);
}
}
// "close requested" event: we close the window
if (evnt.type == sf::Event::Closed)
window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)){
player.move(0.0f, -0.1f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) {
player.move(-0.1f, 0.0f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) {
player.move(0.0f, 0.1f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) {
player.move(0.1f, 0.0f);
}
window.clear();
window.draw(player);
window.display();
}
return 0;
}
The logic behind sf::RenderWindow::clear() is actually quite simple. The reason you see a trail behind the sprite without clear is because you redraw the sprite again without getting rid of the old one. Clearing the screen gets rid of anything that was already on the screen, so you end up with a blank canvas to redraw everything on in its updated position. The character, which is your sprite, isn't actually moving, it is constantly getting redrawn in a new position on the window.
I'm new to game development, SDL and C++. I have been learning with the code here:
http://gamedevgeek.com/tutorials/managing-game-states-in-c/
The relevant bit:
Multiple states are not only important in demos, but also in games in general. Every game starts off in an introduction state, then moves to a menu of some kind, a finally play begins. When you’re finally defeated, the game moves to a game-over state, usually followed by a return to the menu. In most games it is possible to be in more than one state at a time. For example, you can usually bring up the menu during game play.
My question is: To have multiple states display at once, such as displaying a menu on top of game play, must each state have it's own Renderer?
You pass an Image.png*(or other format) onto a Texture, Then you place the Texture on a "surface"(you can clip the texture with this) which is then passed onto a Renderer. So, all you have to do is change the clip and texture, and pass it to the Renderer In the !RIGHT ORDER!
Example: You would Render the background first, and then Sprites, and then Effects, etc...
I hope this helps.
BELOW CODE WAS TAKEN FROM LAZY FOO WEBSITE!! CHECK IT OUT VERY USEFULL TO BEGIN SDL2
http://lazyfoo.net/tutorials/SDL/07_texture_loading_and_rendering/index.php
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
}
//Clear the last frame
SDL_RenderClear( gRenderer );
//Render texture to screen
SDL_RenderCopy( gRenderer, gTexture1, NULL, NULL );
SDL_RenderCopy( gRenderer, gTexture2, NULL, NULL );
SDL_RenderCopy( gRenderer, gTexture3, NULL, NULL );
SDL_RenderCopy( gRenderer, gTexture4, NULL, NULL );
//Update screen
SDL_RenderPresent( gRenderer );}
as you can see in the above CODE the SDL_RenderCopy uses the SAME renderer for RENDERING different TEXTURES. so what you need, is, many textures.
I'm certain there might be a use for multiple renderers but I have no idea why you would do that?
//the next day//
So I checked this out, and Saw that if you have a Multiple Window Application you Can use Multiple renderers.
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.