Why the texture appears only in the first quadrant - c++

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()));

Related

Moving Sprite (SFML) in c++ using Loops

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.

How to know a sprite position inside a view, relative to window?

I have this sprite of a car that moves with varied speed.
It is inside a view and the view is moved to the left to keep the car always in the center of the window.
The view accompanies the displacement of the car, ie it is shifted to the left as the car accelerates or brakes.
This way the car will always appear in the center.
But if for example it is overtaken by another car, it will be left behind.
For it not to disappear from the window, I have to zoom in the view so that all the cars appear.
But for this, I need to know the position of the car in relation to the window (not in relation to the view).
getGlobalBounds().left or getPosition().x show the same value, which is the position relative to the view, not relative to the window, as shown in the image.
How to know a sprite position inside a view, relative to window?
After several hours of research, I finally find the easy way of achieve this. And yes, it was ridiculously easy.
But first, I would like to clear up some misconceptions.
getGlobalBounds().left or getPosition().x show the same value,
which is the position relative to the view, not relative to the
window, as shown in the image.
In fact, those methods return the position in the world, not in the view nor in the window.
You can have, for instance, a 500x500 window, with a 400x400 view, in a 10000x10000 world. You can place things in the world, outside of the view or the window. When the world is rendered, then the transformations of the view (translations, rotations, zoom, ...) are applied to the world and things are finally shown in the window.
To know where a coordinate in the world is represented in the window (or any other RenderTarget) and vice versa, SFML actually have a couple of functions:
RenderTarget.mapCoordsToPixel(Vector2f point)
Given a point in the world gives you the corresponding point in the RenderTarget.
RenderTarget.mapPixelToCoords(Vector2f point)
Given a point in the RenderTarget gives you the corresponding point in the world. (this is useful to map mouse clicks to corresponding points in your world)
Result
Code
int main()
{
RenderWindow window({ 500, 500 }, "SFML Views", Style::Close);
sf::View camera(sf::FloatRect(0, 0, window.getSize().x, window.getSize().y));
sf::Vector2f orig(window.getSize().x / 2, window.getSize().y / 2);
camera.setCenter(orig);
sf::Font f;
f.loadFromFile("C:/Windows/Fonts/Arial.ttf");
sf::Text t;
t.setFont(f);
sf::RectangleShape r;
r.setPosition(10, 10);
r.setSize(sf::Vector2f(20, 20));
r.setOutlineColor(sf::Color::Blue);
r.setFillColor(sf::Color::Blue);
t.setPosition(10, 40);
while (window.isOpen())
{
for (Event event; window.pollEvent(event);)
if (event.type == Event::Closed)
window.close();
else if (event.type == Event::KeyPressed){
camera.move(-3, 0);
camera.rotate(5.0);
camera.zoom(1.1);
}
auto realPos = window.mapCoordsToPixel(r.getPosition());
std::string str = "Pos: (" + std::to_string(realPos.x) +","+ std::to_string(realPos.y) + ")";
t.setString(str);
window.clear();
window.setView(camera);
window.draw(r);
window.draw(t);
window.display();
}
return EXIT_SUCCESS;
}

SFML Views setCenter vs rotation

I have a view with same dimensions of original window (500,300)
I apply view.zoom(2) to leave the view at half the size.
Now the view is centered. I want to move the view to the upper left corner of the original window. So I put view.setCenter(500,300);
The view is now correctly positioned in the upper corner of the original window. But now I want to rotate the view, making the center of the view its own top left corner, ie (0,0): view.setRotation(5);
As you can see, the center of the axis of rotation should be 0.0 but not respected.
The problem is that if I do view.setCenter (0,0), the whole view returns to the middle of the original window.
How to solve this?
Instead of using view.setCenter(500,300); move it via view.move(x_offset, y_offset);. Then applying setCenter(...) won't redefine the center and it won't get reset.
I recommend consulting the API reference of View for further reading.
You might also be interested in void sf::View::setViewport(const FloatRect& viewport) or void sf::View::reset(const FloatRect& rectangle).
This code, kindly provided by Geheim, solves the problem and also teaches a more practical approach to SFML.
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window({500, 300}, "SFML Views", sf::Style::Close);
window.setFramerateLimit(120);
bool useViewPort {true}; // change this to false to see the other version
sf::View camera {{0, 0}, static_cast<sf::Vector2f>(window.getSize())};
if (useViewPort)
{
camera.setViewport({-0.5f, -0.5f, 1, 1});
camera.rotate(5);
}
else
camera.setCenter(camera.getSize());
camera.zoom(2);
window.setView(camera);
sf::RectangleShape background {camera.getSize()};
sf::RectangleShape square {{50, 50}};
square.setFillColor(sf::Color::Red);
sf::RenderTexture texture;
texture.create(window.getSize().x, window.getSize().y);
while (window.isOpen())
{
for (sf::Event event; window.pollEvent(event);)
if (event.type == sf::Event::Closed)
window.close();
window.clear();
if (useViewPort)
{
window.draw(background);
window.draw(square);
}
else
{
texture.clear();
texture.draw(background);
texture.draw(square);
texture.display();
sf::Sprite content {texture.getTexture()};
content.rotate(-5); // you have to rotate in the other disquareion here, do you know why?
window.draw(content);
}
window.display();
}
return EXIT_SUCCESS;
}
I'm glad you got the result you wanted by applying viewports using Geheim's code.
However, if you don't want to be using viewports to clip areas of the window and such, you can still rotate a view around a specific point other than its centre. You just need a little bit of mathematics...
Take the different between the target point (in the view's co-ordinate system) and the view's centre and rotate that point by the amount you wish to rotate the view and around the view's centre. Then, calculate the difference between those points (the target point and the rotated point). Once you have this difference, simply rotate the view (around its centre as normal) but then move the view by that difference.
It might sound complicated so you might want to just use this free function that I made that does it all automatically; it's on the SFML Wiki:
RotateViewAt

Centering Shapes/Objects in SFML for C++

So recently, I have begun using SFML to make games in Visual Studio.
After setting everything up, and writing some sample code, I devised this:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(600, 600), "Move the Shape");
sf::CircleShape shape(100.f);
shape.setFillColor(sf::Color::Green);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(shape);
window.display();
}
return 0;
}
The program produces the following result:
How do I place the circle in the center? I want to set up some code after that lets the user move the circle with the keyboard's arrow keys, so I need the circle in the center.
You need to set the position of shape with shape.setPosition(x, y). You know the width and height of the window (600px each way), and you know the radius of the circle (100px), so you can calculate the x and y that the circle needs to be moved to be centered. I'll leave that as an exercise for you.
You may also want to consider setting the origin of your circle so that you can position it by its center point (see setOrigin).
Actually, I've answered my own question. To set the position of an item, write:
shape.setPosition(x, y);
If you want to center a circle you can do something like this
circle.setPosition((window.getSize().x / 2.f) - circle.getRadius(), (window.getSize().y / 2.f) - circle.getRadius());
First, you should set the origin of the circle in the middle of it:
circle.setOrigin( circle.getRadius() / 2 , circle.getRadius() / 2 );
The, just move the center of the circle in the middle of the scree:
circle.setPosition( window.getSize().x / 2 , window.getSize().y / 2 );

Passing an SDL_Surface or clip_rect

I'm trying to write a bit of code in which I check to see if the mouse has been clicked on a certain SDL Surface. In the code example below, I have the SDL_Surface background which I am trying to check against the mouse position.
bool checkCollide(int myx, int myy, SDL_Surface* objectOne){
if(myx < objectOne->clip_rect.x) return false;
if(myx > objectOne->clip_rect.x + objectOne->clip_rect.w) return false;
if(myy < objectOne->clip_rect.y) return false;
if(myy > (objectOne->clip_rect.y + objectOne->clip_rect.h)) return false;
return true;
}
void main(){
SDL_Surface* background;
while(SDL_PollEvent(&event)){
if(event.type == SDL_MOUSEBUTTONDOWN){
if(checkCollide(event.button.x, event.button.y, background){
// run events
}
}
}
The problem I'm having is that when I try to run it as shown above, the code compiles fine but doesn't do anything. The collision check always fails. I have tried it in a lot of various combinations, including changing the parameters of checkCollide to SDL_Rect and passing the Surface's clip_rect property. The only completely successful way I've done it is to have a separate SDL_Rect with the same size and location as the SDL_Surface, and to check the collision against that. When I run it that way, it all works as I expect it to. Why isn't the surface (or clip_rect) passing correctly, or if it is, what am I doing wrong in the checkCollide function to cause it to fail?
The clip_rect property is not what you think it is:
From the SDL docs:
The clip_rect field is the clipping rectangle as set by
SDL_SetClipRect.
and SDL_SetClipRect
Sets the clipping rectangle for a surface. When this surface is the
destination of a blit, only the area within the clip rectangle will be
drawn into.
The rectangle pointed to by rect will be clipped to the edges of the
surface so that the clip rectangle for a surface can never fall
outside the edges of the surface.
If rect is NULL the clipping rectangle will be set to the full size of
the surface.
The problem is, that when you test the collision later on, the clip_rect x,y are always set to 0. The surface itself does not know about it's position on the screen.
A solution to this would be to create a Sprite like class that will have a Rect and a Surface.