Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UILineRenderer mesh not updating in Editor scene view #127

Closed
SimonDarksideJ opened this issue Apr 25, 2022 · 10 comments
Closed

UILineRenderer mesh not updating in Editor scene view #127

SimonDarksideJ opened this issue Apr 25, 2022 · 10 comments

Comments

@SimonDarksideJ
Copy link
Contributor

Issue created by Jason Horsburgh as Bitbucket Issue #​127 on 2017.03.16 17:53.
I've created a custom editor to help me lay out bezier curves on a UILineRenderer.
It creates position handles to move the 4 points on the renderer, and calls SetVerticesDirty() if the Editor reports a change. I can see how this is supposed to work but the line is not updating its position in the scene view. The mesh will update if you change focus of the window or even click the 'points' foldout in the inspector but will not update while moving the handle.
Do you have any pointers that would help me look into the issue?

I've added the basic editor code here since it's not too long:

[CustomEditor(typeof(UILineRenderer))]
public class BezierLineRendererEditor : Editor {
    void OnSceneGUI() {
        var curveRenderer = target as UILineRenderer;
        var oldMatrix = Handles.matrix;

        var transform = curveRenderer.GetComponent<Transform>();
        Handles.matrix = transform.localToWorldMatrix;

        var points = curveRenderer.Points;

        for (int i = 0; i < points.Length - 1; i += 2) {
            Handles.DrawLine(points[i], points[i + 1]);
        }

        for (int i = 0; i < points.Length; ++i) {
            using (var check = new EditorGUI.ChangeCheckScope()) {
                var p = Handles.PositionHandle(points[i], Quaternion.identity);
                
                if (check.changed) {
                    Undo.RecordObject(curveRenderer, "Changed Curve Position");
                    var newPoints = new Vector2[points.Length];
                    Array.Copy(points, newPoints, newPoints.Length);
                    newPoints[i] = p;
                    curveRenderer.Points = newPoints;
                    // I've tried adding all these after but doesn't help refresh the mesh draw
                    // curveRenderer.SetVerticesDirty();
                    // curveRenderer.Rebuild(UnityEngine.UI.CanvasUpdate.PreRender);
                    // SceneView.RepaintAll();
                }
            }
        }

        Handles.matrix = oldMatrix;
    }
}

Just in case you try and use this for testing: the handles also don't appear to match up to where the line actually generates its points but that's not important for this issue.

@SimonDarksideJ
Copy link
Contributor Author

On 2017.03.16 18:54, @SimonDarksideJ commented:
That looks fantastic.
Always meant to create an error script for the primitive controls but didn't have the time.
Will look into this and get it working/added to the project asap

@SimonDarksideJ
Copy link
Contributor Author

On 2017.03.16 18:59, @SimonDarksideJ commented:
P.s.
The alignment will be due to the ui systems use of screenspace and the editor using worldspace

@SimonDarksideJ
Copy link
Contributor Author

On 2017.03.16 21:43, @SimonDarksideJ commented:
Well I got it working but BOY is it hacky. I had to revert to V3.5 hacks in the end.. But it's working :D

for reference (while I look at generalising the script a bit more for all primitives and sorting the screen position issue), here's the script I have (try not to laugh at the fix)

[CustomEditor(typeof(UILineRenderer))]
public class BezierLineRendererEditor : Editor
{
    void OnSceneGUI()
    {
        UILineRenderer curveRenderer = target as UILineRenderer;

        if (curveRenderer.Points.Length < 2)
        {
            return;
        }

        var oldMatrix = Handles.matrix;
        var transform = curveRenderer.GetComponent<RectTransform>();
        Handles.matrix = transform.localToWorldMatrix;

        var points = curveRenderer.Points;

        for (int i = 0; i < points.Length - 1; i += 2)
        {
            Handles.DrawLine(points[i], points[i + 1]);
        }

        for (int i = 0; i < points.Length; ++i)
        {
            using (var check = new EditorGUI.ChangeCheckScope())
            {
                var p = Handles.PositionHandle(points[i], Quaternion.identity);

                if (check.changed)
                {
                    Undo.RecordObject(curveRenderer, "Changed Curve Position");
                    curveRenderer.Points[i] = p;
                    curveRenderer.transform.gameObject.SetActive(false);
                    curveRenderer.transform.gameObject.SetActive(true);
                }
            }
        }

        Handles.matrix = oldMatrix;
    }
}

@SimonDarksideJ
Copy link
Contributor Author

On 2017.03.16 21:51, Jason Horsburgh commented:
Ooft I saw someone mention toggling the GameObject active flag while googling around for something, nothing like a good hack! Happy it's not something overly simple I've missed though.

Just so you know, this editor is for a fairly specific use-case for a game I'm working on so you don't have to worry too much about trying to mould this into something more general, however, I'm always glad when something gives me inspiration so hope it's helpful.

@SimonDarksideJ
Copy link
Contributor Author

On 2017.04.13 13:03, @SimonDarksideJ commented:
Resolved in source

@SimonDarksideJ
Copy link
Contributor Author

On 2017.04.13 13:03 @SimonDarksideJ modified issue:
status changed newresolved

@SimonDarksideJ
Copy link
Contributor Author

On 2020.07.12 06:06, Andrew Peysakhovich commented:
I know it’s pretty old, but I’ve just faced the same issue while developing my own editor.

After some digging in the UI system internals, I’ve figured out that canvas renderer is not getting refreshed while it has valid mesh available, so instead of toggling the object off and on I just force called internal refresh method of the canvas renderer via reflection, and it’s worked!

            typeof(CanvasRenderer)
              .GetMethod("RequestRefresh", BindingFlags.Static | BindingFlags.NonPublic)
              .Invoke(null, null);

@SimonDarksideJ
Copy link
Contributor Author

On 2020.07.12 06:45, Andrew Peysakhovich commented:
Oh wow, I’ve just found another method that I finally can consider as almost not hacky.

Just call EditorUtility.SetDirty(curveRenderer) and this does the trick!

Why almost not hacky then? From Unity docs:

Prior to Unity 5.3, this was the primary method of marking objects as dirty. From 5.3 onwards, with the introduction of Multi-Scene Editing, this function should no-longer be used for modifying objects in scenes. Instead, you should use Undo.RecordObject prior to making changes to the object. This will mark the object's Scene as dirty and provide an undo entry in the editor.

So, SetDirty was replaced with RecordObject. However, the latter doesn’t trigger canvas renderer refresh for some reason, while the former does.

@SimonDarksideJ
Copy link
Contributor Author

On 2020.07.13 12:05, @SimonDarksideJ commented:
Are you saying this issue is still present @{557058:1f0b7fb9-9baf-42b0-86c3-127d189f4f5e} ? (if so does it need reopening?)
Or is this a new effect in the later Unity builds and should be a new issue?

@SimonDarksideJ
Copy link
Contributor Author

On 2020.07.13 19:16, Andrew Peysakhovich commented:
@{557058:da9b1be2-6172-44a5-a085-cae5d30eda9e}

@simon Jackson

Sorry if my wording was confusing - my comment is not directly related to the Extensions project. I just faced the same issue while developing my own editor for my own line renderer, and wanted to share the possible solution I’ve found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant