Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have some DOT (damage over time) implementation problems. My game runs on 30 FPS speed. Current implementation is: let's say the hero casts a spell which deals 1 damage per second. On every frame I do (pseudo code):

damage_done = getRandomDamage() * delta_time;

I accumulate damage and when it becomes more than 0, I subtract rounded damage from current health and so on. With 30 FPS and 1 DPS it will be 1/33 = 0.05... We know that floats are not precise enough to sum 30 circulating decimals and have exactly 1 in the end. But HP is a discrete value and that's why 1 DPS will not result in 1 damage after 1 second, because the value will be 0.9999... instead. It's not such a big deal when you have 100000 DPS, ±1 damage will not be noticeable. But what if I have 1,5 DPS? How do modern RPGs implement DOTs?

share|improve this question
    
...I'm confused about why you're tracking damage with floating point numbers, when health apparently only exists as integers in your system. Why not just use integers for damage, if that's what you want? –  Trevor Powell Nov 19 '12 at 21:55
    
How do you calculate 1 dps? –  Denis Ermolin Nov 20 '12 at 5:24
    
1 dps is 1 damage every 1 second. 2 dps is 1 damage every 0.5 seconds. 10 dps is 1 damage every 0.1 seconds. 0.1 dps is 1 damage every 10 seconds. The time between applications of 1 point of damage is 1.0/[dps] seconds. I'm just saying that if you're declaring that 'health' is an integer value, that you should be treating 'damage' as a strictly integer value as well, or you're going to run into this sort of issue everywhere. –  Trevor Powell Nov 20 '12 at 9:57
add comment

2 Answers

up vote 4 down vote accepted

In my experience, i prefer to use a single "big" hit every so often than to accumulate a lot of very small differences.

I would recomend you to use a frequency (for the sake of simplicity i'd go with 1 second) and on your update loop check how much time has passed since the last time the dot has applied damage. Once you get to a time where you know damage has to be applied, you apply it, reset the counter and wait for the next tick of the dot.

float dot_frequency = DOT_FREQUENCY;
bool apply_first_tick_immediately = true; //<--With this you control the behavior as you asked

void on_dot_applied()
{
    if (apply_first_tick_immediately)
        time_since_last_tick = dot_frequency;
    else
        time_since_last_tick = 0;
}

void update(float elapsed_time)
{
    time_since_last_tick += elapsed_time;
    time_since_start += elapsed_time;
    if (time_since_last_tick >= dot_frequency)
    {
        apply_dot_damage();  // <-- here get your formula for dot damage
        time_since_last_tick -= dot_frequency;
    }
    if (time_since_start >= DOT_DURATION)
    {
        remove_dot();
    }
 }

I hope this helps.

EDIT: Added an example of how you can work with the "deal damage immediately" idea. Probably the on_dot_applied is an event that is called immediately as a dot is applied to setup certain effects of the specific dot, such as a fire particle effect, maybe a shader change.

share|improve this answer
    
But what if i have a fire pool where player damaged for 5 dps. If pool will "tick" only every second then player can run on it and leave without damage taken. I assume that but is it fair for players? –  Denis Ermolin Nov 19 '12 at 12:03
    
If you always want at least one tick of such damage, simply start your "time_since_last_tick" in a value equal to your frequency. You may get a slightly more complicated exit condition in some cases, but as far as i can see is just a matter of shifting your ticks to start inmediately instead of waiting one tick. Other option that comes to mind, you could simply apply the first tick's damage as soon as a player enters into such pool and the rest of the damage could be managed as a dot. –  Carlos Nov 19 '12 at 12:14
    
Hm, this is a good idea to reverse damage deal, deal then wait, instead of wait->deal damage. You can update your answer) –  Denis Ermolin Nov 19 '12 at 12:19
add comment

If you're worried about imprecisions, you could simply apply the damage every second:

time_last_damage += delta_time;
if (time_last_damage >= 1.0f)
{
    time_elapsed = floor(time_last_damage);

    damage_done = getRandomDamage() * time_elapsed;

    time_last_damage -= time_elapsed; // keep fractional time
}
share|improve this answer
    
This is how DotA 2 does it - Once per half second. –  Ruirize Nov 19 '12 at 11:55
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.