From 71991ff8c73e5f3c76004a4b4ea35bed3e512be9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 3 Jun 2025 15:25:45 -0500 Subject: [PATCH 1/2] Show added and removed actors in prefab diff view. --- .../CustomEditors/Dedicated/ActorEditor.cs | 109 +++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 540b602ec..837e7ad3b 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -240,6 +240,12 @@ namespace FlaxEditor.CustomEditors.Dedicated node.TextColor = Color.OrangeRed; node.Text = Utilities.Utils.GetPropertyNameUI(removed.PrefabObject.GetType().Name); } + // Removed Actor + else if (editor is RemovedActorDummy removedActor) + { + node.TextColor = Color.OrangeRed; + node.Text = $"{removedActor.PrefabObject.Name} ({Utilities.Utils.GetPropertyNameUI(removedActor.PrefabObject.GetType().Name)})"; + } // Actor or Script else if (editor.Values[0] is SceneObject sceneObject) { @@ -295,11 +301,35 @@ namespace FlaxEditor.CustomEditors.Dedicated // Not used } } + + private class RemovedActorDummy : CustomEditor + { + /// + /// The removed prefab object (from the prefab default instance). + /// + public Actor PrefabObject; + + /// + /// The prefab instance's parent. + /// + public Actor ParentActor; + + /// + /// The order of the removed actor in the parent. + /// + public int OrderInParent; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + // Not used + } + } private TreeNode ProcessDiff(CustomEditor editor, bool skipIfNotModified = true) { - // Special case for new Script added to actor - if (editor.Values[0] is Script script && !script.HasPrefabLink) + // Special case for new Script or child actor added to actor + if ((editor.Values[0] is Script script && !script.HasPrefabLink) || (editor.Values[0] is Actor a && !a.HasPrefabLink)) return CreateDiffNode(editor); // Skip if no change detected @@ -359,6 +389,41 @@ namespace FlaxEditor.CustomEditors.Dedicated } } + // Compare child actors for removed actors. + if (editor is ActorEditor && editor.Values.HasReferenceValue && editor.Values.ReferenceValue is Actor prefabObjectActor) + { + var thisActor = editor.Values[0] as Actor; + for (int i = 0; i < prefabObjectActor.ChildrenCount; i++) + { + var prefabActorChild = prefabObjectActor.Children[i]; + if (thisActor == null) + continue; + bool isRemoved = true; + for (int j = 0; j < thisActor.ChildrenCount; j++) + { + var actorChild = thisActor.Children[j]; + if (actorChild.PrefabObjectID == prefabActorChild.PrefabObjectID) + { + isRemoved = false; + break; + } + } + if (isRemoved) + { + var dummy = new RemovedActorDummy + { + PrefabObject = prefabActorChild, + ParentActor = thisActor, + OrderInParent = prefabActorChild.OrderInParent, + }; + var child = CreateDiffNode(dummy); + if (result == null) + result = CreateDiffNode(editor); + result.AddChild(child); + } + } + } + return result; } @@ -459,6 +524,25 @@ namespace FlaxEditor.CustomEditors.Dedicated return; } + // Special case for reverting removed Actors + if (editor is RemovedActorDummy removedActor) + { + Editor.Log("Reverting removed actor changes to prefab (adding it)"); + + var parentActor = removedActor.ParentActor; + var restored = parentActor.AddChild(removedActor.PrefabObject.GetType()); + var prefabId = parentActor.PrefabID; + var prefabObjectId = removedActor.PrefabObject.PrefabObjectID; + string data = JsonSerializer.Serialize(removedActor.PrefabObject); + JsonSerializer.Deserialize(restored, data); + if (Presenter.Owner is PropertiesWindow propertiesWindow) + Editor.Instance.SceneEditing.Spawn(restored, parentActor, removedActor.OrderInParent); + else if (Presenter.Owner is PrefabWindow prefabWindow) + prefabWindow.Spawn(restored, parentActor, removedActor.OrderInParent); + Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(restored), ref prefabId, ref prefabObjectId); + return; + } + // Special case for new Script added to actor if (editor.Values[0] is Script script && !script.HasPrefabLink) { @@ -470,6 +554,27 @@ namespace FlaxEditor.CustomEditors.Dedicated return; } + + // Special case for new Actor added to actor + if (editor.Values[0] is Actor a && !a.HasPrefabLink) + { + Editor.Log("Reverting added actor changes to prefab (removing it)"); + + // TODO: Keep previous selection. + if (Presenter.Owner is PropertiesWindow propertiesWindow) + { + var editorInstance = Editor.Instance.SceneEditing; + editorInstance.Select(a); + editorInstance.Delete(); + } + else if (Presenter.Owner is PrefabWindow prefabWindow) + { + prefabWindow.Select(prefabWindow.Graph.Root.Find(a)); + prefabWindow.Delete(); + } + + return; + } editor.RevertToReferenceValue(); } From d47ac95681be596fdd0ef19603a2d37ac62e8601 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Aug 2025 23:48:25 +0200 Subject: [PATCH 2/2] Modernize the code to use unified scene access #3513 --- .../CustomEditors/CustomEditorPresenter.cs | 5 ++++ .../CustomEditors/Dedicated/ActorEditor.cs | 26 ++++++------------- .../Editor/SceneGraph/ISceneEditingContext.cs | 17 +++++++++++- Source/Editor/SceneGraph/RootNode.cs | 2 +- .../Windows/Assets/BehaviorTreeWindow.cs | 3 +++ .../Windows/Assets/PrefabWindow.Hierarchy.cs | 16 +++++++++--- Source/Editor/Windows/Assets/PrefabWindow.cs | 3 +++ Source/Editor/Windows/PropertiesWindow.cs | 3 +++ Source/Editor/Windows/SceneEditorWindow.cs | 12 +++++++++ 9 files changed, 64 insertions(+), 23 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs index 1030abfda..ccf712904 100644 --- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs +++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs @@ -63,6 +63,11 @@ namespace FlaxEditor.CustomEditors /// Indication of if the properties window is locked on specific objects. /// public bool LockSelection { get; set; } + + /// + /// Gets the scene editing context. + /// + public ISceneEditingContext SceneContext { get; } } /// diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 5b3701789..ecdc1f7db 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -1,8 +1,5 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System; -using System.Collections.Generic; -using System.Linq; using FlaxEditor.Actions; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.CustomEditors.Elements; @@ -10,10 +7,14 @@ using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tree; using FlaxEditor.Scripting; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; using FlaxEngine.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; namespace FlaxEditor.CustomEditors.Dedicated { @@ -542,10 +543,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var prefabObjectId = removedActor.PrefabObject.PrefabObjectID; string data = JsonSerializer.Serialize(removedActor.PrefabObject); JsonSerializer.Deserialize(restored, data); - if (Presenter.Owner is PropertiesWindow propertiesWindow) - Editor.Instance.SceneEditing.Spawn(restored, parentActor, removedActor.OrderInParent); - else if (Presenter.Owner is PrefabWindow prefabWindow) - prefabWindow.Spawn(restored, parentActor, removedActor.OrderInParent); + Presenter.Owner.SceneContext.Spawn(restored, parentActor, removedActor.OrderInParent); Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(restored), ref prefabId, ref prefabObjectId); return; } @@ -568,17 +566,9 @@ namespace FlaxEditor.CustomEditors.Dedicated Editor.Log("Reverting added actor changes to prefab (removing it)"); // TODO: Keep previous selection. - if (Presenter.Owner is PropertiesWindow propertiesWindow) - { - var editorInstance = Editor.Instance.SceneEditing; - editorInstance.Select(a); - editorInstance.Delete(); - } - else if (Presenter.Owner is PrefabWindow prefabWindow) - { - prefabWindow.Select(prefabWindow.Graph.Root.Find(a)); - prefabWindow.Delete(); - } + var context = Presenter.Owner.SceneContext; + context.Select(SceneGraph.SceneGraphFactory.FindNode(a.ID)); + context.DeleteSelection(); return; } diff --git a/Source/Editor/SceneGraph/ISceneEditingContext.cs b/Source/Editor/SceneGraph/ISceneEditingContext.cs index d480ceba5..cc750d854 100644 --- a/Source/Editor/SceneGraph/ISceneEditingContext.cs +++ b/Source/Editor/SceneGraph/ISceneEditingContext.cs @@ -1,8 +1,9 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System.Collections.Generic; using FlaxEditor.SceneGraph; using FlaxEditor.Viewport; +using FlaxEngine; +using System.Collections.Generic; namespace FlaxEditor { @@ -39,9 +40,23 @@ namespace FlaxEditor /// void RenameSelection(); + /// + /// Deletes selected objects. + /// + void DeleteSelection(); + /// /// Focuses selected objects. /// void FocusSelection(); + + /// + /// Spawns the specified actor to the game (with undo). + /// + /// The actor. + /// The parent actor. Set null as default. + /// The order under the parent to put the spawned actor. + /// True if automatically select the spawned actor, otherwise false. + void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true); } } diff --git a/Source/Editor/SceneGraph/RootNode.cs b/Source/Editor/SceneGraph/RootNode.cs index 1a3e47be8..71a9dc83a 100644 --- a/Source/Editor/SceneGraph/RootNode.cs +++ b/Source/Editor/SceneGraph/RootNode.cs @@ -175,7 +175,7 @@ namespace FlaxEditor.SceneGraph public List Selection => SceneContext.Selection; /// - /// Gets the list of selected scene graph nodes in the editor context. + /// Gets the scene editing context. /// public abstract ISceneEditingContext SceneContext { get; } } diff --git a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs index 2b86664f2..03ba26fc8 100644 --- a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs +++ b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs @@ -641,5 +641,8 @@ namespace FlaxEditor.Windows.Assets /// public bool LockSelection { get; set; } + + /// + public ISceneEditingContext SceneContext => null; } } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 24854afb9..0eecc4861 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -430,6 +430,12 @@ namespace FlaxEditor.Windows.Assets } } + /// + public void DeleteSelection() + { + Delete(); + } + /// public void FocusSelection() { @@ -488,7 +494,8 @@ namespace FlaxEditor.Windows.Assets /// The actor. /// The parent. /// The order of the actor under the parent. - public void Spawn(Actor actor, Actor parent, int orderInParent = -1) + /// True if automatically select the spawned actor, otherwise false. + public void Spawn(Actor actor, Actor parent, int orderInParent = -1, bool autoSelect = true) { if (actor == null) throw new ArgumentNullException(nameof(actor)); @@ -514,8 +521,11 @@ namespace FlaxEditor.Windows.Assets // Create undo action var action = new CustomDeleteActorsAction(new List(1) { actorNode }, true); Undo.AddAction(action); - Focus(); - Select(actorNode); + if (autoSelect) + { + Focus(); + Select(actorNode); + } } private void OnTreeRightClick(TreeNode node, Float2 location) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 44c21d863..7f17053ac 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -91,6 +91,9 @@ namespace FlaxEditor.Windows.Assets } } + /// + public ISceneEditingContext SceneContext => this; + /// /// Gets or sets a value indicating whether use live reloading for the prefab changes (applies prefab changes on modification by auto). /// diff --git a/Source/Editor/Windows/PropertiesWindow.cs b/Source/Editor/Windows/PropertiesWindow.cs index e90003038..03316a221 100644 --- a/Source/Editor/Windows/PropertiesWindow.cs +++ b/Source/Editor/Windows/PropertiesWindow.cs @@ -58,6 +58,9 @@ namespace FlaxEditor.Windows } } + /// + public ISceneEditingContext SceneContext => Editor.Windows.EditWin; + /// /// Initializes a new instance of the class. /// diff --git a/Source/Editor/Windows/SceneEditorWindow.cs b/Source/Editor/Windows/SceneEditorWindow.cs index e3c72d069..888cb0888 100644 --- a/Source/Editor/Windows/SceneEditorWindow.cs +++ b/Source/Editor/Windows/SceneEditorWindow.cs @@ -26,12 +26,24 @@ namespace FlaxEditor.Windows FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); } + /// + public void DeleteSelection() + { + Editor.SceneEditing.Delete(); + } + /// public void FocusSelection() { Editor.Windows.EditWin.Viewport.FocusSelection(); } + /// + public void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true) + { + Editor.SceneEditing.Spawn(actor, parent, orderInParent, autoSelect); + } + /// public EditorViewport Viewport => Editor.Windows.EditWin.Viewport;