I am writing a small program using SFML which draws a 5 x 5 rectangle at any point specified by clicking the mouse.
I am using mapPixelToCoords() to map the mouse coordinates to the pixels as recommended in the documentation, however I am still encountering a weird offset between the coordinates and the actual shape which is drawn.
The offset varies on different parts of the window (it is not a constant x,y value).
I would like the rectangle to be drawn precisely under the mouse pointer, how do I go about removing this offset?
This is the code I'm using to draw the rectangle on click:
Screenshot of offset in the window
int main(){
sf::RenderWindow window(sf::VideoMode(800, 720), "SFML window");
sf::Event event;
//Rectangle to be drawn on the screen
sf::RectangleShape someRect;
someRect.setFillColor(sf::Color(0, 0, 0));
someRect.setSize({ 5, 5 });
someRect.setPosition(100, 120);
while (window.isOpen())
{
if (!windowStarted) {
event.type = sf::Event::Closed;
}
while (window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::MouseButtonPressed) {
someRect.setPosition(window.mapPixelToCoords(sf::Mouse::getPosition(window)));
}
}
window.clear(sf::Color(255, 255, 255));
window.draw(someRect);
window.display();
}
return 0;
}
Related
I have a problem using sfml where it seems sf::Transforms get applied relative to the origin of whole coordinate system and not the origin of the sprite/thing being transformed.
For example in the two following example I expect the same behavior and it's not what happens.
(differences in bold, most of rest just serves to actually display a sfml window with a circle that gets scaled down when space is pressed)
First Example : here the circle is scaled down towards the top left corner of the window
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 800), "Example");
sf::CircleShape shape(100.f);
shape.setFillColor(sf::Color::Green);
shape.setPosition(400, 400);
sf::Transform t;
t.scale( 9/10.f , 9/10.f);
sf::Transform t_n;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed){
window.close();
}
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Space) {
t_n = t_n * t;
}
}
}
window.clear();
window.draw(shape, t_n);
window.display();
}
return 0;
}
Second Example : Here the circle is scaled down towards the top left corner of its bounding box : this is the kind of behavior I want to achieve but using Transforms
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 800), "Example");
sf::CircleShape shape(100.f);
shape.setFillColor(sf::Color::Green);
shape.setPosition(400, 400);
// no Transforms need declaring
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed){
window.close();
}
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Space) {
shape.scale(9/10.f, 9/10.f);
}
}
}
window.clear();
window.draw(shape);
window.display();
}
return 0;
}
note: this is a possible duplicate of Transformations igrnores sf::Sprite's origin , but the only answer to this thread is my own, and the solution I suggest is really not ideal.
I want create light blue background so I draw 2 rectangles (blue and white).
But when I execute program i see only black screen.Also my video adapter is fully loaded.
Code:
int main() {
srand(time(NULL));
RenderWindow window(sf::VideoMode(500, 500), "Fish!",sf::Style::Close|sf::Style::Resize);
RectangleShape water(Vector2f(500,500));
RectangleShape background(Vector2f(500,500));
background.setFillColor(Color(255,255,255,3));
water.setFillColor(Color(5,45,255,1));
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case (sf::Event::Resized):
break;
case (sf::Event::Closed):
window.close();
break;
}
}
window.clear();
window.draw(background);
window.draw(water);
window.display();
}
I need window.clear function because i want insert some object later on the screen!
I have this single c++ / SFML code...
#include <SFML/Graphics.hpp>
#define windowWidth 600
#define windowHeight 300
int main()
{
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "SFML Views");
sf::View view(sf::FloatRect(0,0, windowWidth, windowHeight));
view.zoom(2);
window.setView(view);
sf::RectangleShape back (sf::Vector2f(windowWidth, windowHeight));
back.setFillColor(sf::Color::White);
sf::RectangleShape rect (sf::Vector2f(200, 100));
rect.setFillColor(sf::Color::Red);
rect.setPosition(windowWidth - rect.getSize().x, windowHeight - rect.getSize().y); // position in the lower right corner
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(back);
window.draw(rect);
window.display();
}
return 0;
}
...which might position the red rectangle in the lower right corner of the window.
However, when I zoom the view in (as in the code), it obviously moves along with the entire window like this image.
I have some doubts:
To correct the positioning of the red rectangle and place it in the lower right corner of the global window, I currently have to do some calculations considering the zoom factor, original rectangle size, etc. Is there any easier way to position this rectangle in the lower-right corner of the global window?
How do I prevent some objects from being resized by the zoom of the view?
What do I have to do to have multiple active views in the same window?
Is there any easier way to position this rectangle in the lower-right corner of the global window?
From SFML 2D camera tutorial: "To draw something using a view, you must draw it after calling the setView function of the target to which you are drawing"
So the drawing part should be
window.clear();
window.setView(view);
window.draw(back);
window.setView(window.getDefaultView()); //don't zoom red rect
window.draw(rect);
window.display();
What do I have to do to have multiple active views in the same window?
Just call setView() for each active view and then drawing all the things in that view (again, if needed)
window.setView(leftHalfView);
window.draw(a);
window.draw(b);
window.draw(c);
window.setView(rightHalfView);
window.draw(a);
window.draw(b);
window.draw(c);
window.setView(minimap);
window.draw(a);
window.draw(b);
window.setView(window.getDefaultView());
window.draw(x);
window.draw(y);
In this code I have one view (white, zoom 2) and the original window, also one sprite for a car and one sprite for a wall...
#include <SFML/Graphics.hpp>
#include "collision.hpp"
#define windowWidth 1000
#define windowHeight 600
int main()
{
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "SFML Views");
sf::View view(sf::FloatRect(0,0, windowWidth, windowHeight));
view.zoom(2);
// car
sf::Texture imgCar;
Collision::CreateTextureAndBitmask(imgCar, "carro.png" );
sf::Sprite car;
car.setTexture(imgCar);
car.setPosition(0, (windowHeight - imgCar.getSize().y) / 2);
// wall
sf::Texture imgWall;
Collision::CreateTextureAndBitmask( imgWall, "barreira.png" );
sf::Sprite wall;
wall.setTexture(imgWall);
wall.setPosition(windowWidth - imgWall.getSize().x, 0);
sf::RectangleShape background (sf::Vector2f(windowWidth, windowHeight));
background.setFillColor(sf::Color::White);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
if (!Collision::PixelPerfectTest(car, wall))
car.move(0.1, 0);
window.clear();
window.setView(view);
window.draw(background);
window.setView(window.getDefaultView());
window.draw(car);
window.draw(wall);
window.display();
}
return 0;
}
... if both car and wall are related with the main window, I get a perfect car collision with the wall. (see image).
Now if I put the car inside the view ...
window.clear();
window.setView(view);
window.draw(background);
window.draw(car);
window.setView(window.getDefaultView());
window.draw(wall);
window.display();
... the collision is detected as if the wall were within the boundaries of the view (which is zoomed in) (see image).
How can I make the collision detection independent of the view?
I'm actually facing a issue I can't solve by myself, using SFML I have a single shape, and I'm moving it at each frame, and then I draw it. The problem is, SFML using two buffers, the trail won't be the same on each buffer (frame) and it will give a weird blinking effect.
I need your knowledge, should I find another way of doing it, like an array of shapes that I'll extend. Or is there a way to skip the second buffer? Making this buffer a piece of paper, and my shape the brush.
sf::CircleShape circle;
//Init the circle (radius etc)
sf::Vector2f pos(0, 500);
while(!done)
{
pos.x += 1;
circle.setPosition(pos);
window.draw(circle);
window.display();
}
May you spend a nice day.
you can achieve that by using sf::RenderTexture which acts as the back buffer (single buffer) to make your drawing and then draw it on screen via sf::RenderWindow`
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML art");
window.setVerticalSyncEnabled(true);
sf::RenderTexture target;
if (!target.create(window.getSize().x, window.getSize().y))
return -1;
target.clear(sf::Color::White);
target.display();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::CircleShape circle(5.f, 32u);
circle.setPosition(sf::Vector2f(sf::Mouse::getPosition(window)));
circle.setFillColor(sf::Color::Red);
target.draw(circle, sf::BlendNone);
target.display();
}
window.clear();
window.draw(sf::Sprite(target.getTexture()));
window.display();
}
}
The purpose of double buffering is to avoid showing parts of not fully rendered scene during rendering of a frame. So your problem has nothing to do with double-buffering itself. Basically, you need to generate all the primitives necessary to render a particular frame every time. In your specific case you could achieve this like this:
sf::CircleShape circle;
//Init the circle (radius etc)
sf::Vector2f pos(0, 500);
int frameCount = 0;
while(!done)
{
++frameCount;
for (int i = 0; i < frameCount; ++i) {
circle.setPosition(pos + sf::Vector2f(i, 0));
window.draw(circle);
}
window.display();
}