Make UI compass point to 3D object

I want a compass needle to point to a target object offscreen. The compass is an upwards-facing arrow sprite in a Canvas, so will rotate on the Z-axis. The rest of the game is 3D.

From other answers here on UA I’ve made this calculation:

var dir : Vector3;
var angle : float;
var arrow: GameObject;

dir = target.transform.position - transform.position;
angle = Mathf.Atan2(dir.x, dir.z);
arrow.transform.eulerAngles = Vector3(0, 0, angle);

…but this doesn’t give the correct angle. Could anyone give me a pointer please?

Put this script to arrow object and drop your target to “toFollow” public variable or ref it some other way.

using UnityEngine;
using System.Collections;

public class Follow : MonoBehaviour {

public GameObject toFollow;

void Update () {

Vector3 v = toFollow.transform.position;

//z is meaningless

v.z = 0;

//arrow always looks forward so it will show correctly to viewer, and world-up changes the rotation

transform.LookAt(transform.position+transform.forward,v-transform.position);

}
}

Mathf.Atan2(dir.x, dir.z) returns the rotation in Radians, so you have to multiply it by the conveniently named ‘Mathf.Rad2Deg’

angle = Mathf.Atan2(dir.x, dir.z)*Mathf.Rad2Deg;

This worked for me:

var targetPosLocal = Camera.transform.InverseTransformPoint(targetObjectPosition);
var targetAngle = -Mathf.Atan2(targetPosLocal.x, targetPosLocal.y) * Mathf.Rad2Deg - 90;
ArrowUIObject.eulerAngles = new Vector3(0, 0, targetAngle);

Yet another solution:

Vector3 dir = transform.InverseTransformDirection(target.position - transform.position);
float angle = Mathf.Atan2(-dir.x, dir.z) * Mathf.Rad2Deg;
uiTrans.eulerAngles = new Vector3(0, 0, angle);