[SOLVED] C# - Center UI Element on Another UI Element

I made a little minimap for my game, which works great! Some tweaking left to do, but it essentially does what I was looking for. I even made it freeze position rather than rotate with the player. But that created a new problem - it doesn’t show which way the player is facing. So I made a little arrow and got it rotating along with the character and that part’s great! Only problem is, I’m not sure how to render the arrow onto the center of the minimap (which is a RawImage UI element). Right now, it’s displaying in the center of the screen!

I thought about just manually adjusting the coordinate position of the arrow, but I think that would cause problems if the game was run at any other resolution. So what I really need is to somehow fetch the center of the minimap RawImage and render my arrow there. But because of anchor points, the minimap RawImage is at 0,0,0…so I’m not really sure how.

Can anyone point me in the right direction to get the actual center of a UI element on the screen, not relative to parent elements and such?

Your minimap is a RawImage… that’s a UI element. Your arrow is also a UI element (probably RawImage or Image). So, just stick the arrow inside the map (i.e., make the map the parent of the arrow), and set its anchors up so it’s centered on it. Easy peasy, no?

1 Like

So I’m tracking with you, but the only issue is that the arrow I don’t think is actually an object, the way I set it up…exactly. I’m using GUI.DrawTexture. Can I do that and make the arrow a child of the minimap?

On second thought, maybe I’m doing this wrong altogether. Maybe I should create the arrow as an object in the editor, and then just change its rotation instead of rendering it?

:hushed:

So, in all honesty, I really recommend just redoing it with a UI.Image object.

I used to use the various GUI.Draw routines a lot too. In fact in High Frontier, we still have a lot of code using that, because it works and it would be very expensive at this point to redo it.

But all our new stuff uses the new (well, not that new — since 4.6) UI system, which really is a lot better in almost every way. This is a great example of where it’s going to make your life much, much easier. Just throw an arrow into your canvas hierarchy, get a reference to it in your script, and set its rotation. Boom, job done!

1 Like

Okay, thanks! I’ll do that then.

Sorry to be a bother, but I got stuck on the last bit here. I got the arrow as a child of the minimap now and it shows up in the right place. But I’m a little lost on how to actually do the rotating part. Here’s what I tried so far:

    void Update () {
        transform.position = target.transform.position + offset;
        if (minimap.enabled) {
            var meAngle = me.transform.eulerAngles.y + 90;
            meArrow.transform.Rotate (new Vector3 (0, 0, meAngle));
        }
    }

This makes it rotate…but it makes it rotate crazy fast, I think because it’s not setting the rotation, but rather actually rotating it by my rotation every frame. Oops. So I tried this instead:

    void Update () {
        transform.position = target.transform.position + offset;
        if (minimap.enabled) {
            var meAngle = me.transform.eulerAngles.y + 90;
            meArrow.transform.rotation.z = meAngle;
        }
    }

Which SHOULD do I what I want, I thought…except it gives an error that you can’t set the value of rotations that way. Any advice on where to go from here please?

Ah! I found a solution here! My new code that works is this:

    void Update () {
        transform.position = target.transform.position + offset;
        if (minimap.enabled) {
            var newRot = me.transform.eulerAngles;
            newRot.x = 0;
            newRot.z = 360 - (newRot.y + 90);
            newRot.y = 0;
            meArrow.transform.rotation = Quaternion.Euler (newRot);
        }
    }

Yes. The key insight here is that the “Rotate” method does not set the rotation… it actually rotates the object, relative to where it was before. That’s not what you want here; you want to just set the rotation to whatever angle you’ve calculated.

Your previous attempt was almost right, except that yeah, you can’t assign to just the .z component of something like a Vector3d returned from some accessor like .rotation. You have to assign the whole thing at once.