How to apply effects that occur (or change) over time to characters in a game?
- by Joshua Harris
So assume that I have a system that applies Effects to Characters like so:
public class Character {
private Collection<Effect> _effects;
public void AddEffect (Effect e) {
e.ApplyTo(this);
_effects.Add(e);
}
public void RemoveEffect (Effect e) {
e.RemoveFrom(this);
_effects.Remove(e);
}
}
public interface Effect {
public void ApplyTo (Character character);
public void RemoveFrom (Character character);
}
Example Effect: Armor Buff for 5 seconds.
void someFunction() {
// Do Stuff ...
Timer armorTimer = new Timer(5 seconds);
ArmorBuff armorbuff = new ArmorBuff();
character.AddEffect(armorBuff);
armorTimer.Start();
// Do more stuff ...
}
// Some where else in code
public void ArmorTimer_Complete() {
character.RemoveEffect(armorBuff);
}
public class ArmorBuff implements Effect {
public void applyTo(Character character) { character.changeArmor(20); }
public void removeFrom(Character character) { character.changeArmor(-20); }
}
Ok, so this example would buff the Characters armor for 5 seconds. Easy to get working. But what about effects that change over the duration of the effect being applied. Two examples come to mind:
Damage Over Time: 200 damage every second for 3 seconds. I could mimic this by applying an Effect that lasts for 1 second and has a counter set to 3, then when it is removed it could deal 200 damage, clone itself, decrement the counter of the clone, and apply the clone to the character. If it repeats this until the counter is 0, then you got a damage over time ability.
I'm not a huge fan of this approach, but it does describe the behavior exactly.
Degenerating Speed Boost: Gain a speed boost that degrades over 3 seconds until you return to your normal speed. This is a bit harder. I can basically do the same thing as above except having timers set to some portion of a second, such that they occur fast enough to give the appearance of degenerating smoothly over time (even though they are really just stepping down incrementally).
I feel like you could get away with only 12 steps over a second (maybe less, I would have to test it and see), but this doesn't seem very elegant to me. The only other way to implement this effect would be to change the system so that the Character checks the _effects collection for effects that alter any of the properties any time that they are being used. I could handle this in functions like getCurrentSpeed() and getCurrentArmor(), but you can imagine how much of a hassle it would be to have that kind of overhead every time you want to do a calculation with movement speed (which would be every time you move your character).
Is there a better way to deal with these kinds of effects or events?