How to properly offset a Handles.DrawSolidArc by an angle in different axis?

I have spent far too long on this and I do not understand what is causing this. I have a direction vector that points forward on the Z axis. I would like for my entity to see plus or minus X degrees left or right in front of it.

This works fine the way I am doing it, but when trying to get this to work with Handles.DrawSolidArc instead of being straight, it curves in all sorts of ways, and I can’t figure out why.

My code is as follows:

                    var forwardMinY = Quaternion.AngleAxis(-angleDegreesY, perceiver.Entity.CurrentTransform.right) * perceiver.Entity.CurrentTransform.forward;
                    Quaternion spreadAngle = Quaternion.AngleAxis(perceiver.senseOptics.viewOrientation, Vector3.up);

                    var angleNew = spreadAngle*forwardMinY;

                    Handles.DrawSolidArc(perceiver.Entity.CurrentTransform.position, perceiver.Entity.CurrentTransform.right, angleNew , angleDegreesY * 2,
                        perceiver.senseOptics.rangeMin);

What happpens (Sorry Image Upload is not working for me currently):

What I want (offset in the same direction as up axis handle):

Thank you for your time and help!

The exact details will depend on exactly how your code handles your input parameters, but this is how I go about this sort of situation.

// these values would actually come from your perceiver data
// total arc of visiblity in degrees
float visibilityArc= 30f;
// how far the entity can see
float range = 2f;
Handles.DrawSolidArc (
	perceiver.Entity.CurrentTransform.position,
	Vector3.up,
	// start from rotation that is half of entire arc of vision
	Quaternion.AngleAxis(-0.5f * visibilityArc, Vector3.up) * (
		// subtract out vertical component of forward-facing vector if altitude is not used in visibility test
		transform.forward - Vector3.Dot(transform.forward, Vector3.up) * Vector3.up
	),
	visibilityArc,
	range
);

Note, too, that if you’re interested in making an interactive version of this handle, I have a package on the Asset Store that might have some APIs that would interest you.

Well, if you only have a forward axis there are basically two ways you could draw the view area:

  • Either the arc is always flat on the ground
  • or the arc is elevated according to the forward axis you specify

For the first case the normal vector is simply Vector3.up since it’s the world up vector so the “arc plane” is world aligned (the world x-z-plane). Unity now has a dedicated method for projecting a vector onto a plane : Vector3.ProjectOnPlane. With this is’t really easy to get your forward vector down onto the x-z-plane

Vector3 newForward = Vector3.ProjectOnPlane(givenForward, Vector3.up).normalized;

For the second case you need a tilted normal vector so that your desired forward axis is inside the tilted plane. If your forward axis comes from a Transform the easiest way to get the according up vector is using transform.up. If the forward axis is elsewhere defined you can simply calculate it using the cross product. First we need the current “right” vector:

Vector3 right = Vector3.Cross(Vector3.up, givenForward);

With that right vector we can calculate the tilted up vector:

Vector3 up = Vector3.Cross(givenForward, right);

This will give you the tilted up vector that is perpendicular to the given forward vector.

Of course the starting vector “from” would simply be the forward vector rotated by half the visibility angle around the up vector:

Vector3 fromVec = Quaternion.AngleAxis(-0.5f * visibilityArc, up) * givenForward;

Now you should have everything you need:

  • The position
  • the normal vector (our “up” vector)
  • the starting vector (our “fromVec”)
  • the visibility angle (visibilityArc)
  • and your radius

Like this:

Handles.DrawSolidArc( yourPosition, up, fromVec, visibilityArc, radius);