Hey all! Does someone know how to create a horizontal compass like this one: https://forums.unrealengine.com/attachment.php?attachmentid=61721&d=1445173999 I have all the GUI elements I need to create something similiar, but I don't know how to make the compass work... Any advice?
code is attached to the Image that is the compass in the UI. Code (csharp): 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)); } } the compass image is double just to make sure it doesn't "run out" and end before the imagemask does. Due to the virtue of angles "wrapping around" naturally you don't have to worry about clamping the position etc. oh, in the code "Vector3.forward" is "north" if you want to change that to be something different. Needs to be done in line 18 and 20
Thank you mister! Didn't expect a code to copy & paste, just guidelines.. But this is awesome! Thank you!
hmm, actually I missed something. When i made the compass image I made it 90 pixels between each tick, so 1deg = 1 pixel. If your image isn't at that resolution you'll need to factor in a ratio. I've updated the above code.
@LeftyRighty I have finally put it to the test... My compass consists of numbers instead of the traditional "N S W E".. My compass doesn't seem to loop seamlessly like yours do. What am I doing wrong? (I haven't altered your code...)
first thing that comes to mind is that 360 and 0 are the same thing, on your compass they are separated... can you post the entire image of your compass image (like i did underneath the code above, the entire image with all the numbers etc. )
I've posted the image with the numbers.. look at the post I did just before your last reply... "There's (slightly visible)......".... The image is there..
OK, I'm feeling dumb. @LeftyRighty, can you explain how this works? In the animated GIF and in the OP's original request, it looks like an image whose texture shifts according to which way you're facing. So I would expect to see code that mucks with the material's texture offset. But this code just updates transform.position. How does moving the image around result in the texture appearing to shift?!?
@JoeStrout in 3d/2d you'd probably need to do something like offset the material, but this is UI... canvas > image mask > compass image the Red area is the image mask (offsetted up, coloured and mask component removed), the compass is below it. As the object rotates the offset and mask perform something similar to a texture offset.
having tried to make the example for joe I've found you really should include some sort of reference to the gameobject's horizontal scale in the pixel ratio... haven't got time to update the code yet, but as an example, scale of 0.5 means 180 pixels from north to north when the image is actually 360 pixels north to north etc.
Yep, I see now — sticking it inside an ImageMask makes perfect sense. Very clever solution (and yep, I feel sheepish for not thinking of it right away!).
i'm trying to add this compass to my plane's hud . Because the plane is moving too , ui canvas including hud is going to somwhere else . I need help @LeftyRighty @Kovenant
Then you haven't set up your UI canvas correctly. Either set it up to use screen space, or if you're going to put it in world space, then put it inside your plane so it moves with the plane.
yes . there is a world space canvas that is attached to cockpit and there is another canvas which holds a panel with mask and compass image . that's my hierarchy . i have another question for you , what is the target in your code i don't understand exactly . Thanks for reply Note : gif with cube and compass above in your post , think it like cube is also moving . this is what i need , cube is moving , compass ui image is sliding (world space mode) and staying in screen
i think it's not the problem cause other hud objects are moving with the plane (but these ones are not moving like the compass , they are static ui images , texts and scroll bars ) . Thanks for reply .
target is the cube (or whatever you want to take the baring of), the code above goes on the Image for the compass UI.
Hi guys. how would I go about creating a world space compass that is 360* around the player. Would it be the same? Something like this: I've got the UI elements but I'm just an artist that is just not too good at coding but can manage.
@reocwolf er, not really. For something like that I think you're looking at a separate gameobject which has it's position updated to the be the same as the player (don't want to parent it as it'll mess with the rotation), so it's more akin to a follow camera than the ui compass. If you're looking specifically for the "cylinder" style you might even be better off with a cylinder billboard style mesh with the compass texture applied to it than canvasUI based, there are ways of "curving" the canvas but they get really complicated fast.
Hi LeftyRight, Very nice compass. Thanks for sharing but I really need your help. I put the compass bar image onto the canvas and put my player as the target. It works just fine but when the player faces north (0) or south (180) the compass image moves left and right when I look up and down using mouselook. I just want the compass to move when I moving the player to the left and right. I really don´t get the mathematics behind the script and would appreciate some help. Here is a webdemo to show you what I mean http://angu.ddns.net/CompBar/compbar.html
this line assumes it's a 2d plane Code (csharp): Vector3.Angle(target.transform.forward, Vector3.forward) if you want to get it to work with 3d rotations you'll need to "ignore" the y axis so something like Code (csharp): // replace with Vector3.Angle(new Vector3(target.transform.forward.x, 0f, target.transform.forward.z), Vector3.forward)
Thank you very much and for the fast reply!! It works perfectly now. Is this code for free? I mean. Can I use it in an asset at Asset Store?
sorry for the necropost but is that a whole image im confused on how to set this up I have just a pointer (upside down triangle) could someone go through the whole steps?
Hi, The compass seems not to be working correctly when using the canvas scaler. Try to disable it in the Inspector. @LeftyRighty. Is there any way to make it compatible with the canvas scaler? By the way. Thanks for the script.. I´m using it with my Compass Bar at Asset Store. https://assetstore.unity.com/packages/tools/gui/compbar-v1-0-76991
How can i get to do something like he did, would you be able to explain, i know this post was 2 years ago.
@LeftRighty Bit random but just jumping in to post that was really interesting to learn about, thanks.
Hi guys, wasn't sure if anyone was still wondering about this, but there is a solution that's a little bit easier. Code (csharp): using UnityEngine; using UnityEngine.UI; public class Compass : MonoBehaviour { public RawImage compass; public Transform player; void Update() { compass.uvRect = new Rect(player.localEulerAngles.y / 360f, 0, 1, 1); } } This is using a compass like this: Import it and set the wrap mode to "repeat". Assign it to a RawImage on your UI. (You'll probably want to make a new compass, I just made this one black because the white wouldn't show up on the background here.) Assign the RawImage to the public compass field in the inspector wherever you choose to put the above code, and assign the player's transform to the player field. I'm not sure if it's more or less efficient than the previously posted example, but I found it easier to understand, and it works the same.
Thank you! this worked brilliantly - thank you Zymu - life saver!!! Works a charm. I am using it with a mask as a child to the image. Thank you.
For anyone thats using this way because your compass isnt just one image and wants to have markers that loop around the south pole. The looping part took me 2 hours and I felt after taking from this post ill give something back Code (csharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Compass : MonoBehaviour { [SerializeField] float rotationAngleToPixel;//-8.67 [SerializeField] RectTransform transformToMove, iconTransform; [SerializeField] MarkerData[] markers; [SerializeField] GameObject makerPrefab; static MarkerData[] markersStatic; static GameObject makerPrefabStatic; static RectTransform iconTransformStatic; static float rotationAngleToPixelStatic; static List<MarkerObject> compassMarkers = new List<MarkerObject>(); static Transform cam; [System.Serializable] struct MarkerData { public Sprite sprite; public Color color; public Vector3 size; } struct MarkerObject { public RectTransform marker; public RectTransform markerS; public Transform follow; } public enum MarkerType { Main, Side, Enemy, Player, Death, Base}; void Start() { cam = Camera.main.transform; markersStatic = markers; rotationAngleToPixelStatic = rotationAngleToPixel; makerPrefabStatic = makerPrefab; iconTransformStatic = iconTransform; AddMarker(MarkerType.Main, testFollow); } void FixedUpdate() { Vector3 facing = new Vector3(cam.forward.x, 0, cam.forward.z); Vector3 perp = Vector3.Cross(Vector3.forward, facing); float dir = Vector3.Dot(perp, Vector3.up); transformToMove.anchoredPosition = new Vector3(Vector3.Angle(facing, Vector3.forward) * Mathf.Sign(dir) * rotationAngleToPixel, 0, 0); if (compassMarkers.Count > 0) MoveMarkers(); } void MoveMarkers() { for (int i = 0; i < compassMarkers.Count; i++) { MoveMarker(compassMarkers[i]); } } static void MoveMarker(MarkerObject marker) { Vector3 direction = (marker.follow.position - cam.position).normalized; Vector3 facing = new Vector3(direction.x, 0, direction.z); Vector3 perp = Vector3.Cross(Vector3.forward, facing); float dir = Vector3.Dot(perp, Vector3.up); float angle = Vector3.Angle(facing, Vector3.forward); marker.marker.anchoredPosition = new Vector3(angle * Mathf.Sign(dir) * -rotationAngleToPixelStatic, 0, 0);//normal marker.markerS.anchoredPosition = new Vector3(Map(angle, 0, 180, 360, 180) * Mathf.Sign(dir) * rotationAngleToPixelStatic, 0, 0);//looped } static float Map(float value, float min1, float max1, float min2, float max2) { float perc = (value - min1) / (max1 - min1); return perc * (max2 - min2) + min2; } [SerializeField] Transform testFollow; public static void AddMarker(MarkerType markerType, Transform follow) { MarkerData markerData = markersStatic[(int)markerType]; GameObject newMarker = Instantiate(makerPrefabStatic, iconTransformStatic); GameObject newMarkerS = Instantiate(makerPrefabStatic, iconTransformStatic); MarkerObject markerObject = new MarkerObject(); Image image = newMarker.GetComponent<Image>(); image.sprite = markerData.sprite; image.color = markerData.color; image = newMarkerS.GetComponent<Image>(); image.sprite = markerData.sprite; image.color = markerData.color; markerObject.marker = newMarker.GetComponent<RectTransform>(); markerObject.markerS = newMarkerS.GetComponent<RectTransform>(); markerObject.follow = follow; MoveMarker(markerObject); compassMarkers.Add(markerObject); } public static void RemoveMaker(Transform follow) { for (int i = 0; i < compassMarkers.Count; i++) { MarkerObject mark = compassMarkers[i]; if (mark.follow == follow) { Destroy(compassMarkers[i].marker.gameObject); compassMarkers.RemoveAt(i); break; } } } }