Moving Sprite (SFML) in c++ using Loops - c++

I am new to c++ and as well as SFML. I am trying to make my sprite object move down in position relative to its last position using a loop. I am looking for the animation of it sprite object falling when the program starts.
I thought implementing a the sleep function in my for loop would help solve the issue i was having where the program would just display the object at the last iteration of the loop. However my program just freezes and crashes.
Looking for some direction. Maybe the sleep function isn't the right thing to call here?
#include <SFML/Graphics.hpp>
#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
// Create the window here. Calling out the dimensions
sf::RenderWindow window(sf::VideoMode(800, 600), "Example Window");
// run the program as long as the window is open
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
//close window we requested
if (event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear(sf::Color::Black);
sf::Texture texture;
if (!texture.loadFromFile("c:\\abstract.png"))
{
cout<<"Failed to load image...";
}
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setTextureRect(sf::IntRect(20,20,30,30));
for (float i = 0; i < 30.; i++)
{
sprite.move(sf::Vector2f(5.f, i));
window.draw(sprite);
Sleep(50);
}
window.display();
}
return 0;
}

What you are doing in your for is : Processing, drawing, processing, drawing... And finally displaying what you've drawn using window.display().
Meaning that what will be displayed on your window every frames, is the result of your "Processing, drawing" thing, in other word, 30 times your sprite at different positions.
What you want is to move your sprite a bit every frames. Thus, you have to finish your current while (window.isOpen()) iteration to move your sprite, draw it, and display it, and this over and over.
What you should do is declaring your sprite outside of your game loop (Which is while (window.isOpen())), and move it in this loop.
Step by step, your program should look like:
[Start]
Initialize your context
Create a sprite
Start looping
Clear the screen
Collect inputs
Move your sprite
Draw your sprite
Display your drawing on the window
End looping
[Exit]
The last thing you will need to handle is deltaTime (The timestep). Because if you move your sprite from (x,y) every frames, it means that the faster your computer is (Able to render a lot of frames quickly), the faster your sprite will move. In order to fix this problem, you'll have to move your sprite considering the time elapsed between the current frame and the previous frame (The slower is your PC, the more your sprite will move in one frame, the faster is your PC, the less your sprite will move in one frame). Timestep will cause your sprite to move (x,y) per second instead of (x,y) per frame, which is what you want in most graphic applications.

Related

How is SFML so fast?

I need to draw some graphics in c++, pixel by pixel on a window. In order to do this I create a SFML window, sprite and texture. I draw my desired graphics to a uint8_t array and then update the texture and sprite with it. This process takes about 2500 us. Drawing two triangles which fill the entire window takes only 10 us. How is this massive difference possible? I've tried multithreading the pixel-by-pixel drawing, but the difference of two orders of magnitude remains. I've also tried drawing the pixels using a point-map, with no improvement. I understand that SFML uses some GPU-acceleration in the background, but simply looping and assigning the values to the pixel array already takes hundreds of microseconds.
Does anyone know of a more effective way to assign the values of pixels in a window?
Here is an example of the code I'm using to compare the speed of triangle and pixel-by-pixel drawing:
#include <SFML/Graphics.hpp>
#include <chrono>
using namespace std::chrono;
#include <iostream>
#include<cmath>
uint8_t* pixels;
int main(int, char const**)
{
const unsigned int width=1200;
const unsigned int height=1200;
sf::RenderWindow window(sf::VideoMode(width, height), "MA: Rasterization Test");
pixels = new uint8_t[width*height*4];
sf::Texture pixels_texture;
pixels_texture.create(width, height);
sf::Sprite pixels_sprite(pixels_texture);
sf::Clock clock;
sf::VertexArray triangle(sf::Triangles, 3);
triangle[0].position = sf::Vector2f(0, height);
triangle[1].position = sf::Vector2f(width, height);
triangle[2].position = sf::Vector2f(width/2, height-std::sqrt(std::pow(width,2)-std::pow(width/2,2)));
triangle[0].color = sf::Color::Red;
triangle[1].color = sf::Color::Blue;
triangle[2].color = sf::Color::Green;
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
window.close();
}
}
window.clear(sf::Color(255,255,255,255));
// Pixel-by-pixel
int us = duration_cast< microseconds >(system_clock::now().time_since_epoch()).count();
for(int i=0;i!=width*height*4;++i){
pixels[i]=255;
}
pixels_texture.update(pixels);
window.draw(pixels_sprite);
int duration=duration_cast< microseconds >(system_clock::now().time_since_epoch()).count()-us;
std::cout<<"Background: "<<duration<<" us\n";
// Triangle
us = duration_cast< microseconds >(system_clock::now().time_since_epoch()).count();
window.draw(triangle);
duration=duration_cast< microseconds >(system_clock::now().time_since_epoch()).count()-us;
std::cout<<"Triangle: "<<duration<<" us\n";
window.display();
}
return EXIT_SUCCESS;
}
Graphics drawing in modern devices using Graphic cards, and the speed of drawing depends on how many triangles in the data you sent to the Graphic memory. That's why just drawing two triangles is fast.
As you mentioned about multithreading, if you using OpenGL (I don't remember what SFML use, but should be the same), what you thinking you are drawing is basically send commands and data to graphic cards, so multithreading here is not very useful, the graphic card has it's own thread to do this.
If you are curious about how graphic card works, this tutorial is the
book you should read.
P.S. As you edit you question, I guess the duration 2500us vs 10us is because you for loop create a texture(even if the texture is a pure white background)(and the for loop, you probably need to start counting after the for loop), and send texture to graphic card need time, while draw triangle only send several points. Still, I suggest to read the tutorial, create a texture pixel by pixel potentially prove the miss understanding of how GPU works.

Why the texture appears only in the first quadrant

What's wrong with this code using SFML?
In the code below, I have this image (1000x1000) and I want to show it in a window (500x500) using sf::RenderTexture.
However, only part of the image appears in the first quadrant:
#include <SFML/Graphics.hpp>
using namespace sf;
int main()
{
RenderWindow window({500, 500}, "SFML Views", Style::Close);
View camera;
camera.setSize(Vector2f(window.getSize()));
Texture background;
background.loadFromFile("numeros.png");
Sprite numeros (background);
RenderTexture texture;
texture.create(window.getSize().x, window.getSize().y);
Sprite content;
content.setTexture(texture.getTexture());
texture.draw(numeros);
texture.display();
while (window.isOpen())
{
for (Event event; window.pollEvent(event);)
if (event.type == Event::Closed)
window.close();
window.clear();
window.setView(camera);
window.draw(content);
window.display();
}
return EXIT_SUCCESS;
}
As far as I can understand, the code should generate the original image (1000x1000) automatically adjusted to 500x500.
Could anyone tell you what is wrong?
You're facing, in fact, two distinct problems:
First one:
As far as I can understand, the code should generate the original
image (1000x1000) automatically adjusted to 500x500.
This is not really true. SFML handles the sprites with the real size of the texture. If your image is 1000x1000, but you want representing it as 500x500, you should assign the texture to a sprite, as you do:
Sprite numeros(background);
and then scale this sprite to fit in a 500x500 window, this is:
numeros.setScale(0.5, 0.5);
With this change you should view the whole image, but...
Second one:
You're messing with the view of the window. If we check SFML documentation, we can see that sf::View expects:
A sf::FloatRect: this is, a coordinate (x,y) - in this case the top-left corner - and a size(width, height)
or
Two sf::Vector2f: one corresponding to the coordinates of the center and the other corresponding to the size of the view.
Assuming you want to use the second one, you're missing the first parameter, the center coordinates, but this is not really necessary. If you simply don't apply the view, the image should be shown in the whole window.
So you simply need to remove:
window.setView(camera);
The code I've tried:
int main()
{
RenderWindow window({ 500, 500 }, "SFML Views", Style::Close);
View camera;
camera.setSize(Vector2f(window.getSize()));
Texture background;
background.loadFromFile("numeros.png");
Sprite numeros(background);
numeros.setScale(0.5, 0.5); // <-- Add this
RenderTexture texture;
texture.create(window.getSize().x, window.getSize().y);
Sprite content;
content.setTexture(texture.getTexture());
texture.draw(numeros);
texture.display();
while (window.isOpen())
{
for (Event event; window.pollEvent(event);)
if (event.type == Event::Closed)
window.close();
window.clear();
//window.setView(camera); <-- Remove this
window.draw(content);
window.display();
}
return EXIT_SUCCESS;
}
And my result:
Just to add another option to #alseether 's excellent response, I realized that the whole issue consisted of that bad View initialization.
This way you can also set the size of the view = to the size of the background image (1000,1000) and finally set the center of the view to the windows's upper left corner.
As the view is larger than the window size (500,500) it will automatically be adjusted to this new size.
In short, the section to be changed would be:
View camera;
camera.setSize(Vector2f(background.getSize().x, background.getSize().y));
camera.setCenter(Vector2f(window.getSize()));

Flickering when drawing the background outside of the main loop

Here's my CODE:
#include <SFML\Graphics.hpp>
sf::RenderWindow renderWindow(sf::VideoMode(300, 300), "The Problem");
int main()
{
sf::Texture tX;
tX.loadFromFile("Recursos/Fondo.png");
sf::Sprite tXt(tX);
renderWindow.draw(tXt);
while (true) // main loop
{
renderWindow.display();
}
}
tX is a large sprite (think: background), so I want to avoid redrawing it every frame.
When I try this, I get flickering, and I have no idea where it's coming from.
Your code, simplified, pseudo:
Initialize();
Draw();
Loop {
Display();
}
What you're doing is drawing the scene to a buffer (without clearing it first, by the way), and then displaying it in a loop (without redrawing anything).
I think the reason you have flickering is due to double-buffering (v-sync).
What ends up happening is that you drew to one buffer, but left the other one empty. When you display the active buffer, they get flipped (so the next time you display, it's an empty buffer).
You really should draw inside the loop.
Alternatively, you can disable multiple-buffering, or try drawing, displaying, drawing again, and then looping (so that you draw to both buffers, assuming there are only two).
Both solutions are not elegant.
If drawing the background really needs optimization, I'm afraid SFML won't give you that granularity. You might want to look in to OpemGL Stencil wizardry.

C++ SFML, orbiting

I recently started to learn SFML, and I have a question, how to make what would be the second body moving in an orbit, help please.
#include <SFML/Graphics.hpp>
using namespace sf;
int main()
{
RenderWindow window(VideoMode(800, 600), "Hello, world!");
CircleShape shape(50.f);
shape.setFillColor(Color::Black);
shape.setPosition(400,300);
shape.setOrigin(50,50);
CircleShape shape2(10.f);
shape2.setFillColor(Color::Black);
shape2.setPosition(700,500);
shape2.setOrigin(10,10);
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear(Color::White);
window.draw(shape);
window.draw(shape2);
window.display();
}
return 0;
}
Well... I will not post full solution. It would not be educational to give you complete code. But I will give you some hints :) .
Your world update should happen in the loop. In the while loop. You have two there. Which one do you think is the one which updates your world?
Circle equation in Cartesian coordinate system is: (x-a)^2 + (y-b)^2 = r^2
In the loop from 1 you should use equation from 2 to update coordinates of second object (shape2).
To perform action from point 3 you have two possibilities: function setPosition and function move, both members of class CircleShape.
If you have further questions ask them in the comments.
For your future questions on stack: give us proof that you put some effort in resolving problem. If question looks like the one I'm answering now we have the idea that you did not think about it just posted it here and you are waiting for someone to write your code for you.
The easiest and i think most precise way would be to use two perpendicular sine waves with the origin being your well origin and the amplitude being the distance for which you want to orbit.
#include <cmath> -- sin(), cos functions
As you probably know sin goes from 0 to 1 then -1 and it repeats. cos is identical but shifted. If you use both for the position then you're left with the sine and cosine values of a given number. If you use one for 'x' and the other for 'y' you're left with circular motion (perpendicular waves). Try out the code below:
int main()
{
CircleShape shape(50.f);
shape.setFillColor(Color::Black);
shape.setPosition(400,300);
shape.setOrigin(50,50);
CircleShape shape2(10.f);
shape2.setFillColor(Color::Black);
shape2.setPosition(700,500);
shape2.setOrigin(10,10);
CircleShape shape3(10.f);
shape3.setFillColor(Color::Black);
shape3.setPosition(700,500);
shape3.setOrigin(10,10);
CircleShape shape4(10.f);
shape4.setFillColor(Color::Black);
shape4.setPosition(700,500);
shape4.setOrigin(10,10);
float speed = 0.01;
float distance = 100;
// create the window
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
window.setFramerateLimit(60);
float counter = 0;
// 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();
}
counter += speed;
// clear the window with black color
window.clear(Color::White);
shape2.setPosition(shape.getPosition().x+distance*sin(counter),shape.getPosition().y+distance*cos(counter));
shape3.setPosition(shape.getPosition().x+distance*sin(counter),shape.getPosition().y+distance*sin(counter));
shape4.setPosition(shape.getPosition().x+2*distance*sin(counter),shape.getPosition().y+distance*cos(counter));
// draw everything here...
// window.draw(...);
window.draw(shape);
window.draw(shape2);
window.draw(shape3);
window.draw(shape4);
// end the current frame
window.display();
}
return 0;
}
As you can see shape2 is oribitng shape1. Shape2 and shape3's position is dependant on the position of shape1 (our origin) as well as the sine and cosine values generated. I marked distinct places in the code with variables to show you the possibilities you can have with this setup. Let's go through the setposition function:
For both x and y we need a base position from which the shape will orbit. we use shape1's position. The sine and cos need some type of number input from which to generate a value. I just used a normal iterator that grows every frame by 'speed' amount. the more it is the faster the sine will repeat thus faster orbiting around the shape1. The next part is the addition of the sine and cos values to the origin. this is where things get fun. we're essentially adding the sine value to the position to move it by that much in any direction. 'Distance' is how much we multiply the sine cos values. Thus bigger distance = further away.
Note:
using sine for x and cos for y you get a normal circular orbit.
using sine for both x and y you get a 3D-looking orbit to and from the screen (shape3)
multiplying sine and cos value additions by a non identical number will result in eliptical rotations (shape4)

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.