Prefab Mode changes only one of several prefab copies until the others are manually selected

I am creating bezier curves with the following code and made a prefab of my Bezier curve game object.
I added a few copies of the prefab to my scene. When I open the original prefab in prefab mode (I am using Unity 2019.1.0a8) and adjust any of the points it only affects one of the prefabs, the changes are not recognised in the scene view till I select the other objects manually.

I added ‘SceneView.RepaintAll();’ after calling ‘path.MovePoint(i, newPos);’ in the ‘Draw()’ method but it still doesn’t update the others.
Could it be that I haven’t serialised by variables correctly? How can I solve this?

Path

[System.Serializable]
public class Path {

[SerializeField, HideInInspector]
List<Vector2> points;

[SerializeField, HideInInspector]
public bool isContinuous;

public Path(Vector2 centre)
{
    points = new List<Vector2>
    {
        centre+Vector2.left,
        centre+(Vector2.left+Vector2.up)*.5f,
        centre + (Vector2.right+Vector2.down)*.5f,
        centre + Vector2.right
    };
}

public Vector2 this[int i]
{
    get
    {
        return points[i];
    }
}

public int NumPoints
{
    get
    {
        return points.Count;
    }
}

public int NumSegments
{
    get
    {
        return (points.Count - 4) / 3 + 1;
    }
}

public void AddSegment(Vector2 anchorPos)
{
    points.Add(points[points.Count - 1] * 2 - points[points.Count - 2]);
    points.Add((points[points.Count - 1] + anchorPos) * .5f);
    points.Add(anchorPos);
}

public Vector2[] GetPointsInSegment(int i)
{
    return new Vector2[] { points[i * 3], points[i * 3 + 1], points[i * 3 + 2], points[i * 3 + 3] };
}

public void MovePoint(int i, Vector2 pos)
{

    if (isContinuous)
    {

        Vector2 deltaMove = pos - points[i];
        points[i] = pos;

        if (i % 3 == 0)
        {
            if (i + 1 < points.Count)
            {
                points[i + 1] += deltaMove;
            }
            if (i - 1 >= 0)
            {
                points[i - 1] += deltaMove;
            }
        }
        else
        {
            bool nextPointIsAnchor = (i + 1) % 3 == 0;
            int correspondingControlIndex = (nextPointIsAnchor) ? i + 2 : i - 2;
            int anchorIndex = (nextPointIsAnchor) ? i + 1 : i - 1;

            if (correspondingControlIndex >= 0 && correspondingControlIndex < points.Count)
            {
                float dst = (points[anchorIndex] - points[correspondingControlIndex]).magnitude;
                Vector2 dir = (points[anchorIndex] - pos).normalized;
            points[correspondingControlIndex] = points[anchorIndex] + dir * dst;
                }
            }
        }
    }

    else {
         points[i] = pos;
    }
}

PathCreator

public class PathCreator : MonoBehaviour {

[HideInInspector]
public Path path;


public void CreatePath()
{
    path = new Path(transform.position);
}
}

PathEditor

[CustomEditor(typeof(PathCreator))]
public class PathEditor : Editor {

PathCreator creator;
Path path;

public override void OnInspectorGUI()
{
    base.OnInspectorGUI();
    EditorGUI.BeginChangeCheck();

    bool continuousControlPoints = GUILayout.Toggle(path.isContinuous, "Set Continuous Control Points");
    if (continuousControlPoints != path.isContinuous)
    {
        Undo.RecordObject(creator, "Toggle set continuous controls");
        path.isContinuous = continuousControlPoints;
    }

    if (EditorGUI.EndChangeCheck())
    {
        SceneView.RepaintAll();
    }
}

void OnSceneGUI()
{
    Input();
    Draw();
}

void Input()
{
    Event guiEvent = Event.current;
    Vector2 mousePos = HandleUtility.GUIPointToWorldRay(guiEvent.mousePosition).origin;

    if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.shift)
    {
        Undo.RecordObject(creator, "Add segment");
        path.AddSegment(mousePos);
    }
}

void Draw()
{

    for (int i = 0; i < path.NumSegments; i++)
    {
        Vector2[] points = path.GetPointsInSegment(i);
        Handles.color = Color.black;
        Handles.DrawLine(points[1], points[0]);
        Handles.DrawLine(points[2], points[3]);
        Handles.DrawBezier(points[0], points[3], points[1], points[2], Color.green, null, 2);
    }

    Handles.color = Color.red;
    for (int i = 0; i < path.NumPoints; i++)
    {
        Vector2 newPos = Handles.FreeMoveHandle(path[i], Quaternion.identity, .1f, Vector2.zero, Handles.CylinderHandleCap);
        if (path[i] != newPos)
        {
            Undo.RecordObject(creator, "Move point");
            path.MovePoint(i, newPos);
        }
    }
}

void OnEnable()
{
    creator = (PathCreator)target;
    if (creator.path == null)
    {
        creator.CreatePath();
    }
    path = creator.path;
}
}

I would really appreciate some help

I would suggest to read Saving prefab after ContextMenu change the same issue occurred there and was solved there

I think you simply need to recalculate the spline in Awake.

When you have multiple instances of a prefab in the scene and you then modify the prefab asset, all instances are merged with the asset and will get Awake called.

I added an Awake method that recalculates the curve, but it didn’t solve my issue.

void Awake()
    {
        DrawBezierCurve();
    }

After giving it some further thought, I realised that I forgot to mention that I am rotating all but one of the prefab instances in the scene before trying to adjust the prefab. So I deleted and them and recopied them without rotation and realised that all of the copies are updating. That basically means the rotation is causing the issue. How can I resolve this?

It is hard to say, not sure where things are failing. Could you please file a bug report and attach a small project with steps to reproduce.

Okay, I’ll file the bug report. I was just wondering, how long it could possibly take to get a reply or the issue addressed.

Also is there a prefabUtility function that can “manually” force instances to update (because when I select the instances in hierarchy window they update)? If there is, where should I place it in my code?

Instances are force updated in the scene when you save the prefab asset.
Without a project it is hard for me to actually see what is happening, it is hard to tell what is going wrong.
I am confident that the instances are updated, but your are missing something in your script that recalculates/redraws the spline.
You can verify this by changing some other property in the prefab. e.g try to disable a GameObject or component.

I just sent a bug report with a sample project reproducing my issue.

Still trying to get this solved, will appreciate some help

If you post the case number of the bug report you made, it will be easier for us to find it.

1 Like

The bug report Case Number is 1118349

I took a quick look and could see that your Prefab instances don’t update at all if the Inspector is set to Debug mode. So something in your custom editor is causing the splines to be updated upon selection.

I also tried having some Prefabs that are just LineRenderers without your script, and then all instances are synced with the Prefab immediately.

You’ll have to figure out your own scripts yourself. It’s not trivial for us to see exactly what those scripts are doing.

Thanks. I’ll keep at it.