Skip to content

Instantly share code, notes, and snippets.

@paulhoux
Created May 21, 2015 22:52
Show Gist options
  • Save paulhoux/a595f7c08130f769ba3a to your computer and use it in GitHub Desktop.
Save paulhoux/a595f7c08130f769ba3a to your computer and use it in GitHub Desktop.
Sample showing how to perform a fixed number of updates per second, resulting in frame rate independent animation.
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/params/Params.h"
#include "cinder/Rand.h"
using namespace ci;
using namespace ci::app;
using namespace std;
class FixedTimestepApp : public App {
public:
void setup() override;
void update() override;
void draw() override;
void animate();
void setVerticalSync( bool enabled ) { gl::enableVerticalSync( enabled ); }
bool hasVerticalSync() const { return gl::isVerticalSyncEnabled(); }
void setFixedFramerate( bool enabled ) { if( enabled ) setFrameRate( 20 ); else disableFrameRate(); }
bool hasFixedFramerate() const { return isFrameRateEnabled(); }
void setFixedTimestep( bool enabled ) { mFixedTimestepEnabled = enabled; }
bool hasFixedTimestep() const { return mFixedTimestepEnabled; }
private:
params::InterfaceGlRef mParams;
ci::vec2 mPosition;
ci::vec2 mTarget;
ci::vec2 mVelocity;
bool mFixedTimestepEnabled = false;
int mFrameRate;
};
void FixedTimestepApp::setup()
{
// Disable the frame rate...
disableFrameRate();
// ...and use vertical sync to limit it.
gl::enableVerticalSync( true );
// Create params, so we can tweak settings.
mParams = params::InterfaceGl::create( "Settings", ivec2( 250, 120 ) );
mParams->setOptions( "", "valueswidth=50" );
mParams->addParam<int>( "Frame rate", &mFrameRate, true );
mParams->addSeparator();
mParams->addParam<bool>( "Enable Vertical Sync",
std::bind( &FixedTimestepApp::setVerticalSync, this, std::placeholders::_1 ),
std::bind( &FixedTimestepApp::hasVerticalSync, this ) );
mParams->addParam<bool>( "Enable Fixed Frame Rate",
std::bind( &FixedTimestepApp::setFixedFramerate, this, std::placeholders::_1 ),
std::bind( &FixedTimestepApp::hasFixedFramerate, this ) );
mParams->addParam<bool>( "Enable Fixed Timestep",
std::bind( &FixedTimestepApp::setFixedTimestep, this, std::placeholders::_1 ),
std::bind( &FixedTimestepApp::hasFixedTimestep, this ) );
}
void FixedTimestepApp::update()
{
if( !mFixedTimestepEnabled ) {
// Update once per frame. This will make our animation frame rate dependent.
animate();
}
else {
// Use a fixed time step for a steady 60 updates per second.
static const double timestep = 1.0 / 60.0;
// Keep track of time.
static double time = getElapsedSeconds();
static double accumulator = 0.0;
// Calculate elapsed time since last frame.
double elapsed = getElapsedSeconds() - time;
time += elapsed;
// Update the animation.
accumulator += math<double>::min( elapsed, 0.1 ); // prevents 'spiral of death'
while( accumulator > timestep ) {
animate();
accumulator -= timestep;
}
}
mFrameRate = (int) ceil( getAverageFps() );
}
void FixedTimestepApp::draw()
{
gl::clear();
// Draw line connecting target and position.
gl::color( 1, 1, 0 );
gl::drawLine( mTarget, mPosition );
// Draw target.
gl::color( 1, 0, 0 );
gl::drawSolidCircle( mTarget, 5.0f );
// Draw position.
gl::color( 1, 1, 1 );
gl::drawSolidCircle( mPosition, 5.0f );
// Draw settings.
mParams->draw();
}
void FixedTimestepApp::animate()
{
// Perform animation using simple Euler integration.
// This kind of animation often depends on a fixed number of
// updates per second.
// Choose a new target if position is too close.
float dist = glm::distance( mTarget, mPosition );
if( dist < 50.0f ) {
mTarget.x = Rand::randFloat( 150.0f, getWindowWidth() - 150.0f );
mTarget.y = Rand::randFloat( 150.0f, getWindowHeight() - 150.0f );
}
// Change velocity.
auto direction = glm::normalize( mTarget - mPosition );
mVelocity = 0.995f * mVelocity + 0.1f * direction;
// Change position.
mPosition += mVelocity;
}
CINDER_APP( FixedTimestepApp, RendererGl )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment