Hysteresis

Originally posted to Shawn Hargreaves Blog on MSDN, Monday, April 23, 2007

Games often have to make discrete decisions based on continuously varying analog input values.

This happens a lot when making animation systems respond to controller inputs:

    if (speed > 0.7)
        ShowRunAnimation();
    else if (speed > 0.1)
        ShowWalkAnimation();
    else
        ShowStandStillAnimation();

Also when making AI decisions:

    if (health > 0.5)
        AttackEnemy();
    else
        RunAway();

And when playing sounds in response to the state of a physics simulation:

    if (sidewaysVelocity > frictionFactor)
        PlaySkidSound();
    else
        StopSkidSound();

And when rendering graphics:

    DrawThreeClosestEnemiesAtHighDetail();
    DrawAllOtherEnemiesAtLowerDetail();

In fact this sort of thing happens all over the place. Games are complex simulations that contain a lot of chaotically varying information, and they often need to examine the simulation state and make decisions based on specific values.

The problem is, game simulations tend to produce a lot of small and basically random fluctuations from one frame to the next. When these values are used to control a discrete decision, and when the input value is close to the decision threshold, the results can look pretty silly:

This stuff is distracting and breaks the suspension of disbelief.

The solution is to add hysteresis in your decision making code. Any time you find yourself making a boolean decision by testing a floating point value:

    if (inputValue > threshold)
        DoSomething();
    else
        OtherThing();

you should think about including a hysteresis region:

    const float hysteresisAmount = 0.1;

    if (inputValue > threshold + hysteresisAmount)
        DoSomething();
    else if (inputValue < threshold - hysteresisAmount)
        OtherThing();
    else
        KeepOnDoingWhateverYouAlreadyAre();

This prevents tiny fluctuations from causing unwanted flickering in the decision result. If the input value is significantly above or below the decision threshold the game will change state, but when the input is near the threshold it just keeps on doing whatever it had previously decided.

Smaller hysteresis amounts make the decision change more easily (increasing the danger of flickering), while larger values make it more stable and less inclined to change state.

The need for hysteresis is not unique to games. Similar situations crop up all the time in real life engineering. Consider a water heater hooked up to a thermostat, for instance. If the thermostat was too eager to turn the heater on and off, the water temperature would end up incredibly close to the thermostat setting, and the heater would constantly be switching on and off in response to every tiny temperature fluctuation. This would be ridiculous, so thermostats don't actually work that way. Instead, they wait until the water drops to maybe 4 degrees below the thermostat setting. When that happens they switch the heater on, and leave it on until the water reaches 4 degrees above the desired temperature, at which point they turn the heater off and let the water start cooling down again. The resulting temperature is less precise, but now the heater runs in long bursts at occasional intervals rather than irritating you by constantly flickering on and off.

That is hysteresis in action, and games need it every bit as badly as water heaters do.

Blog index   -   Back to my homepage