Separating event updates from time updates

Started by
1 comment, last by Mybowlcut 12 years, 11 months ago
In my previous games I've used separate update functions for input events and time-based "delta" updates. E.g. event updates would set the direction an object was moving:
[source]void Human_Controller::Event_Update(const SDL_Event* event_)
{
using namespace Direction;
using namespace SDL_Tools;

unit_ptr commander((*map->Get_Players())[PLAYER]->Get_Commander());

if(responsive && commander && commander->Is_Alive())
{
if(event_->type == SDL_KEYDOWN || event_->type == SDL_KEYUP)
{
// Using GetKeyState works much better for multiple keys being held down.
unsigned char* keys = SDL_GetKeyState(NULL);

if(keys[SDLK_LEFT] || keys[SDLK_a])
{
commander->Set_Direction(PointF(-1.0f, commander->Get_Direction().y));
}
else if(keys[SDLK_RIGHT] || keys[SDLK_d])
{
commander->Set_Direction(PointF(1.0f, commander->Get_Direction().y));
}
else
{
commander->Set_Direction(PointF(0, commander->Get_Direction().y));
}

if(keys[SDLK_UP] || keys[SDLK_w])
{
commander->Set_Direction(PointF(commander->Get_Direction().x, -1.0f));
}
else if(keys[SDLK_DOWN] || keys[SDLK_s])
{
commander->Set_Direction(PointF(commander->Get_Direction().x, 1.0f));
}
else
{
commander->Set_Direction(PointF(commander->Get_Direction().x, 0));
}
}
}
}[/source]

and time-based updates would move it in that direction, decreasing its velocity slightly each frame:
[source]void Human_Controller::Time_Update(float delta)
{
player->Update(delta);

player->Get_Totals().Set_Seconds_Elapsed(player->Get_Totals().Get_Seconds_Elapsed() + delta);

const Unit_Data* unit_data = NULL;
unit_it begin = player->Get_Units().begin(), end = player->Get_Units().end();

unit_ptr commander(player->Get_Commander());
if(commander)
{
PointF old_pos = commander->Get_Position();

commander->Update(delta);

if(!commander->Is_Alive())
{
float speed = GET_UNIT_DATA(commander->Get_Type_ID()).Get_Movement_Speed();
GET_UNIT_DATA(commander->Get_Type_ID()).Set_Movement_Speed(speed *= 0.99f);
}

commander->Set_Position(commander->Get_Position() +
commander->Get_Direction() * GET_UNIT_DATA(*commander).Get_Movement_Speed() * delta);

if(commander->Is_Alive() && commander->Get_Position() != old_pos)
{
Refocus_Turrets(*commander);
}
}
}[/source]

Now I'm working with a different library (SFML) that has a function GetFrameTime in the window class, and I'm wondering if it's still worth separating these update functions. I think it is, because this way you have nicely separated functions, and it's easy to control the game speed if necessary. What are others' opinions on this?

Advertisement

In my previous games I've used separate update functions for input events and time-based "delta" updates. E.g. event updates would set the direction an object was moving:
[source]void Human_Controller::Event_Update(const SDL_Event* event_)
}[/source]

and time-based updates would move it in that direction, decreasing its velocity slightly each frame:
[source]void Human_Controller::Time_Update(float delta)
}[/source]

Now I'm working with a different library (SFML) that has a function GetFrameTime in the window class, and I'm wondering if it's still worth separating these update functions. I think it is, because this way you have nicely separated functions, and it's easy to control the game speed if necessary. What are others' opinions on this?


I would maintain the separation myself as depending on the type of game and the processing pipeline both are valid items. Basically the separation allows you to maintain an impulse versus interpolation model. What I mean by this is that for instance in the game I'm writing right now for hobby fun I only actually simulate the world 10 times a second and interpolate the positions/orientations of everything in between. The simulation is not that expensive that I couldn't simulate faster, in fact I intend to crank it up later, but right now the idea is to make sure everything is rock solid with deterministic equations. So, the "inputs" are pulled frame by frame but only added to the simulation as impulses at the next simulation update.

Depending on your game it may not make much sense to maintain the separation but having it available really can't hurt for the most part. I usually encapsulate this sort of stuff in a "services" framework myself. Basically I use a singleton where I register services which can be evaluated once a frame or at fixed intervals. I.e.:


class DLL_LINKAGE Service : public Core::iRefCount
{
public:
// Interface.
virtual void Update( const float deltat )=0;
virtual uint32_t Priority() const {return 0;}
virtual float Rate() const {return 0.0f;}
};

I derive things off of that then register with a singleton manager I update once a frame. Priority just allows ordering, rate allows it to be triggered every "x" seconds when non-zero.

With your example I'd put the input in a priority 100 0.0 rate service and let it feed inputs as needed, ignoring the delta time input. This makes the entire main loop nice since for the most part it is just "ServiceManager::Update( delta )" and the details of separate input types are hidden in the services.
Interesting perspective. Thanks for the reply. :)

This topic is closed to new replies.

Advertisement