Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Smooth screen scrolling  (Read 4370 times)

0 Members and 1 Guest are viewing this topic.

starkhorn

  • Jr. Member
  • **
  • Posts: 55
    • View Profile
    • Email
Smooth screen scrolling
« on: May 19, 2015, 05:34:05 am »
Hi Folks,

So I'm using a view to display the game area. When the mouse goes to the edge of the game window, then I simply move the view by offset in that direction. This seems to work ok, i.e. I can detect the mouse pos and the view.move does indeed move it in the correct direction.

However the scrolling is jerky and not consistence. Sometimes it is really fast and other times it is painfully slow.
I am still quite new to sfml and c++ in general so am I missing something obvious here in regards to how quickly it goes throw the event loop? Is there a technique to make that more consistent?

Here is some example code of what I have right now.


#include <TGUI/TGUI.hpp>

using namespace std;

class Test
{
public:
        Test();

        enum screenScrollDirection
        {
                NO_SCROLL = -1,
                SCROLL_LEFT,
                SCROLL_RIGHT,
                SCROLL_UP,
                SCROLL_DOWN
        };

        void calcMapView();
        void initTextureList();
       
        void scrollMapView(screenScrollDirection passed_ScrollDirection);
        screenScrollDirection isScreenScrollRequired();

        void drawAllRegions();
        void drawRegion(int passed_indx);

        tgui::Gui gui;
        tgui::Callback callback;
       
        sf::RenderWindow gameWindow;
        sf::VideoMode desktop;
        vector<sf::Texture> regionTextureList;
        sf::Font font;
        sf::Event event;
        sf::View mapView;
        sf::Vector2f mousePos;

};

Test::Test()
{
       
        desktop = sf::VideoMode::getDesktopMode();
        gameWindow.create(sf::VideoMode(desktop.width, desktop.height, desktop.bitsPerPixel), "Test", sf::Style::Fullscreen);
        gameWindow.setPosition(sf::Vector2i(0, 0));
        gui.setWindow(gameWindow);
        calcMapView();
        initTextureList();
}

void Test::calcMapView()
{
        int xSize = gameWindow.getSize().x;
        int ySize = gameWindow.getSize().y * 0.7500;

        float xFloat = (float)xSize/gameWindow.getSize().x;
        float yFloat = (float)ySize/gameWindow.getSize().y;

        mapView.reset(sf::FloatRect(0.f, 0.f, xSize, ySize));
        mapView.setViewport(sf::FloatRect(0.f, 0.f, xFloat, yFloat));
}

void Test::initTextureList()
{
        for (int i = 0; i < 14; i++)
        {
                regionTextureList.push_back(sf::Texture());
                regionTextureList[i].loadFromFile("regionImage_" + to_string(i) + ".png");
        }

}

void Test::scrollMapView(Test::screenScrollDirection passed_ScrollDirection)
{
        switch (passed_ScrollDirection)
        {
        case Test::SCROLL_LEFT:
                mapView.move(-1 * 30, 0);
                break;
        case Test::SCROLL_RIGHT:
                mapView.move(1 * 30, 0);
                break;
        case Test::SCROLL_UP:
                mapView.move(0, -1 * 30);
                break;
        case Test::SCROLL_DOWN:
                mapView.move(0, 1 * 30);
                break;
        case Test::NO_SCROLL:
        default:
                break;
        }
}

//checks mouse position if screen scroll is required. Return the enum direction or -1 if not required.
Test::screenScrollDirection Test::isScreenScrollRequired()
{
        if (mousePos.x < 10)
        {
                return SCROLL_LEFT;
        }
        else if (mousePos.x > gameWindow.getSize().x - 10)
        {
                return SCROLL_RIGHT;
        }
        else if (mousePos.y < 10)
        {
                return SCROLL_UP;
        }
        else if (mousePos.y > gameWindow.getSize().y - 10)
        {
                return SCROLL_DOWN;
        }
       
        return NO_SCROLL;
}

void Test::drawAllRegions()
{
        gameWindow.clear(sf::Color::Black);
       
        gameWindow.setView(mapView);
        for (int i = 0; i < 14; i++)
        {
                drawRegion(i);
        }
        gameWindow.setView(gameWindow.getDefaultView());
        gameWindow.display();
       
}

void Test::drawRegion(int passed_indx)
{
        sf::Sprite sprite;

        sprite.setTexture(regionTextureList[passed_indx]);
        sprite.setPosition(0, 0);
        sprite.setScale(4.0f, 3.0f);

        gameWindow.draw(sprite);
}


int main()
{
        Test testClass;

        do
        {
                testClass.drawAllRegions();

                while (testClass.gameWindow.pollEvent(testClass.event))
                {
                        if (testClass.event.type == sf::Event::MouseMoved)
                        {
                                testClass.mousePos.x = testClass.event.mouseMove.x;
                                testClass.mousePos.y = testClass.event.mouseMove.y;
                                testClass.scrollMapView(testClass.isScreenScrollRequired());
                        }
                }

               
        } while (true);
}

 

Cpl.Bator

  • Hero Member
  • *****
  • Posts: 540
    • View Profile
Re: Smooth screen scrolling
« Reply #1 on: May 19, 2015, 07:09:51 am »
Hi. search for deltatime on the forum

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10815
    • View Profile
    • development blog
    • Email
AW: Smooth screen scrolling
« Reply #2 on: May 19, 2015, 07:51:14 am »
As hinted your movement should be independent from the rendering, so when you get different framerates it shouldn't impact the "physics". You might also want to look into fixed timestep.

Additionally you might want to move the view with a fixed speed instead of some ofset.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Hapax

  • Hero Member
  • *****
  • Posts: 3349
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Smooth screen scrolling
« Reply #3 on: May 19, 2015, 08:38:50 pm »
On top of what the others said above about multiplying movement by delta time and looking into fixed timestep to help with regulation, you probably don't want to use events for this particular situation. The mousemove event occurs only when you move the mouse so if you move the mouse to the edge (which triggers the scroll) and hold the mouse there, it won't trigger any more events. You might find reading the current mouse state in real-time to be more suitable here.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

starkhorn

  • Jr. Member
  • **
  • Posts: 55
    • View Profile
    • Email
Re: Smooth screen scrolling
« Reply #4 on: May 19, 2015, 11:12:41 pm »
Thanks everyone for your comments.

So I found several useful examples using the delta time and fixed timestep as searches - thank you as I was unaware of this concept.

Those threads lead me to this article which helped get my head around it.
http://gafferongames.com/game-physics/fix-your-timestep/

I also found this sfml tutorial video from coding made easy which helped me as well.
https://www.youtube.com/watch?v=ghgd-R1gRmc

So I've done the below changes

- Added a scrollSpeed variable to the class.
- Added a sf::Clock to the class.
- As per Hapax suggestion, I stopped using events and instead used the getPosition in real-time method for the mouse.
- Changed the view.move so that it used the scrollSpeed multiplied by the elapsed time.

This seemed to do the trick - so many thanks guys. One question that I did have is about the window class method - setFramerateLimit

http://www.sfml-dev.org/documentation/2.0/classsf_1_1Window.php

Given what I have done below, is this actually required/useful? I guess I am trying to understand in the scenario where the setFramerateLimit method is used? Or whether in my scenario this method is useful/required?

For completeness, here is my changed code - note the below if proof of concept only and isn't obviously my final code. I was just testing how to get screen scrolling working with the below.

#include <TGUI/TGUI.hpp>

using namespace std;

class Test
{
public:
        Test();

        enum screenScrollDirection
        {
                NO_SCROLL = -1,
                SCROLL_LEFT,
                SCROLL_RIGHT,
                SCROLL_UP,
                SCROLL_DOWN
        };

        void calcMapView();
        void initTextureList();
       
        void scrollMapView(screenScrollDirection passed_ScrollDirection);
        screenScrollDirection isScreenScrollRequired();

        void drawAllRegions();
        void drawRegion(int passed_indx);

        tgui::Gui gui;
        tgui::Callback callback;
       
        sf::RenderWindow gameWindow;
        sf::Clock gameClock;
        sf::Time gameTime;
        sf::VideoMode desktop;
        vector<sf::Texture> regionTextureList;
        sf::Font font;
        sf::Event event;
        sf::View mapView;
        sf::Vector2i mousePos;

        int scrollSpeed;

};

Test::Test()
{
       
        desktop = sf::VideoMode::getDesktopMode();
        gameWindow.create(sf::VideoMode(desktop.width, desktop.height, desktop.bitsPerPixel), "Test", sf::Style::Fullscreen);
        gameWindow.setPosition(sf::Vector2i(0, 0));
        gui.setWindow(gameWindow);
        calcMapView();
        initTextureList();
        scrollSpeed = 1000;
}

void Test::calcMapView()
{
        int xSize = gameWindow.getSize().x;
        int ySize = gameWindow.getSize().y * 0.7500;

        float xFloat = (float)xSize/gameWindow.getSize().x;
        float yFloat = (float)ySize/gameWindow.getSize().y;

        mapView.reset(sf::FloatRect(0.f, 0.f, xSize, ySize));
        mapView.setViewport(sf::FloatRect(0.f, 0.f, xFloat, yFloat));
}

void Test::initTextureList()
{
        for (int i = 0; i < 14; i++)
        {
                regionTextureList.push_back(sf::Texture());
                regionTextureList[i].loadFromFile("C:/Users/mcgoldrickb/Documents/Visual Studio 2012/Projects/romeGame_v2/Debug/test/regionImage_" + to_string(i) + ".png");
        }

}

void Test::scrollMapView(Test::screenScrollDirection passed_ScrollDirection)
{
        switch (passed_ScrollDirection)
        {
        case Test::SCROLL_LEFT:
                mapView.move(-scrollSpeed * gameClock.getElapsedTime().asSeconds(), 0);
                break;
        case Test::SCROLL_RIGHT:
                mapView.move(scrollSpeed * gameClock.getElapsedTime().asSeconds(), 0);
                break;
        case Test::SCROLL_UP:
                mapView.move(0, -scrollSpeed * gameClock.getElapsedTime().asSeconds());
                break;
        case Test::SCROLL_DOWN:
                mapView.move(0, scrollSpeed * gameClock.getElapsedTime().asSeconds());
                break;
        case Test::NO_SCROLL:
        default:
                break;
        }
}

//checks mouse position if screen scroll is required. Return the enum direction or -1 if not required.
Test::screenScrollDirection Test::isScreenScrollRequired()
{
        mousePos = sf::Mouse::getPosition();

        if (mousePos.x < 10)
        {
                return SCROLL_LEFT;
        }
        else if (mousePos.x > gameWindow.getSize().x - 10)
        {
                return SCROLL_RIGHT;
        }
        else if (mousePos.y < 10)
        {
                return SCROLL_UP;
        }
        else if (mousePos.y > gameWindow.getSize().y - 10)
        {
                return SCROLL_DOWN;
        }
       
        return NO_SCROLL;
}

void Test::drawAllRegions()
{
        gameWindow.clear(sf::Color::Black);
       
        gameWindow.setView(mapView);
        for (int i = 0; i < 14; i++)
        {
                drawRegion(i);
        }
        gameWindow.setView(gameWindow.getDefaultView());
        gameWindow.display();
       
}

void Test::drawRegion(int passed_indx)
{
        sf::Sprite sprite;

        sprite.setTexture(regionTextureList[passed_indx]);
        sprite.setPosition(0, 0);
        sprite.setScale(4.0f, 3.0f);

        gameWindow.draw(sprite);
}


int main()
{
        Test testClass;

        while (testClass.gameWindow.isOpen())
        {
                testClass.gameClock.restart();
                testClass.drawAllRegions();
                testClass.scrollMapView(testClass.isScreenScrollRequired());
       
        }
}

 

Hapax

  • Hero Member
  • *****
  • Posts: 3349
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Smooth screen scrolling
« Reply #5 on: May 20, 2015, 07:44:24 pm »
Thanks everyone for your comments.
You're welcome :)

http://gafferongames.com/game-physics/fix-your-timestep/
This is the exact place I learnt about it and would always recommend that site as the first place to go.

You may also be interested in looking at this class (usage example here) that can help with timestep control.

One question that I did have is about the window class method - setFramerateLimit

http://www.sfml-dev.org/documentation/2.0/classsf_1_1Window.php

Given what I have done below, is this actually required/useful? I guess I am trying to understand in the scenario where the setFramerateLimit method is used? Or whether in my scenario this method is useful/required?
You should be aware that the documentation you linked was for SFML 2.0. The documentiation for SFML 2.3 is the most up-to-date.

As stated in the documentation for setFrameLimit(), using it will cause the window to add a small delay on each frame to keep the frame rate at (approximately) the given value. Using this allows your operating system more processor time rather than hogging everything that it can.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*