Skip to content

Clamp / Map Range Clamped / Normalize To Range

At a glance

Lives in: Kismet Math Library; Blueprint API / Math / Float - Returns: one real number; never None - Changes: nothing; these are pure calculations, so you must assign or pass the result yourself - Fails by: producing a valid-looking fallback or unintended endpoint when bounds are equal/reversed incorrectly; no failure pin or warning - Official docs: Clamp, Map Range Clamped, and Normalize To Range

The one-minute version

  • Clamp keeps a value between a minimum and maximum. It does not convert one range into another.
  • Map Range Clamped converts a value from input endpoints A/B to output endpoints A/B and limits values outside the input range to output endpoints.
  • Normalize To Range answers "how far from Range Min to Range Max is this value?" It returns 0 at the first endpoint and 1 at the second, but it does not clamp values outside the range.
  • Normalize To Range(150, 0, 100) returns 1.5, not 1.
  • Reversed endpoints can be intentional. Mapping 100 -> 0 into 0 -> 1 makes 75 become 0.25.
  • Equal input endpoints have zero width, so no meaningful ratio exists. Branch before the node and choose an explicit fallback.
  • For a health bar, guard MaxHealth <= 0, then use a clamped 0..1 result.
  • Keep range math as real numbers. Convert to Integer only at the edge with an explicit rounding rule.

Pick the node by the question

Question Node Example result
"Keep this health between 0 and 100." Clamp Clamp(130, 0, 100) = 100
"Convert speed 0..600 into volume 0..1, limited." Map Range Clamped 300 -> 0.5
"What fraction of 200..600 is 300?" Normalize To Range 0.25
"Blend A to B using an existing 0..1 alpha." Lerp consumes the ratio; does not derive it

All three nodes are pure. Calling Clamp(CurrentHealth, 0, MaxHealth) does not change CurrentHealth until you assign the returned value:

CurrentHealth = Clamp(CurrentHealth + Delta, 0, MaxHealth)

Clamp limits; it does not remap

Clamp(Value, Min, Max) has three outcomes:

Value below Min  -> Min
Value in range   -> Value
Value above Max  -> Max

Examples:

Call Result
Clamp(-20, 0, 100) 0
Clamp(40, 0, 100) 40
Clamp(130, 0, 100) 100

The result stays in the original units. Clamping health to 0..100 does not turn health into a 0..1 UI percentage.

Min should be less than or equal to Max. A reversed Clamp is not an instruction to invert the value and the node does not sort the bounds for you. If bounds can arrive in either order, establish them explicitly:

Low  = Min(A, B)
High = Max(A, B)
SafeValue = Clamp(Value, Low, High)

Do not use that sorting pattern for a deliberate reversed mapping. Mapping endpoint order carries direction; Clamp minimum/maximum order does not.

Map Range Clamped converts and limits

Map Range Clamped takes five values:

Value
In Range A
In Range B
Out Range A
Out Range B

It finds Value's fraction between the two input endpoints, limits that fraction to 0..1, and uses it between the output endpoints:

pseudocode - not engine source

t = (Value - InA) / (InB - InA)
t = Clamp(t, 0, 1)
Result = OutA + t * (OutB - OutA)

For Value = 75, input 0 -> 100, output 0 -> 1, the result is 0.75. Outside values stop at the corresponding output endpoint:

Value Input Output Result
-20 0 -> 100 0 -> 1 0
25 0 -> 100 0 -> 1 0.25
150 0 -> 100 0 -> 1 1

The output range can use any units. For example, speed 0..600 cm/s can drive sound volume 0..1, camera shake strength 0..2, or a material parameter 0.2..0.8.

Normalize To Range produces an unclamped ratio

Normalize To Range(Value, Range Min, Range Max) calculates:

(Value - RangeMin) / (RangeMax - RangeMin)

For an ordinary increasing range:

Call Result
Normalize To Range(10, 10, 50) 0
Normalize To Range(20, 10, 50) 0.25
Normalize To Range(50, 10, 50) 1
Normalize To Range(70, 10, 50) 1.5
Normalize To Range(0, 10, 50) -0.25

"Normalize" here means express a scalar relative to a range. It is not the same operation as normalizing a Vector to unit length.

The result can be useful outside 0..1 when overshoot matters. If the consumer requires a bounded ratio, clamp it:

Ratio = Clamp(Normalize To Range(Value, A, B), 0, 1)

Or use Map Range Clamped with output 0 -> 1.

Reversed ranges

A reversed input range is valid when it expresses direction:

Normalize To Range(75, 100, 0) = 0.25

Map Range Clamped
    Value       = 75
    In Range A  = 100
    In Range B  = 0
    Out Range A = 0
    Out Range B = 1
-> 0.25

Endpoint A still maps to output A, and endpoint B maps to output B. The labels Range Min and Range Max on Normalize To Range are conventional names; the formula's direction comes from their order.

A reversed output range is also useful:

Map temperature 0 -> 100
to cooling strength 1 -> 0

Do not sort endpoints automatically unless increasing order is truly the data contract. Sorting a deliberate 100 -> 0 input changes its meaning.

Equal endpoints and division by zero

When both input endpoints are equal, the denominator is zero:

RangeA = 50
RangeB = 50

There is no answer to "what fraction of the way from 50 to 50 is Value?" The Blueprint nodes do not provide a failure pin or diagnostic for this invalid mapping contract.

Current engine source gives Normalize To Range a special exact-equality fallback: it returns Range Min when Range Min == Range Max. That prevents a literal divide-by-zero result, but the fallback is not a useful normalized ratio. With 50 -> 50, it returns 50, not 0 or 1.

Map Range Clamped delegates to the core FMath mapped-range helpers. Do not make gameplay depend on their degenerate endpoint handling. Guard zero-width input explicitly and choose the meaning your system needs:

if Nearly Equal(InA, InB, Tolerance):
    Result = OutA       // or OutB, 0, disabled, hidden - choose deliberately
else:
    Result = Map Range Clamped(Value, InA, InB, OutA, OutB)

Use Nearly Equal when endpoints are computed values. Exact equality is fine for an authored invariant such as MaxHealth == 0, but the branch should still make the fallback clear.

Health and progress bars

There are two separate operations:

  1. keep gameplay health legal;
  2. convert legal health to a UI ratio.
CurrentHealth = Clamp(CurrentHealth + Delta, 0, MaxHealth)

if MaxHealth <= 0:
    HealthPercent = 0
else:
    HealthPercent = Map Range Clamped(
        CurrentHealth,
        0,
        MaxHealth,
        0,
        1)

The second step could also use:

HealthPercent = Clamp(CurrentHealth / MaxHealth, 0, 1)

after the same MaxHealth guard. A Progress Bar expects a real value from 0..1, not 0..100; see Buttons and Progress Bars.

Do not hide invalid MaxHealth behind range math. Decide whether zero maximum means empty, disabled, indeterminate, or a setup error.

Dead zones are not just Clamp

Clamping axis input to -1..1 limits extreme values, but it does not remove small stick drift around zero. A dead zone applies a threshold and often rescales the remaining interval so motion starts smoothly after that threshold.

A manual one-sided shape is:

if Absolute(Axis) <= DeadZone:
    Output = 0
else:
    Magnitude = Map Range Clamped(
        Absolute(Axis),
        DeadZone,
        1,
        0,
        1)
    Output = Magnitude * Sign(Axis)

For Enhanced Input, prefer its Dead Zone modifier so the rule lives with the Input Action mapping. Tier 36 covers that node and radial versus axial input.

Keep integers out of the middle

Range conversion usually produces fractions. Integer arithmetic or an early Integer conversion can discard them:

Bad shape:
    convert 25 / 100 to Integer too early
    -> loses the 0.25 ratio

Good shape:
    keep Value, ranges, ratio, and mapped output as real numbers
    -> convert only the final display/count value

When an Integer is required, choose Round, Floor, Ceil, or Truncate explicitly. Those rules differ for fractional and negative values. An implicit conversion hides a design decision.

Relationship to Lerp

Lerp consumes an alpha; it does not discover that alpha from an input range. The operations fit together like this:

Alpha = Normalize To Range(Value, InA, InB)
Result = Lerp(OutA, OutB, Alpha)

With a valid non-equal input range, Map Range Clamped is approximately the bounded form:

Alpha = Clamp(Normalize To Range(Value, InA, InB), 0, 1)
Result = Lerp(OutA, OutB, Alpha)

Use Lerp, FInterp To, and moving things smoothly when the real question is interpolation over time. Range mapping is an instant conversion; it does not animate or smooth by itself.

The C++ twins

For the curious, an original guarded example is:

double UStatusReadout::GetHealthPercent(
    double CurrentHealth,
    double MaxHealth) const
{
    if (MaxHealth <= 0.0)
    {
        return 0.0;
    }

    return FMath::GetMappedRangeValueClamped(
        FVector2D(0.0, MaxHealth),
        FVector2D(0.0, 1.0),
        CurrentHealth);
}

FMath::Clamp, FMath::GetRangePct, FMath::GetRangeValue, and FMath::GetMappedRangeValueClamped are the core helpers behind the same families of operations.

Lookalikes - which one do I want?

Node Returns Use when Watch out
Clamp Original value limited to Min/Max One value must stay within legal bounds. Does not remap; Min must not exceed Max.
Map Range Clamped Value converted and bounded to output endpoints Input and output use different scales and overshoot should stop. Equal input endpoints have no meaningful ratio.
Map Range Unclamped Value converted without limiting alpha Extrapolation outside output endpoints is intentional. Overshoot can become large.
Normalize To Range Unclamped ratio relative to two endpoints You need an alpha/fraction and may care about overshoot. Result can be below 0 or above 1.
Lerp Value between output A/B for an alpha You already have the alpha. Does not derive or advance alpha.
Min / Max One selected endpoint/value You need to order or choose values. Does not clamp by itself.

Going deeper