Clamping angles?

I’m a little confused on how clamp angle works.

The following returns 180 (but 8 is clearly in the interval):

The following returns 0 (but 8 is clearly in the interval):

This returns 179:


It seems that to make this work you have to reduce the available angle interval, so this works (prints 8):

Is this expected? I realize that the whole purpose of the method is to clamp an angle, but you might want to use this method and then allow the min&max defined to full spectrum based on some settings.



The node description is not very clear. The min and max are both supposed to be positive.

Thanks for your input. The -180 to +179.9 interval works, though… So this has to limit the angles, correct?

Well, you can see it doesn’t work like that :wink:

Notice the mouseover

I don’t think anybody could consider a negative angle as ‘sweeping clockwise’.

Also, why not just use ‘clamp float’, then you defintely can use any pair of numbers you want :smiley:

1 Like

Ok, thanks for your time.

1 Like

Not really:

This means that the minimum value should be less than the maximum (and this should be obvious) but you can definitely use negative values in the clamp angle node


This doesn’t work because +180 and 180 are the same angle value, the same happens with 0 and 360. So 8 is clearly NOT in the interval because the interval is 0 degrees wide😅

This example really doesn’t make sense to me, this interval is 3 degrees wide (179, 180 and 181), so it will be clamped to the closest value to 8, 179 indeed. (try clamping a value bigger than 181 and less than 360 and it will return 181 because it’s the closest value)

This is the only correct example, because you are using an interval of 259.9 degrees, it would have worked even with 0 and 359.9 (but it doesn’t really make sense to clamp an angle to a 360 degrees interval, because 560 for example is already converted to 200 automatically if you input it in a rotator value).


I was of course going off route to see why the original -180to +180 example didn’t work, I didn’t expect the other examples to work, rather, see what would happen.

It is a pity that this method doesn’t allow to be used with the full angle spectre, because then it cannot be used with a setting that would decide not to clamp it.

Thank you for your time @Ares9323! Super explanation.


1 Like

If you want to do that just use the regular clamp node!


I don’t understand why this clamp angle node exists actually…

I used it today in a project, it’s useful because the regular clamp node doesn’t behave in the same mode:

If i input 690 in a -20 / +20 ClampAngle it would return -20 (because 690 is -30), the regular clamp with the same values would return +20


It’s ironic that Epic obviously though about this node, but totally missed the relative rotation problem ( ie, 180 suddenly becomes -180 ).

If by regular you mean the float clamp, then that method does not consider the fact that the data are angles. That’s the whole purpose.

For instance, if you clamp 359 between -20 and +20 you will get +20 with float clamping, but 359 (or maybe -1) with angle clamping.

Yes and no :slight_smile: take a look at the UE documentation for this node and see Epic arguing with themselves on this one :stuck_out_tongue:

That node uses FMath::ClampAngle

float FMath::ClampAngle(float AngleDegrees, float MinAngleDegrees, float MaxAngleDegrees)
	float const MaxDelta = FRotator::ClampAxis(MaxAngleDegrees - MinAngleDegrees) * 0.5f;			// 0..180
	float const RangeCenter = FRotator::ClampAxis(MinAngleDegrees + MaxDelta);						// 0..360
	float const DeltaFromCenter = FRotator::NormalizeAxis(AngleDegrees - RangeCenter);				// -180..180

	// maybe clamp to nearest edge
	if (DeltaFromCenter > MaxDelta)
		return FRotator::NormalizeAxis(RangeCenter + MaxDelta);
	else if (DeltaFromCenter < -MaxDelta)
		return FRotator::NormalizeAxis(RangeCenter - MaxDelta);

	// already in range, just return it
	return FRotator::NormalizeAxis(AngleDegrees);

Let’s try to input 8, -180 and + 180

FORCEINLINE float FRotator::ClampAxis( float Angle )
	// returns Angle in the range (-360,360)
	Angle = FMath::Fmod(Angle, 360.f);

	if (Angle < 0.f)
		// shift to [0,360) range
		Angle += 360.f;

	return Angle;
  • FRotator::ClampAxis has a input: 180-(-180), so 360
  • 360%360 = 0
  • The returned angle is 0, so, MaxDelta = 0

  • FRotator::ClampAxis has a input -180+0, so 180
  • 180%360 = 180
  • The returned angle is 0, so, RangeCenter = 0

  • AngleDegrees - RangeCenter = 8 - 0 = 8
FORCEINLINE float FRotator::NormalizeAxis( float Angle )
	// returns Angle in the range [0,360)
	Angle = ClampAxis(Angle);

	if (Angle > 180.f)
		// shift to (-180,180]
		Angle -= 360.f;

	return Angle;
  • NormalizeAxis uses ClampAxis Again, so:
  • 8%360 = 8
  • Angle is < 180, so 8 is returned and DeltaFromCenter is 8

  • DeltaFromCenter (8) is greater than MaxDelta (0),
  • We call NormalizeAxis on RangeCenter (0) + MaxDelta (0)
  • 0%360 = 0
  • ClampAngle returned value is 0

As I said in the first comment

The picture you linked is talking about the return type, not the input parameters

This means that the returned clamped value will always be greater than -180 and less than 180


@Ares9323 Thanks a lot for the detailed input and for the time you took to do all of that!!!
I’m not arguing with you, indeed my picture shows the Output range of that BP node.

The only thing I meant by my comment was that the initial statement “-180 is the same angle as +180” is “true” from a trigonometrical point of view, but also “false” in the sense that as a range, one would expect [-180, +180] to be 360 degrees wide, not 0, despite the angles themselves coinciding on the circle. And Epic also used that vocabulary to indicate such an expectation. Hope I was clear enough, since such topics are quite hard to detail in words :slight_smile:

1 Like