SFML community forums

Help => Graphics => Topic started by: BreadMonika on March 09, 2023, 01:39:53 am

Title: Keeping the sf::View inside of the map
Post by: BreadMonika on March 09, 2023, 01:39:53 am
Hi, I've just started to learn SFML, and as I was experimenting with the views, I came up with a problem that I do not know how to solve, (I did this in SDL2 but things here are different), anyway, what I'm trying to do is keep the view inside of the map limits while scrolling. Like it would scroll in a super mario game, the view is not completely centered in the player but rather it moves when the player is at half of the window.

How can I keep the view inside of the map limits?

Here is what I have so far, it works well for the top and left corners but the right and bottom ones dont.

please see the gif to see my current behavior.

Here is my code:
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow app(sf::VideoMode(480, 320), "view demo");

    // create player, at the screen center
    sf::RectangleShape player(sf::Vector2f(20, 20));
    player.setFillColor(sf::Color::Red);
    player.setOrigin(10, 10);
    player.setPosition(240, 160);
    const float player_speed = 100;

    sf::Texture backgroundImg;
    backgroundImg.loadFromFile("./resources/true.jpg");


    sf::Sprite background(backgroundImg);

   

    // we create our custom view
    sf::View player_view(sf::FloatRect(0, 0, app.getSize().x, app.getSize().y));
    player_view.setViewport(sf::FloatRect(0, 0, 1, 1));
    sf::Clock clock;
    bool running = true;
    sf::Event event;
    app.setView(player_view);

    while (running)
    {
        while (app.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                running = false;
        }

        // moving player
        float frametime = clock.getElapsedTime().asSeconds();
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            player.move(0, -player_speed * frametime);
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            player.move(0, player_speed * frametime);
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            player.move(-player_speed * frametime, 0);
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            player.move(player_speed * frametime, 0);

        clock.restart();

        // we keep our view centered on the player

        sf::Vector2f position;
        auto sizeX(app.getSize().x);
        auto sizeY(app.getSize().y);
        player.getPosition().x >= sizeX / 2 ? position.x = player.getPosition().x : position.x = sizeX / 2;
        player.getPosition().y >= sizeY / 2 ? position.y = player.getPosition().y : position.y = sizeY / 2;


        player_view.setCenter(position);
        app.setView(player_view);

        // rendering entities
        app.draw(background);
        app.draw(player);

        app.display();
        app.clear();
    }
    app.close();
    return 0;
}
 
Title: Re: Keeping the sf::View inside of the map
Post by: Stauricus on March 09, 2023, 11:43:55 am
If I understand correctly what you want, you just need to check if the view corners are outside your map coordinates. we don't have a function to get view corners, so you can use the view center minus (or plus) half of its size, both in the X and in the Y coordinates.

//after this line:
player_view.setCenter(position);
//add:
sf::Vector2f map_start(0, 0); //here im setting the map to start at the (0,0) coord
sf::Vector2f map_end(100, 100); //and to end at the (100, 100) coord

//first, in the X axis:

//you check if the view center minus half of the view size is a number smaller than the map default coordinates. if it is, set it to said coordinate.
if (player_view.getCenter().x - player_view.getSize().x/2 < map_start.x) {
  player_view.setCenter(map_start.x +player_view.getSize().x/2 , player_view.getCenter().y);
}
//if its not below the minimum, it could be above the maximum:
else if (player_view.getCenter().x + player_view.getSize().x/2 > map_end.x) {
  player_view.setCenter(map_end.x - player_view.getSize().x/2, player_view.getCenter().y);
}

//then you do the same for the Y axis.
 


EDIT: there are other ways to do it. you could check if the player is in the correct bounds for the view to follow him, and only then use player_view.setCenter(position);
but the way above should work well.
Title: Re: Keeping the sf::View inside of the map
Post by: BreadMonika on March 13, 2023, 03:45:29 am
That did it, thank you so much ^-^/