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