I'm trying to make a horizontal compass. The compass is made up of UI components: panel (with mask), with a child UI Image (the compass image).

Here is the compass from Fallout:

enter image description here

As you turn around, so does the compass. I want to move the child based on the rotation of the player. I want to move the child by using transform.position so that any child components will also move (i.e way points).

I found some code on the Unity forum. I got it to kinda work, however when it wraps around, it doesn't wrap to the correct position. See GIF. When I approach the South rotation, it wraps but not to the correct position. The compass image and code can be found here.

I've made sure that the image is native size, and also tried various values in the "numberOfPixelsNorthToNorth" property.

using UnityEngine;
using UnityEngine.UI;

using System.Collections;

public class CompassHandler : MonoBehaviour {
    public float numberOfPixelsNorthToNorth;
    public GameObject target;
    Vector3 startPosition;
    float rationAngleToPixel;

    void Start(){
        startPosition = transform.position;
        rationAngleToPixel = numberOfPixelsNorthToNorth / 360f;

    void Update (){
        Vector3 perp = Vector3.Cross(Vector3.forward, target.transform.forward);
        float dir = Vector3.Dot(perp, Vector3.up);
        transform.position = startPosition + (new Vector3(Vector3.Angle(target.transform.forward, Vector3.forward) * Mathf.Sign(dir) * rationAngleToPixel, 0, 0));

I don't really understand how the code works in the Update method, as my Math knowledge is pretty poor. I looked up the methods used, but still was clueless. So I can't work out how to solve it.

I would like to understand what is going on in the Update, but if that is too much, can anyone help fix the issue that is seen in the GIF?

Edit: Here is a GIF using the accepted solution.


1 Answer 1


I'd junk this code and do it in a simpler way. ;)

// Get an arrow pointing to the marked object in the player's local frame of reference
Vector3 offset = playerTransform.InverseTransformPoint(markedObjectTransform.position);

// Get an angle that's 0 when the object is directly ahead, 
// and ranges -pi...pi for objects to the left/right.
float angle = Mathf.Atan2(offset.x, offset.z);

// Let's put these markers in a container parent object 
// whose center is where you want the middle of the compass. 
// Then each marker only needs to worry about its x position relative to this.
compassMarker.localPosition = Vector3.right * compassWidth * angle/(2f * Mathf.PI);
  • \$\begingroup\$ Thanks again @DMGregory i.imgur.com/7u9YPQC.gif I used your code to set the poles as well. Just made sure they were far enough out otherwise the way you are heading can make them appear to come together. Not sure if this is ideal, but if they are far enough it seems ok. Also works fantastic for waypoints. \$\endgroup\$ Oct 9, 2017 at 16:54
  • 1
    \$\begingroup\$ For poles at an infinite distance you can replace offset = playerTransform.InverseTransformDirection(Vector3.forward) for north, using Vector3.right .back .left for east, south, and west respectively. \$\endgroup\$
    – DMGregory
    Oct 9, 2017 at 16:58
  • \$\begingroup\$ Oh yes, I did try that, but forgot I needed the position of the player as well. That's working perfectly now. Thanks \$\endgroup\$ Oct 9, 2017 at 17:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .