Game loop with interpolation - weird step back - c++

I have read about an interpolation applied to game loops and tried to implement it myself. It looks almost same as I expected, but when the object ends its movement weird step back takes place. I decided to paste here full source, because this problem may be caused by everything.
#include <SFML/Graphics.hpp>
#include <chrono>
sf::RenderWindow window(sf::VideoMode(800, 600), "Interpolation");
sf::Event event;
int fps = 10; // set to 10 for testing purpose
std::chrono::nanoseconds timePerFrame = std::chrono::seconds(1);
std::chrono::nanoseconds accumulator;
std::chrono::steady_clock::time_point start;
sf::RectangleShape shape1(sf::Vector2f(50, 50));
sf::RectangleShape shape2(sf::Vector2f(50, 50));
sf::Vector2f movement(0, 0);
sf::Vector2f position1(375, 100);
sf::Vector2f position2(375, 275);
void initialization();
void processInput();
void update();
void interpolate();
void render();
int main()
{
initialization();
while(window.isOpen())
{
start = std::chrono::steady_clock::now();
processInput();
while(accumulator >= timePerFrame)
{
update();
accumulator -= timePerFrame;
}
interpolate();
render();
accumulator += std::chrono::steady_clock::now() - start;
}
return 0;
}
void initialization()
{
timePerFrame /= fps;
shape1.setPosition(position1);
shape2.setPosition(position2);
}
void processInput()
{
while(window.pollEvent(event))
{
if(event.type == sf::Event::Closed) window.close();
}
}
void update()
{
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) movement = sf::Vector2f(-300, 0);
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) movement = sf::Vector2f(300, 0);
else movement = sf::Vector2f(0, 0);
position1.x += movement.x / fps;
position2.x += movement.x / fps;
shape1.setPosition(position1);
shape2.setPosition(position2);
}
void interpolate()
{
double interpolationFactor = (double) accumulator.count() / timePerFrame.count();
shape2.setPosition(position2.x + (movement.x / fps * interpolationFactor), position2.y);
}
void render()
{
window.clear(sf::Color::Black);
window.draw(shape1);
window.draw(shape2);
window.display();
}
I do not know what may cause that kind of problem. I'm looking forward your help.

Your interpolate function can count some parts of the time interval multiple times.
Since accumulator only resets every timePerFrame ticks, a fast loop rate can add smaller intervals multiple times. If the main loop runs in 0.01 seconds, the first call to interpolate uses that 0.01 in the interpolation factor. The next time, it uses 0.02 (for a total add of 0.03). This continues until there is enough time accumulated for update to update the position using 0.1 seconds (the time step). Since interpolate added in more time than that, the object jumps back.
interpolate should only add in the time of the current step, and not the fully accumulated time. (Also, rather than calling now to get the start time every loop, the previous loop's now value used for the end time should be used as the start time for the next loop. Otherwise you'll lose occasional clock ticks when it changes between the end of one loop and the start of the next.)

Related

C++ SFML. How to create a diminishing(shrinking) circle

I have a class of circles that appear and disappear in the window for a while, there may be several, or maybe one. Currently drawn circles are stored in the vector_of_current_circles vector. I need to make them shrink to a certain size over time. How to do it?
window while loop:
while (window.isOpen()) {
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event)) {
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
for (int i = 0; i < vector_of_circles.size(); i++) {
if (std::fabs(vector_of_circles[i].getBeginOfLife() - clock.getElapsedTime().asSeconds()) < 1e-2) {
if (!vector_of_circles[i].get_is_drawn()) {
window.clear();
window.draw(sprite);
vector_of_current_circles.push_back(vector_of_circles[i]);
for (const auto &item : vector_of_current_circles) {
item.print_circle(window);
}
window.display();
vector_of_circles[i].set_is_drawn();
}
}
if (std::fabs(vector_of_circles[i].getEndOfLife() - clock.getElapsedTime().asSeconds()) < 1e-2) {
if (vector_of_circles[i].get_is_drawn()) {
vector_of_current_circles.erase(vector_of_current_circles.begin());
vector_of_circles[i].set_is_drawn();
}
window.clear();
window.draw(sprite);
for (const auto &item : vector_of_current_circles) {
item.print_circle(window);
}
window.display();
}
}
}
Here is Circle code:
private:
sf::CircleShape circle_;
//sf::Clock clock;
float begin_of_life_;
bool is_drawn_ = false;
float end_of_life_;
//sf::RenderWindow& window_;
public:
Circle();
void print_circle(sf::RenderWindow&) const;
float get_radius() const;
void set_position(float, float);
void set_texture(sf::Texture&);
void setBeginOfLife(float);
void setEndOfLife(float);
double getBeginOfLife() const;
double getEndOfLife() const;
bool get_is_drawn() const;
void set_is_drawn();
To reduce equaly a circl in a certain time with a certain speed, you need:
speed (speed_) value: the speed of the reduice of radius by second,
radius (radius_) value: the initial value of the radius.
Your circle need to have setOrigin to the center.
// your function to reduce a certain circle (class member)
void reduce()
{
float elapsed_time = ; // your time elapsed from the last call
// Getting the position of your center
// if you haven't set the origin to the center of the circle this code doesn't work
sf::Vector2f pos = circle_.getPosition();
radius_ -= (speed_ * elapsed_time); // calculating the new radius
circle_.setRadius(radius_); // set the new radius
circle_.setOrigin(sf::Vector2f(radius_ / 2, radius_ / 2)); // update the origine to the center
circle_.setPosition(pos); // not 100% sure that this line is used
}
Now you have a circle that reduce at a certain speed and staying in the same place.

Handling time in C++

I am writing a simulation program. I have some difficulties when handling the time.
My program has a time period and a total elapsed time variables. I have traffic light objects. I want to change the color of them when the elapsed time is integer multiple of the period. For example, say total time starts from 0 and finishes in 60 seconds and the period is 10 seconds. So, when time is 10, 20, 30, etc. , the color should be changed.
I tried solving this issue with using simple math but nothing has been changed when I draw the objects. So, how can I handle the time to change their colors?
You can use the standard <chrono> library, but SFML has its own set of tools for handling time. You don't need any complicated calculations or threads. A simplified example of part of what you need:
#include <SFML/Graphics.hpp>
#include <vector>
class Light : public sf::CircleShape {
public:
Light(std::vector<sf::Color> cols, sf::Time period)
:colors{ cols }, colorIdx{ 0 }, changePeriod{ period }
{
setRadius(100);
}
void update(sf::Time deltaTime) {
elapsedTime += deltaTime;
while (elapsedTime >= changePeriod) {
elapsedTime -= changePeriod;
changeColor();
}
setColor();
}
protected:
void changeColor() {
if (++colorIdx == colors.size()) {
colorIdx = 0;
}
}
void setColor() {
setFillColor(colors[colorIdx]);
}
private:
std::vector<sf::Color> colors;
std::size_t colorIdx;
sf::Time changePeriod;
sf::Time elapsedTime;
};
int main() {
Light light1({sf::Color::Red, sf::Color::Yellow, sf::Color::Green}, sf::seconds(1));
Light light2({sf::Color::Red, sf::Color::Green}, sf::milliseconds(200));
light2.setPosition(300, 300);
light2.setRadius(20);
sf::RenderWindow window(sf::VideoMode(400, 400), "");
sf::Event event;
sf::Clock clock;
while (window.isOpen()) {
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
sf::Time dt = clock.restart();
light1.update(dt);
light2.update(dt);
window.clear();
window.draw(light1);
window.draw(light2);
window.display();
}
}
You could use delay(milliseconds) with the header file #include<dos.h>. For example if you want your traffic lights to be changed every 10 seconds in 60 seconds you could do the following code:
for(int i = 0; i < 6; i ++)
{
changeColor();
delay(1000);
}
Also, you could use sleep(). You may find the answer to your question here:
http://www.cplusplus.com/forum/beginner/13906/

issue regarding variable scope

I have trouble grasping the concept of variable scope. What is acceptable and what is not acceptable? I am aware that I've left out all of the graphics related code, and I am aware that I have an infinite game loop, but bear with me:
#include "LList.h"
#include "Snake.h"
#undef main
int main()
{
float dt; // time since last update.
int start_time;
bool paused = false;
float originalTime = 1.0f;
float timer = originalTime;
Snake p1Snake(10, false);
// Start the 'stopwatch'
start_time = SDL_GetTicks();
///////////////////////
// The 'game loop' //
///////////////////////
while (!done)
{
//////////////////////
// Update variables //
//////////////////////
// Update the dt value (to be the time since the last update)
dt = (SDL_GetTicks() - start_time) / 1000.0f;
start_time = SDL_GetTicks();
//increment the movement timer
timer-=dt;
if(timer<=0) When timer hits zero the snake is moved north.
{
p1Snake.goNorth();
timer = originalTimer; //reset timer.
}
}
return 0;
}
Okay! So my question is about the variable 'originalTimer'. It is out of scope where the timer is reset, so what can I do differently? Sorry if this is an extremely elementary question.
You used different names. originalTime and originalTimer
#include "LList.h"
#include "Snake.h"
#undef main
int main()
{
float dt; // time since last update.
int start_time;
bool paused = false;
float originalTimer = 1.0f; //Changed to originalTimer
float timer = originalTimer; //Changed to originalTimer
Snake p1Snake(10, false);
// Start the 'stopwatch'
start_time = SDL_GetTicks();
///////////////////////
// The 'game loop' //
///////////////////////
while (!done)
{
//////////////////////
// Update variables //
//////////////////////
// Update the dt value (to be the time since the last update)
dt = (SDL_GetTicks() - start_time) / 1000.0f;
start_time = SDL_GetTicks();
//increment the movement timer
timer-=dt;
if(timer<=0) //When timer hits zero the snake is moved north.
{
p1Snake.goNorth();
timer = originalTimer; //reset timer.
}
}
return 0;
}
May be typo, but there are two different variables originalTime and originalTimer
Changing in code below should work for you..
timer = originalTime; //reset timer.

Design fps limiter

I try to cap the animation at 30 fps. So I design the functions below to achieve the goal. Unfortunately, the animation doesn't behave as fast as no condition checking for setFPSLimit() function when I set 60 fps (DirectX caps game application at 60 fps by default). How should I fix it to make it work?
getGameTime() function counts the time like stopwatch in millisecond when game application starts.
//Called every time you need the current game time
float getGameTime()
{
UINT64 ticks;
float time;
// This is the number of clock ticks since start
if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) )
ticks = (UINT64)timeGetTime();
// Divide by frequency to get the time in seconds
time = (float)(__int64)ticks/(float)(__int64)ticksPerSecond;
// Subtract the time at game start to get
// the time since the game started
time -= timeAtGameStart;
return time;
}
With fps limit
http://www.youtube.com/watch?v=i3VDOMqI6ic
void update()
{
if ( setFPSLimit(60) )
updateAnimation();
}
With No fps limit http://www.youtube.com/watch?v=Rg_iKk78ews
void update()
{
updateAnimation();
}
bool setFPSLimit(float fpsLimit)
{
// Convert fps to time
static float timeDelay = 1 / fpsLimit;
// Measure time elapsed
static float timeElapsed = 0;
float currentTime = getGameTime();
static float totalTimeDelay = timeDelay + getGameTime();
if( currentTime > totalTimeDelay)
{
totalTimeDelay = timeDelay + getGameTime();
return true;
}
else
return false;
}

How to make timer for a game loop?

I want to time fps count, and set it's limit to 60 and however i've been looking throught some code via google, I completly don't get it.
If you want 60 FPS, you need to figure out how much time you have on each frame. In this case, 16.67 milliseconds. So you want a loop that completes every 16.67 milliseconds.
Usually it goes (simply put): Get input, do physics stuff, render, pause until 16.67ms has passed.
Its usually done by saving the time at the top of the loop and then calculating the difference at the end and sleeping or looping doing nothing for that duration.
This article describes a few different ways of doing game loops, including the one you want, although I'd use one of the more advanced alternatives in this article.
delta time is the final time, minus the original time.
dt= t-t0
This delta time, though, is simply the amount of time that passes while the velocity is changing.
The derivative of a function represents an infinitesimal change
in the function with respect to one of its variables.
The derivative of a function with respect to the variable is defined as
f(x + h) - f(x)
f'(x) = lim -----------------
h->0 h
http://mathworld.wolfram.com/Derivative.html
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<windows.h>
#pragma comment(lib,"winmm.lib")
void gotoxy(int x, int y);
void StepSimulation(float dt);
int main(){
int NewTime = 0;
int OldTime = 0;
float dt = 0;
float TotalTime = 0;
int FrameCounter = 0;
int RENDER_FRAME_COUNT = 60;
while(true){
NewTime = timeGetTime();
dt = (float) (NewTime - OldTime)/1000; //delta time
OldTime = NewTime;
if (dt > (0.016f)) dt = (0.016f); //delta time
if (dt < 0.001f) dt = 0.001f;
TotalTime += dt;
if(TotalTime > 1.1f){
TotalTime=0;
StepSimulation(dt);
}
if(FrameCounter >= RENDER_FRAME_COUNT){
// draw stuff
//Render();
gotoxy(1,2);
printf(" \n");
printf("OldTime = %d \n",OldTime);
printf("NewTime = %d \n",NewTime);
printf("dt = %f \n",dt);
printf("TotalTime = %f \n",TotalTime);
printf("FrameCounter = %d fps\n",FrameCounter);
printf(" \n");
FrameCounter = 0;
}
else{
gotoxy(22,7);
printf("%d ",FrameCounter);
FrameCounter++;
}
}
return 0;
}
void gotoxy(int x, int y){
COORD coord;
coord.X = x; coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
return;
}
void StepSimulation(float dt){
// calculate stuff
//vVelocity += Ae * dt;
}
You shouldn't try to limit the fps. The only reason to do so is if you are not using delta time and you expect each frame to be the same length. Even the simplest game cannot guarantee that.
You can however take your delta time and slice it into fixed sizes and then hold onto the remainder.
Here's some code I wrote recently. It's not thoroughly tested.
void GameLoop::Run()
{
m_Timer.Reset();
while(!m_Finished())
{
Time delta = m_Timer.GetDelta();
Time frameTime(0);
unsigned int loopCount = 0;
while (delta > m_TickTime && loopCount < m_MaxLoops)
{
m_SingTick();
delta -= m_TickTime;
frameTime += m_TickTime;
++loopCount;
}
m_Independent(frameTime);
// add an exception flag later.
// This is if the game hangs
if(loopCount >= m_MaxLoops)
{
delta %= m_TickTime;
}
m_Render(delta);
m_Timer.Unused(delta);
}
}
The member objects are Boost slots so different code can register with different timing methods. The Independent slot is for things like key mapping or changing music Things that don't need to be so precise. SingTick is good for physics where it is easier if you know every tick will be the same but you don't want to run through a wall. Render takes the delta so animations run smooth, but must remember to account for it on the next SingTick.
Hope that helps.
There are many good reasons why you should not limit your frame rate in such a way. One reason being as stijn pointed out, not every monitor may run at exactly 60fps, another reason being that the resolution of timers is not sufficient, yet another reason being that even given sufficient resolutions, two separate timers (monitor refresh and yours) running in parallel will always get out of sync with time (they must!) due to random inaccuracies, and the most important reason being that it is not necessary at all.
Note that the default timer resolution under Windows is 15ms, and the best possible resolution you can get (by using timeBeginPeriod) is 1ms. Thus, you can (at best) wait 16ms or 17ms. One frame at 60fps is 16.6666ms How do you wait 16.6666ms?
If you want to limit your game's speed to the monitor's refresh rate, enable vertical sync. This will do what you want, precisely, and without sync issues. Vertical sync does have its pecularities too (such as the funny surprise you get when a frame takes 16.67ms), but it is by far the best available solution.
If the reason why you wanted to do this was to fit your simulation into the render loop, this is a must read for you.
check this one out:
//Creating Digital Watch in C++
#include<iostream>
#include<Windows.h>
using namespace std;
struct time{
int hr,min,sec;
};
int main()
{
time a;
a.hr = 0;
a.min = 0;
a.sec = 0;
for(int i = 0; i<24; i++)
{
if(a.hr == 23)
{
a.hr = 0;
}
for(int j = 0; j<60; j++)
{
if(a.min == 59)
{
a.min = 0;
}
for(int k = 0; k<60; k++)
{
if(a.sec == 59)
{
a.sec = 0;
}
cout<<a.hr<<" : "<<a.min<<" : "<<a.sec<<endl;
a.sec++;
Sleep(1000);
system("Cls");
}
a.min++;
}
a.hr++;
}
}