Printing a 2d array in sfml from a text file - c++

Im trying to print out a 2d array of characters that is from a text file, into a renderwindow using the window.draw() function. However, everything seems to work perfectly fine, except that when printing its skipping exactly one character.
For instance if i had a line of 15 chars, it will only print 8,but when i print it on the terminal it prints it perfectly fine, I simply dont know why its behaving like that, tried several things like changing the text file itself, or changing the size. Nothing seems to work for me. any ideas or help is very much appreciated.
here is the part of the code:
void Level::printgrid(int level)
{
sf::RenderWindow window(sf::VideoMode(800, 600), "Game On");
sf::Font MyFont;
if (!MyFont.loadFromFile("tnr.ttf"))
{
// Error...
}
ifstream my_file("nn.txt"); //text that i will be reading from
char number;
int i = 0; //to help loop
int j = 0;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
else
{
window.clear();
while (i<row) // ive tried 2 for loops that didnt work cause
{ // it kept re-entering those loops
while (j<col)
{
my_file.get(grid[i][j]); //getting the chars one by one
cout << grid[i][j]; // printing them on terminal
//so i can check its correct
//changing to text that is drawable to print in window
sf::Text text(grid[i][j], MyFont, 30);
text.setPosition(0 + (30 * i), 0 + (30 * j));
// drawing it in window
window.draw(text);
window.display();
j++;
}
my_file.get(number);
cout << endl;
i++;
j = 0;
}
}
}
}
}

There are a few issues with your code, but the most apparent problem is the fact that you're always drawing only one character, then presenting the result (which will flip buffers).
Due to this you're basically alternating characters between the two buffers (since SFML is double buffering).
In addition, you're continually reading from your file, recreating the characters/text elements, etc. which is not very effective and should fail once you reach the end of the file.
For some actual game or program, you should work on optimizing the drawing (drawing 10 characters at once as one sf::Text is faster than drawing 10 individual sf::Text objects). I'll be ignoring this for simplicity for now.
The following is just a quick (untested) example to show you the basic idea.
// First I'd use some container to store our `sf::Text` objects
// This will just create all the objects
std::vector<sf::Text> chars(rows * cols);
// Now let's read the characters and add to our vector
for (std::size_t y = 0; y < rows; ++y) {
for (std::size_t x = 0; x < cols; ++x) {
// Just an alias to simplify the following lines
sf::Text &text = chars[y * cols + x];
// Now just set up everything
text.setFont(my_font);
text.setFillColor(sf::Color::White);
text.setCharacterSize(30);
text.setPosition(30 * x, 30 * y);
text.setString(my_file.get());
}
}
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
// Just your usual event handling
}
window.clear();
// Iterate over all elements and draw them
for (const auto &text : chars)
window.draw(text);
window.display();
}
If you're trying to create some kind of graphical console, you should put all characters into some sf::VertexArray or potentially render them using a shader.

Related

Why is this code taking up so much memory?

I ran into this problem when I was trying to draw each pixel in the window separately, so here is the simpler version of my problem. When I run this code, the process memory starts to increase rapidly until it reaches 365 MB and stops increasing. Why is this taking up so much memory and what can I do to fix this issue?
#include <SFML/Graphics.hpp>
int main() {
sf::RenderWindow window{ sf::VideoMode{ 1024, 960 }, "Pixels", sf::Style::Close };
while (window.isOpen()) {
sf::Event sfmlEvent;
while (window.pollEvent(sfmlEvent)) {
if (sfmlEvent.type == sf::Event::Closed) {
window.close();
}
}
window.clear();
for (int i = 0; i < window.getSize().x; ++i) {
for (int j = 0; j < window.getSize().y; ++j) {
sf::RectangleShape rect{ { 1.0f, 1.0f } };
rect.setPosition(i, j);
window.draw(rect);
}
}
window.display();
}
return 0;
}
What you are doing is creating a new sf::RectangleShape for every pixel every frame. The sf::RectangleShape class has 4 floating point numbers for the x, y, width, and height. The total amount of rectangles created every frame would be 983,040, and each would consist of four floats, meaning that you are creating close to 4 million floats every frame taking up a TON of memory. I'm not sure what it is your trying to do exactly, but if you want precise control over individual pixels, you should be doing that on the GPU using OpenGL and maybe some shaders.

SFML Vertical Sync is not supported

I trying to implement a wator simulation were sharks eat fishes, I want to randomly spawn sharks, the program compiles but i get "Setting Vertical Sync not supported" .
working on Ubuntu 16.04. Before i was working on something else and i got the same error but the window was displayed this is not. Any help?
EDIT I have fixed up the code i had one too many { in my loop but now i am getting a "Segmentation Fault (Core dumped)" Error i have changed my png to 8 bit but that didnt help.
#include <SFML/Graphics.hpp>
int main()
{
int n;
int x;
int y;
sf::RenderWindow window(sf::VideoMode(800, 800), "SFML works!");
// Set Frame Rate to 60fps
window.setFramerateLimit(60);
srand(time(0));
sf::Texture shark;
shark.loadFromFile("image.png");
std::vector<sf::Sprite> Fishes(n,sf::Sprite(shark));
for (int n = 0; n < Fishes.size(); n++){
Fishes[n].setOrigin(15, 15);
Fishes[n].getPosition();
Fishes[n].setPosition(x = rand() % 790 + 10, y = rand() % -10 - 50);
}
// run the program 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 event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
Fishes[n].setPosition(x, y+=1);
Fishes[n].rotate(1);
// clear the window with black color
window.clear(sf::Color::Black);
// draw everything here...
// window.draw(...);
window.draw(Fishes[n]);
// end the current frame
window.display();
}
return 0;
}
I would add a comment but I lack reputation. A segfault is caused writing or reading from illegal memory. In your case, I would try checking to see if your image is being loaded properly.
I would also note for loops with only one line in the body don't use just one curly bracket, use both or none.
You are still missing a loop over your fishes in your rendering loop.
Your first loop sets your loop variable n to the number after your last fish. Using that same loop variable again will result in undefined behavior. Fix it. Probably by adding another for loop where you use n the second time in your while rendering-loop.
Specifically, when you come to this line:
Fishes[n].setPosition(x, y+=1);
you variable n is not any kind of loop variable. Even worse it's totally random, you have not set any value. It's int n; from the first line after main(). If you delete that line (the first after main), you will see what is wrong.

How to draw SFML shapes stored in a vector

I'm making a copy of the game "Asteroids" using SFML.
To store all my asteroids I use a std::vector<sf::ConvexShape> vector, which stores all of the asteroid shapes. The problem is drawing the shapes from the vector. I looked at this post and saw that I could use an iterator to draw my shapes (I know he used sprites in that post but I presume it makes no difference if I were to use shapes.)
So I tried that:
for(std::vector<sf::ConvexShape>::iterator it=allShapes.begin();it!= allShapes.end(); ++it){
window.draw(*it);
}
And that throws an exception, terminating my program.
FYI: above, allShapes contains sf::ConvexShape shapes.
So the question is: How do I draw shapes that I store in a vector?
Full sauce:
using namespace std;
class asteroid{
public:
sf::Vector2f pos;
double angle;
void update(sf::Vector2f);
void create(std::vector<sf::ConvexShape>);
};
/* Will be implemented later
void asteroid::update(sf::Vector2f a){
pos += a;
};
*/
void asteroid::create(std::vector<sf::ConvexShape> a){
cout << "Creating..." << endl;
a.push_back(sf::ConvexShape()); //New asteroid SHAPE
std::vector<sf::ConvexShape>::iterator tempIt = a.end();
tempIt->setPointCount(4);
for(int i = 0; i < tempIt->getPointCount()+1; i++){ //Drawing asteroid
tempIt->setPoint(i, sf::Vector2f(i*100, i*100));
}
tempIt->setFillColor(sf::Color::White);
cout << "Done!" << endl;
};
int main()
{
// Init //
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
std::vector<sf::ConvexShape> allShapes; //List of all asteroid SHAPES
std::vector<asteroid> allAsteroids; //List of asteroid CLASS OBJECTS
allAsteroids.push_back(asteroid()); //New asteroid CLASS OBJECT
for(std::vector<asteroid>::iterator it = allAsteroids.begin(); it != allAsteroids.end(); ++it){ //Creating asteroids
it->create(allShapes);
}
// Loop //
while (window.isOpen())
{
// Event //
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
// Display //
window.clear();
for(std::vector<sf::ConvexShape>::iterator it = allShapes.begin(); it != allShapes.end(); ++it){
window.draw(*it);
}
window.display();
}
return 0;
}
You have a few problems here...
First:
std::vector<sf::ConvexShape>::iterator tempIt = a.end();
tempIt->setPointCount(4);
end() does NOT give you an iterator to the last element in the vector - it gives you an iterator to the "element" just past it, and is used for bounds checking (e.g. in a for loop). In other words it does not point to valid data, so the second line is invalid. There are many approaches you could use here; the following two are "most correct" though:
//with iterators using a.end() - 1
std::vector<sf::ConvexShape>::iterator tempIt = a.end() - 1;
tempIt->setPointCount(4);
//with back, accessing the element directly
a.back().setPointCount(4);
Secondly:
for(int i = 0; i < tempIt->getPointCount()+1; i++)
The asteroid has four points, so getPointCount()+1 = 5. This means that the loop executes for i = 0, 1, 2, 3, and 4 - one too many times.
Thirdly:
tempIt->setPoint(i, sf::Vector2f(i*100, i*100));
This line runs once for each i. Assuming you make the change above, this results in the points (0,0), (100, 100), (200, 200), and (300, 300). This is a line, not a convex shape. You will need to revise your formula. Setting the points directly is also a fine option, as there are only 4 to set.
You also may want to pass the vector by reference to avoid making any unnecessary copies:
void create(std::vector<sf::ConvexShape>& a){
//...
}

SFML 2.1 Full Explanation of using Vectors to create multiple Sprites

I am using SFML 2.1 in Code Blocks and I can't figure out how to use Vectors to make clones of my asteroid sprite. It keeps saying that asteroid_V hasn't been declared, and a warning box pops up saying it is "using characters that are illegal in the selected coding" and that they "were changed to protect [me] from losing data".
The objective of this program is to continuously create asteroid sprites that will spawn at random points above the screen before dropping straight down. There were other sprites and aspects in the program but I removed them from this post to properly condense it. This appears to be the only problem after all.
int n;
int main()
{
RenderWindow window;
window.setFramerateLimit(30);
RenderWindow mainMenu;
srand( time(0));
Texture asteroid_Pic;
asteroid_Pic.loadFromFile("Asteroid.png");
std::vector<sf::Sprite> asteroid(n, Sprite(asteroid_Pic));
for (int i = 0; i < asteroid.size(); i++){
asteroid[n].setOrigin(15, 15);
asteroid[n].getPosition();
asteroid[n].setPosition(x = rand() % 790 + 10, y = rand() % -10 - 50);
}
// run the program 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
Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == Event::Closed){
window.close();
}
asteroid[n].setPosition(x, y+=1);
asteroid[n].rotate(1);
// clear the window with black color
window.clear(Color::Black);
// draw everything here...
// window.draw(...);
window.draw(player1);
window.draw(asteroid[n]);
// end the current frame
window.display();
}
return 0;
}
You have another while (window.isOpen()) inside your main loop. Your program will enter the main loop and then never get out of that inner loop. It will never get to drawing at least once.
You need to get rid of the inner while (window.isOpen()) loop and find another way.
Although the original question was about timers, you can find a basic explanation of a game loop here. You have to handle time in you loop if you want to do something (move sprites, create new ingame entities) based on time.

SFML 2.0 Looping a sprite to display more than once

I have asked a similar question in the past but I still can't get my head around this. I am doing an invaders game based on SFML 2.0. So far I have one sprite sheet which runs through using my clock. This part works just fine:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <string>
int spriteWalkSpeed = 10;
int DownspriteWalkSpeed = 5;
int up=-spriteWalkSpeed, down=DownspriteWalkSpeed, left=-spriteWalkSpeed, right=spriteWalkSpeed;
int xVelocity =0, yVelocity=0;
const int SC_WIDTH=800;
const int SC_HEIGHT= 600;
const float REFRESH_RATE =0.01f; //how often we draw the frame in seconds
const double delay=0.1;
const int SPRITEROWS=1; //number of ROWS OF SPRITES
const int SPRITECOLS=2;//number of COLS OF SPRITES
std::string gameOver = "Game Over";
int main()
{
// Create the main window
sf::RenderWindow App (sf::VideoMode(SC_WIDTH, SC_HEIGHT, 32), "Space Invaders!",sf::Style::Close );
// Create a clock for measuring time elapsed
sf::Clock Clock;
//background texture
sf::Texture backGround;
backGround.loadFromFile("images/background.jpg");
sf::Sprite back;
back.setTexture(backGround);
//load the invaders images
sf::Texture invaderTexture;
invaderTexture.loadFromFile("images/invaders.png");
sf::Sprite invadersSprite(invaderTexture);
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
int invadersWidth=invaderTexture.getSize().x;
int invadersHeight=invaderTexture.getSize().y;
int spaceWidth=invadersWidth/SPRITECOLS;
int spaceheight=invadersHeight/SPRITEROWS;
//Sprites
sf::IntRect area(0,0,spaceWidth,spaceheight);
invadersSprite.setTextureRect(area);
invadersSprite.setPosition(30, NULL);
App.setKeyRepeatEnabled(false);
//Collision detection
// Start game loop
while (App.isOpen())
{
// Process events
sf::Event Event;
while (App.pollEvent(Event))
{
// Close window : exit
if (Event.type == sf::Event::Closed)
App.close();
}
// Create an array of 10 sprites (cannot initialise them with textures here)
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setPosition(30,0);
if(Clock.getElapsedTime().asSeconds()>REFRESH_RATE)
{
//carry out updating tasks
static float spriteTimer=0.0; //keep track of sprite time
spriteTimer+=Clock.getElapsedTime().asSeconds();
static int count=0; //keep track of where the sub rect is
if(spriteTimer>delay)
{
invaderSprites[i].setTextureRect(area);
++count;
invaderSprites[i].move(xVelocity, yVelocity);
if(count==SPRITECOLS) //WE HAVE MOVED OFF THE RIGHT OF THE IMAGE
{
area.left=0; //reset texture rect at left
count=0; //reset count
}
else
{
area.left+=spaceWidth; //move texture rect right
}
spriteTimer=0; //we have made one move in the sprite tile - start timing for the next move
}
Clock.restart();
}
App.draw(back);
App.draw(invaderSprites[i]);
// Finally, display the rendered frame on screen
App.display();
}
}
return EXIT_SUCCESS;
}
The issue I am having is that the sprite only shows once, not 10 times (as the for loop states)
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
// Loop over the elements of the vector of sprites
for (int i = 0; i < invaderSprites.size(); i++)
{
invaderSprites[i].setPosition(30, NULL);
}
// Create an array of 10 sprites (cannot initialise them with textures here)
sf::Sprite invaderSprites[10]; // Loop over each sprite, setting their textures
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setTexture(invaderTexture);
}
I am pretty sure it has something to do with the app drawing invadersSprite whereas the loop is setup for invaderSprites. Even just a little insight into what is going wrong would be such a big help.
I am pretty sure it has something to do with the app drawing
invadersSprite whereas the loop is setup for invaderSprites.
Yes, that certainly has something to do with it. You need to call App.draw(...) for each sprite that you want to draw. You're not calling it for any of the sprites in your vector. For that, you would want a loop:
for (int i=0; i<invaderSprites.size(); ++i)
App.draw(invaderSprites[i]);
There are other problems though. For example, why are you declaring an array of sprites called invaderSprites, when you already have a vector of sprites with that same name? The latter hides the former once it is declared.
Another thing is that you are setting all the sprites to the same position, so even if if you do manage to draw them all, they will all be in the same spot, and as such they will not appear as separate objects.