How can set up time elapsed based events separate from the game loop?
I want the character to heal over real time rather than per cycle of the game loop. How might i do that?
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It only takes a minute to sign up.
Sign up to join this communityHow can set up time elapsed based events separate from the game loop?
I want the character to heal over real time rather than per cycle of the game loop. How might i do that?
This is a very wide question, and difficult to answer specifically, so I will be general instead:
Any "over time" effects will still be based on your game loop, but have their own "tick" of time they are bound to. Diablo 3 does this for damage and healing over time effects. The tool tips list the amounts as "per second" but it is actually applied "per tick", which is more than a frame (max 16ms), but less than a second.
An example could be:
//Base effect class
class OverTimeEffect
{
public:
virtual void apply(Character& c, float dt)=0;
void setTickSize(float tick);
void setDuration(float seconds);
float getRemaining();
protected:
float m_tick_s;
float m_duration;
float m_accum;
float m_remaining;
};
class HealOverTime:
public OverTimeEffect
{
public:
virtual void apply(Character& c, float dt)
{
if(m_accum + dt >= m_tick_s)
{
c.addHealth(1);
m_accum = 0.0f;
m_remaining-=dt;
}
else {
m_accum += dt;
m_remaining-=dt;
}
}
};
// This is your game character base class
class Character
{
public:
void addHealth(int hp);
void addOTE(OverTimeEffect& ote)
{
m_ote.push_back(ote);
//then min sort by remaining time in effect
//so shortest duration is always at the front of the queue
}
void update(float dt)
{
for (auto& effect: m_ote)
{
effect.apply(*this,dt);
if(effect.getRemaining() <= 0.0f)
{
m_ote.pop_front();
}
}
}
private:
deque<OverTimeEffect> m_ote;
float m_hp;
};
This is but one of the many, many ways you could implement such effects. I recommend further reading up on game engine architecture books and articles, as this is a very common feature of games, and so there is likely to be a solution that will suit your game.
Time elapsed events are not separate from the game loop but are controlled from the game loop.
The easiest way to achieve the effect is to have a floating-point value that represents the number of seconds the event should be active.
From the game loop you call an update function that takes the number of seconds since the last frame: Update(float deltaSeconds);
and in that you increment an internal value based on that elapsed time. If it's greater or equal to your pre-determined length of time you stop:
Example Pseudo-code:
class Player {
//Other stuff...
public:
void Update(float deltaSeconds);
void DoHealOverTime(float deltaSeconds);
float maxTimeToHeal = 5.0f;
float currentTimeToHeal = 0.0f;
float health = 0.0f;
float healthPerSecond = 1.0f;
float maxHealth = 100.0f;
bool isHealing = false;
};
void Player::Update(float deltaSeconds) {
DoHealOverTime(deltaSeconds);
}
void Player::DoHealOverTime(float deltaSeconds) {
if(!isHealing || maxHealth < health) {
return;
}
timeToHeal += deltaSeconds;
health += healthPerSecond * deltaSeconds;
if(maxTimeToHeal < timeToHeal) {
isHealing = false;
timeToHeal = 0.0f;
}
//std::clamp is a better use here, but need c++17 compiler.
//Parenthesis around function names are intentional to avoid name-collisions with preexisting macros.
health = (std::max)(0.0f, (std::min)(health, maxHealth));
}