Managing Different Update Frequencies
What's the best way to handle objects that need to update at different frequencies?
Handling objects with different update frequencies is a common challenge in game development. Here are some strategies to manage this effectively:
Delta Time and Accumulation
Use delta time and accumulate it for each object. When the accumulated time reaches the object's update interval, perform the update.
#include <iostream>
#include <memory>
#include <vector>
class GameObject {
public:
GameObject(float updateInterval) :
updateInterval(updateInterval) {}
virtual void Tick(float deltaTime) {
accumulatedTime += deltaTime;
if (accumulatedTime >= updateInterval) {
Update();
accumulatedTime -= updateInterval;
}
}
virtual void Update() = 0;
private:
float updateInterval;
float accumulatedTime{0};
};
class FastObject : public GameObject {
public:
FastObject() : GameObject(0.016f) {} // 60 FPS
void Update() override {
std::cout << "Fast object updated\n";
}
};
class SlowObject : public GameObject {
public:
SlowObject() : GameObject(1.0f) {} // 1 FPS
void Update() override {
std::cout << "Slow object updated\n";
}
};
int main() {
std::vector<std::unique_ptr<GameObject>>
objects;
objects.push_back(
std::make_unique<FastObject>());
objects.push_back(
std::make_unique<SlowObject>());
float deltaTime = 0.016f; // Assume 60 FPS
for (int frame = 0; frame < 120; ++frame) {
for (auto& obj : objects) {
obj->Tick(deltaTime);
}
}
return 0;
}
Update Groups
Group objects by their update frequency and tick each group separately.
class World {
public:
void AddObject(
std::unique_ptr<GameObject> obj,
UpdateFrequency freq) {
switch (freq) {
case UpdateFrequency::EveryFrame:
everyFrameObjects.push_back(
std::move(obj));
break;
case UpdateFrequency::Every10Frames:
every10FramesObjects.push_back(
std::move(obj));
break;
// Add more cases as needed
}
}
void TickAll(int frameCount) {
for (auto& obj : everyFrameObjects) {
obj->Tick();
}
if (frameCount % 10 == 0) {
for (auto& obj : every10FramesObjects) {
obj->Tick();
}
}
// Add more update groups as needed
}
private:
std::vector<std::unique_ptr<GameObject>>
everyFrameObjects;
std::vector<std::unique_ptr<GameObject>>
every10FramesObjects;
};
Fixed Timestep
Implement a fixed timestep system that allows objects to update at multiples of a base timestep.
#include <chrono>
#include <thread>
class FixedTimestepWorld {
public:
void Run() {
const double dt = 1.0 / 60.0;
// 60 FPS base timestep
double currentTime = GetTime();
double accumulator = 0.0;
while (true) {
double newTime = GetTime();
double frameTime = newTime - currentTime;
currentTime = newTime;
accumulator += frameTime;
while (accumulator >= dt) {
TickAll(dt);
accumulator -= dt;
}
Render();
}
}
private:
double GetTime() {
using namespace std::chrono;
return duration_cast<duration<double>>(
system_clock::now().time_since_epoch())
.count();
}
void TickAll(double dt) {
for (auto& obj : objects) { obj->Tick(dt); }
}
void Render() {
// Render game objects
}
std::vector<std::unique_ptr<GameObject>> objects;
};
These strategies allow you to manage objects with different update frequencies while maintaining a smooth and consistent game loop. Choose the approach that best fits your game's architecture and performance requirements.
Ticking
Using Tick()
functions to update game objects independently of events