Hi there, I need some help with my game's timestep. And yes, I've read over the infamous and all-too reposted gaffer article: gafferongames.com/game-physics/fix-your-timestep/
Basically my current game loop code is broken and passes a deltatime through each update(elapsedTime) method. Then movements are multiplied based on that value, of course.
My game does need to be networked, but I'm hoping that such a thing doesn't change much, (essentially I'll just have the clients act as dumb input terminals and the server will tell everyone the state, it's only for a small number of players so it should be fine and as such will be authoritative).
The parts I do not understand about *this* solution, is how the State struct fits into all of this. Why is it only 1 dimensional? Do I add y (it's a 2d game) as well in a same similar manner, along with vy to make this usable for 2d?
Why is it interpolating the rendering itself and not collision and such?
If I have a world->update() call which updates all the entities and checks collision on them..what do I pass to them and how do I use this in a similar manner as I was doing?
I don't see how just interpolating the rendering would result in something usable?
Where do I place the input collection code and the update (collision/physics etc.) code, as well as networking. Especially since I have a handleEvents() { while (SDL_PollEvent(&event)) {.. }.. } Can I just place all of these in one area? Where, exactly, in the main loop?
I've scoured the internet and unfortunately have yet to find a *real* example that uses a fixed time step
I'm posting the code below so that it's easy to talk about as I need to understand it, and a lot of it I do not, and so people can hopefully walk me through the parts I'm confused on.
Thanks, really hoping someone here can help me understand and use this code. Game loops seem to be an area where from a distance they seem obvious and simple/straight forward, but of course, that is not the case at all.
struct State
{
float x;
float v;
};
struct Derivative
{
float dx;
float dv;
};
State interpolate(const State &previous, const State ¤t, float alpha)
{
State state;
state.x = current.x*alpha + previous.x*(1-alpha);
state.v = current.v*alpha + previous.v*(1-alpha);
return state;
}
float acceleration(const State &state, float t)
{
const float k = 10;
const float b = 1;
return - k*state.x - b*state.v;
}
Derivative evaluate(const State &initial, float t)
{
Derivative output;
output.dx = initial.v;
output.dv = acceleration(initial, t);
return output;
}
Derivative evaluate(const State &initial, float t, float dt, const Derivative &d)
{
State state;
state.x = initial.x + d.dx*dt;
state.v = initial.v + d.dv*dt;
Derivative output;
output.dx = state.v;
output.dv = acceleration(state, t+dt);
return output;
}
void integrate(State &state, float t, float dt)
{
Derivative a = evaluate(state, t);
Derivative b = evaluate(state, t, dt*0.5f, a);
Derivative c = evaluate(state, t, dt*0.5f, b);
Derivative d = evaluate(state, t, dt, c);
const float dxdt = 1.0f/6.0f * (a.dx + 2.0f*(b.dx + c.dx) + d.dx);
const float dvdt = 1.0f/6.0f * (a.dv + 2.0f*(b.dv + c.dv) + d.dv);
state.x = state.x + dxdt*dt;
state.v = state.v + dvdt*dt;
}
int main()
{
const int width = 640;
const int height = 480;
if (!openDisplay("Timestep", width, height))
return 1;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, 4.0/3.0, 1, 1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0,0,150, 0,0,0, 0,1,0);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glEnable(GL_POINT_SMOOTH);
glPointSize(4);
glClearColor(0.3f, 0.3f, 0.3f, 1);
State current;
current.x = 100;
current.v = 0;
State previous = current;
float t = 0.0f;
float dt = 0.1f;
float currentTime = 0.0f;
float accumulator = 0.0f;
while (!quit)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
const float newTime = time();
float deltaTime = newTime - currentTime;
currentTime = newTime;
if (deltaTime>0.25f)
deltaTime = 0.25f;
accumulator += deltaTime;
while (accumulator>=dt)
{
accumulator -= dt;
previous = current;
integrate(current, t, dt);
t += dt;
}
State state = interpolate(previous, current, accumulator/dt);
glBegin(GL_POINTS);
glColor3f(1,1,1);
glVertex3f(state.x, 0, 0);
glEnd();
updateDisplay();
}
closeDisplay();
return 0;
}