#Undo
1 messages · Page 1 of 1 (latest)
Basically my problem is this: I'm dragging a wall around using handles, and generating blocks to fill out its length. They get created and destroyed during the drag. I want to be able to undo this operation afterwards, but I get a warning about dangling references if I both add and remove the same prefab instance during the same drag operation.
At the moment every object that is created gets recorded via Undo, and I'm using Undo.DestroyImmediate to clear them
I believe the issue is arising when the same object is created and destroyed in a single input operation.
But I'm not sure.
Can you show the code for that section?
using UnityEngine;
using UnityEditor;
using Sirenix.OdinInspector.Editor;
using System.Collections.Generic;
namespace EffortStar.Editor {
[CustomEditor(typeof(WallCollider)), CanEditMultipleObjects]
public class WallColliderEditor : OdinEditor {
public void OnSceneGUI() {
var wall = target as WallCollider;
if (Selection.Contains(wall.gameObject)) {
var start = (Vector3) wall.Start;
var end = (Vector3) wall.End;
var thickness = wall.Thickness;
using var check = new EditorGUI.ChangeCheckScope();
Handles.TransformHandle(ref start, Quaternion.identity, ref thickness);
Handles.TransformHandle(ref end, Quaternion.identity, ref thickness);
if (check.changed) {
RecordUndo(wall);
wall.Initialize(start, end, thickness, generateMesh: true);
}
}
}
static List<Object> _objects = new();
void RecordUndo(WallCollider wall) {
_objects.Clear();
wall.GetUndoObjects(_objects);
Undo.RecordObjects(_objects.ToArray(), "Modify wall");
}
}
}
Remove the unnecessary part
And then within Initialize
transform.DestroyAllChildrenImmediate();
// redacted...
for (var i = 0; i < blockCount; i++) {
var xOffset = (_blockPrefab.Size.x / 2 - _blockPrefab.Origin.x) * blockScale.x;
var position = new Vector3(
x: 0,
y: i * blockLength + xOffset,
z: 0
);
var block = GameObjectUtility.InstantiateChild(
_blockPrefab,
transform,
position
);
#if UNITY_EDITOR
if (!Application.isPlaying) {
UnityEditor.Undo.RegisterCreatedObjectUndo(block, "Created block");
}
#endif
block.transform.localRotation = blockRotation;
block.transform.localScale = blockScale;
if (cladding != null) {
block.SetLeftCladding(cladding.Sample());
block.SetRightCladding(cladding.Sample());
}
}
public static void DestroyAllChildrenImmediate(this Transform transform) {
for (var i = transform.childCount - 1; i >= 0; i--) {
#if UNITY_EDITOR
if (!Application.isPlaying) {
UnityEditor.Undo.DestroyObjectImmediate(transform.GetChild(i).gameObject);
continue;
}
#else
Object.DestroyImmediate(transform.GetChild(i).gameObject);
#endif
}
}
Sorry it's complicated
But basically this codepath runs every time a handle moves.
Works perfectly but for the undoing
It would be easy to fix if I could somehow find out when the drag stops, but I don't know how
Or if I could work out when the undo was applied, that would also be fine
Right, I get the gist I think. So here is what you should be doing I think. When you start a move/edit, cache the Undo.CurrentUndoGroup(), record changes to the wall prefabs using Undo.RecordObject(wallSegment.transform, "wall changed), when creating a new wall segment, use Undo.RegisterCreatedObjectUndo(newWallSegment, "wall created);, and when getting rid of a wall segment you use Undo.DestoryObjectImmediate(wallSegment);.
Once the change is finished, you do Undo.CollapseUndoGroup(cachedGroup);
Don't be creating and destorying every object in the wall each frame, that is going to be terrible for performance and balloon the memory. Just the delta.
Once the change is finished, you do Undo.CollapseUndoGroup(cachedGroup);
If I was able to get a change finished event then I could solve this in any number of ways.
Although my understanding is that the undo collapses itself when the input event ends?
Ahhh... I can use the current group ID as a cache breaker maybe
The problem is that this code runs every time the mouse moves (or doesn't) during the drag operation
wrt performance -- it seems fine.
It's just editor code.
lol, for now
I think you should be able to get the mouse up event
No, it's both editor and runtime code
But it only generates once in the game
There will be no free dragging of walls in the game
And it can be changed later
I've just made it so that the walls are destroyed when you undo and there's a button on the inspector to regen them
Not worth the automation