Jitter when setting GUIUtility.RotateAroundPivot() angle with angle from player

Howdy all. Been having a pretty good time with Unity so far, but I ran into an issue I just can’t seem to google my way out of so I figured I would post here.

I am trying to make a radar/minimap for a flight game that displays the location of enemies relative to the player. So in OnGUI in the radar script (which is attached to the player), I use GUIUtility.RotateAroundPivot() to rotate the radar using transform.eulerAngles.y. This basically works, but the radar jitters around slightly. I managed to replicate this issue in a simplified setting. I will post the code here.

Movement script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class testmovement : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        transform.Rotate(0f, 300f * Time.deltaTime, 0f);
    }
}

Radar script:

using UnityEngine;
using System.Collections;

public class testGUI : MonoBehaviour
{
    public Texture2D texture;
    float size = 150f;
    Vector2 offset;
    Rect rectangle;
    Vector2 pivotPoint;
    // Start is called before the first frame update
    void Start()
    {
        offset = new Vector2 (Screen.width - size, Screen.height - size);
        rectangle = new Rect(offset.x, offset.y, size, size);
        pivotPoint = offset + new Vector2(size / 2f, size / 2f);
    }

    // Update is called once per frame
    void OnGUI()
    {
        GUIUtility.RotateAroundPivot(-transform.eulerAngles.y, pivotPoint);
        GUI.DrawTexture(rectangle, texture);
    }
}

If one attaches these two scripts to the same object and then provides a texture to the radar script, that would be sufficient to replicate the issue. I assume it’s happening because Update() runs out-of-sync with OnGUI(), though I don’t completely get why that would cause jitter. LateUpdate() and FixedUpdate() don’t fare any better, naturally. If anyone has any insight, I would be terribly grateful. Maybe this just isn’t an appropriate use for GUIUtility.RotateAroundPivot()? Should I apply the textures to 2D planes and then rotate those in Update() instead to try to get things to sync up? I would prefer not to have to do that, as it seems kind of tricky, but maybe there’s no choice.

1 Like

You’ve probably long moved on, but today I had the same issue and thought I could share what I found out.
9659321--1375046--Jitter.gif
The right circle texture is a GUI image which I rotate with RotateAroundPivot and it jitters on the x and y position. The left texture is a regular quad texture which I rotate with a special shader, which is smooth.
To me, this indicates that there’s some issue in the way the GUI matrix performs these calculations.

I see no hint that this has anything to do with syncing between different update loops, because only the angle is calculated with a deltaTime, and as you can see both circles have a nice flowing rotation, it’s just that the right one moves up down and left right by what looks like half or one pixel.

var rect = new Rect(10, 70, size, size);
_material.SetFloat("_Rotation", _angle);
Graphics.DrawTexture(rect, _material.mainTexture, _material);

var rect2 = new Rect(40, 70, size, size);
Matrix4x4 matrix = GUI.matrix;
GUIUtility.RotateAroundPivot(_angle, rect2.center);
GUI.DrawTexture(rect2, _material.mainTexture);
GUI.matrix = matrix;

What resolution does your image have? Keep in mind that rotating around a point means it’s a single point you rotate around. If your image has a power of two size, the perfect center would be between two pixels. It’s possible that Unity snaps the actual pivot of the object to the closest grid position, who knows.

Anyways, RotateAroundPivot does nothing really special. The “Unclip” call just converts a nested rect / position back to screen space. The rest is just usual matrix stuff. It might be interesting to see what happens to normal object when you apply this matrix transform to them.

I tried with different resolution images (32, 33, 512), offsetting the desired centerPivot and also doing the matrix math myself, giving me a chance to first scale it up, then move, rotate, move back, scale down in an attempt to reduce any floating point errors.

However, in all cases, no matter the size, the circle jittered a tiny amount, meaning, if the image was small, it was very noticeable, if it was large, it was not so noticeable, but still like 1 pixel offset here and there.

Yea, it looks like Unity is sometimes “snapping to pixels”, but of course the API itself uses floats for position.

I’m trying to decipher where the positional jitter comes from like this:

// State 1
GUI.matrix = Matrix4x4.identity;

// State 2
GUI.matrix = Matrix4x4.TRS(-rect.center, Quaternion.identity, Vector3.one) * matrix;

// State 3 and 4, etc
GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0.0f, 0.0f, _angle), Vector3.one) * GUI.matrix;

When I log these values I get:

State 1:
1.00000    0.00000    0.00000    0.00000
0.00000    1.00000    0.00000    0.00000
0.00000    0.00000    1.00000    0.00000
0.00000    0.00000    0.00000    1.00000

State 2:
1.00000    0.00000    0.00000    -16.00000
0.00000    1.00000    0.00000    -16.00000
0.00000    0.00000    1.00000    0.00000
0.00000    0.00000    0.00000    1.00000

State 3:
-0.30493    0.95238    0.00000    -10.35917
-0.95238    -0.30493    0.00000    20.11685
0.00000    0.00000    1.00000    0.00000
0.00000    0.00000    0.00000    1.00000

State 4:
-0.18013    0.98364    0.00000    -12.85619
-0.98364    -0.18013    0.00000    18.62037
0.00000    0.00000    1.00000    0.00000
0.00000    0.00000    0.00000    1.00000

State 1 is identity, no transformation.
State 2 moves my 32 pixel graphic to its center (offset by half size so that the center lies on the origin of the coordinate system.
State 3 applies a slight rotation.
State 4 is the rotation matrix at the next Update call.

It’s hard to tell if the jitter is visible in the positional offsets, sadly, but I assume that’s where they occur.

I even tried doing the entire matrix calculations by myself with doubles and using Math.Sin/Cos to increase the precision, however the same jittery result in the end.

1 Like

I am still haunted by this problem :melting_face: does anyone find a workaround?