diff --git a/src/Makefile.am b/src/Makefile.am index 7180e9ae..22461872 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ libcommon_a_SOURCES = \ game.cpp game.h tiler.cpp tiler.h tileset.cpp tileset.h \ scene.cpp scene.h font.cpp font.h layer.cpp layer.h map.cpp map.h \ joystick.cpp joystick.h asset.cpp asset.h ticker.cpp ticker.h \ - forge.cpp forge.h video.cpp video.h \ + forge.cpp forge.h video.cpp video.h timer.cpp timer.h \ debugfps.cpp debugfps.h libcommon_a_CXXFLAGS = `pkg-config --cflags sdl gl SDL_image` diff --git a/src/debugfps.cpp b/src/debugfps.cpp index a0325403..4231fcc6 100644 --- a/src/debugfps.cpp +++ b/src/debugfps.cpp @@ -65,7 +65,7 @@ void DebugFps::TickRender(float delta_time) char buf[1024]; sprintf(buf, "%3.2f ms (%3.2f fps) -- max %3.2f ms -- #%i", - mean, 1000.0f / mean, max, data->frame); + 1000.0f * mean, 1.0f / mean, 1000.0f * max, data->frame); data->font->Print(10, 10, buf); data->font->Print(11, 10, buf); data->font->Print(10, 11, buf); diff --git a/src/gtk/editor.cpp b/src/gtk/editor.cpp index dea7eb3f..f423325c 100644 --- a/src/gtk/editor.cpp +++ b/src/gtk/editor.cpp @@ -21,9 +21,8 @@ static volatile int quit = 0; -static GTimer *timer; -static float delta_time; static int ticking = 0; +static float const FPS = 30.0f; static gint main_quit(GtkWidget *widget, GdkEventExpose *event) { @@ -38,8 +37,6 @@ static gint main_quit(GtkWidget *widget, GdkEventExpose *event) static gboolean tick(void *widget) { // FIXME: do not do anything if the previous tick was too recent? - delta_time = 1000.0f * g_timer_elapsed(timer, NULL); - g_timer_start(timer); // FIXME: only quit if all assets have been cleaned if (quit) @@ -48,7 +45,7 @@ static gboolean tick(void *widget) ticking = 1; /* Tick the game */ - Ticker::TickGame(delta_time); + Ticker::TickGame(); gtk_widget_draw(GTK_WIDGET(widget), NULL); @@ -82,8 +79,9 @@ static gint draw(GtkWidget *widget, GdkEventExpose *event) /* Clear the screen, tick the renderer, and show the frame */ Video::Clear(); - Ticker::TickRender(delta_time); + Ticker::TickRender(); gtk_gl_area_swapbuffers(GTK_GL_AREA(widget)); + Ticker::ClampFps(FPS); } return TRUE; @@ -158,9 +156,8 @@ int main(int argc, char **argv) new DebugFps(); //gtk_idle_add(tick, glarea); - gtk_timeout_add(33, tick, glarea); + gtk_timeout_add(1000 / FPS, tick, glarea); - timer = g_timer_new(); gtk_main(); return EXIT_SUCCESS; diff --git a/src/test-map.cpp b/src/test-map.cpp index fcc868d1..842cae9e 100644 --- a/src/test-map.cpp +++ b/src/test-map.cpp @@ -18,6 +18,8 @@ #include "ticker.h" #include "video.h" +static int const FPS = 30.0f; + int main(int argc, char **argv) { /* Initialise SDL */ @@ -39,9 +41,6 @@ int main(int argc, char **argv) SDL_ShowCursor(0); SDL_WM_GrabInput(SDL_GRAB_ON); - /* Initialise timer */ - Uint32 ticks = SDL_GetTicks(); - /* Initialise OpenGL */ Video::Setup(video->w, video->h); @@ -54,22 +53,16 @@ int main(int argc, char **argv) while (!game->Finished()) { - /* Compute delta time */ - Uint32 newticks = SDL_GetTicks(); - float delta_time = (float)(newticks - ticks); - ticks = newticks; - /* Tick the game */ - Ticker::TickGame(delta_time); + Ticker::TickGame(); /* Clear the screen, tick the renderer, and show the frame */ Video::Clear(); - Ticker::TickRender(delta_time); + Ticker::TickRender(); SDL_GL_SwapBuffers(); /* Clamp to desired framerate */ - while (SDL_GetTicks() < ticks + (33.33333f - 0.5f)) - SDL_Delay(1); + Ticker::ClampFps(FPS); } SDL_Quit(); diff --git a/src/ticker.cpp b/src/ticker.cpp index 6d3e24ab..4aadc544 100644 --- a/src/ticker.cpp +++ b/src/ticker.cpp @@ -13,6 +13,7 @@ #include "ticker.h" #include "asset.h" +#include "timer.h" /* * Ticker implementation class @@ -29,6 +30,8 @@ public: { for (int i = 0; i < Asset::GROUP_COUNT; i++) list[i] = NULL; + timer = new Timer(); + bias = 0.0f; } ~TickerData() @@ -37,12 +40,19 @@ public: if (nassets) fprintf(stderr, "ERROR: still %i assets in ticker\n", nassets); #endif + delete timer; } private: + /* Asset management */ Asset *todo; Asset *list[Asset::GROUP_COUNT]; int nassets; + + /* FPS management */ + float delta_time; + Timer *timer; + float bias; } tickerdata; @@ -61,8 +71,11 @@ void Ticker::Register(Asset *asset) data->todo = asset; } -void Ticker::TickGame(float delta_time) +void Ticker::TickGame() { + data->delta_time = data->timer->GetSeconds(); + data->bias += data->delta_time; + /* Insert waiting objects in the appropriate lists */ while (data->todo) { @@ -93,15 +106,23 @@ void Ticker::TickGame(float delta_time) for (int i = 0; i < Asset::GROUP_COUNT; i++) for (Asset *a = data->list[i]; a; a = a->next) if (!a->destroy) - a->TickGame(delta_time); + a->TickGame(data->delta_time); } -void Ticker::TickRender(float delta_time) +void Ticker::TickRender() { /* Tick objects for the render loop */ for (int i = 0; i < Asset::GROUP_COUNT; i++) for (Asset *a = data->list[i]; a; a = a->next) if (!a->destroy) - a->TickRender(delta_time); + a->TickRender(data->delta_time); +} + +void Ticker::ClampFps(float fps) +{ + float ideal_time = 1.0f / fps; + + data->timer->WaitSeconds(ideal_time - data->bias); + data->bias -= ideal_time; } diff --git a/src/ticker.h b/src/ticker.h index a0e93509..d40c6390 100644 --- a/src/ticker.h +++ b/src/ticker.h @@ -21,8 +21,9 @@ class Ticker public: static void Register(Asset *asset); - static void TickGame(float delta_time); - static void TickRender(float delta_time); + static void TickGame(); + static void TickRender(); + static void ClampFps(float fps); }; #endif // __DH_TICKER_H__ diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 00000000..92543173 --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,113 @@ +// +// Deus Hax (working title) +// Copyright (c) 2010 Sam Hocevar +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#if defined __linux__ +# include +#elif defined _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#else +# include +#endif + +// XXX +#include + +#include "timer.h" + +/* + * Timer implementation class + */ + +class TimerData +{ + friend class Timer; + +private: + TimerData() + { +#if defined __linux__ + gettimeofday(&tv0, NULL); +#elif defined _WIN32 + LARGE_INTEGER tmp; + QueryPerformanceFrequency(&tmp); + seconds_per_cycle = 1.0f / tmp.QuadPart; + QueryPerformanceCounter(&cycles0); +#else + SDL_Init(SDL_INIT_TIMER); + ticks0 = SDL_GetTicks(); +#endif + } + + float GetSeconds(bool update) + { + float ret; +#if defined __linux__ + struct timeval tv; + gettimeofday(&tv, NULL); + ret = 1e-6f * (tv.tv_usec - tv0.tv_usec) + (tv.tv_sec - tv0.tv_sec); + if (update) + tv0 = tv; +#elif defined _WIN32 + LARGE_INTEGER cycles; + QueryPerformanceCounter(&cycles); + ret = seconds_per_cycle * (cycles.QuadPart - cycles0.QuadPart); + if (update) + cycles0 = cycles; +#else + Uint32 ticks = SDL_GetTicks(); + ret = 1e-3f * (ticks - ticks0); + if (update) + ticks0 = ticks; +#endif + return ret; + } + +#if defined __linux__ + struct timeval tv0; +#elif defined _WIN32 + float seconds_per_cycle; + LARGE_INTEGER cycles0; +#else + Uint32 ticks0; +#endif +}; + +/* + * Timer public class + */ + +Timer::Timer() +{ + data = new TimerData(); +} + +Timer::~Timer() +{ + delete data; +} + +float Timer::GetSeconds() +{ + return data->GetSeconds(true); +} + +void Timer::WaitSeconds(float seconds) +{ + float sleep = seconds - data->GetSeconds(false); + if (sleep <= 1e-4f) + return; + + SDL_Delay((int)(sleep * 1000.0f + 0.5f)); +} + diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 00000000..84bfdbc3 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,30 @@ +// +// Deus Hax (working title) +// Copyright (c) 2010 Sam Hocevar +// + +// +// The Timer class +// --------------- +// + +#if !defined __DH_TIMER_H__ +#define __DH_TIMER_H__ + +class TimerData; + +class Timer +{ +public: + Timer(); + ~Timer(); + + float GetSeconds(); + void WaitSeconds(float milliseconds); + +private: + TimerData *data; +}; + +#endif // __DH_TIMER_H__ +