Fix creating prefabs directly from prefab objects

#1432
This commit is contained in:
Wojtek Figat
2023-10-06 10:07:57 +02:00
parent 3a56e39306
commit 9870d162e4
5 changed files with 56 additions and 29 deletions

View File

@@ -37,11 +37,6 @@ namespace FlaxEditor.Modules
/// </summary> /// </summary>
public event Action<Prefab, Actor> PrefabApplied; public event Action<Prefab, Actor> PrefabApplied;
/// <summary>
/// Locally cached actor for prefab creation.
/// </summary>
private Actor _prefabCreationActor;
internal PrefabsModule(Editor editor) internal PrefabsModule(Editor editor)
: base(editor) : base(editor)
{ {
@@ -65,13 +60,14 @@ namespace FlaxEditor.Modules
/// To create prefab manually (from code) use <see cref="PrefabManager.CreatePrefab"/> method. /// To create prefab manually (from code) use <see cref="PrefabManager.CreatePrefab"/> method.
/// </remarks> /// </remarks>
/// <param name="selection">The scene selection to use.</param> /// <param name="selection">The scene selection to use.</param>
public void CreatePrefab(List<SceneGraphNode> selection) /// <param name="prefabWindow">The prefab window that creates it.</param>
public void CreatePrefab(List<SceneGraphNode> selection, Windows.Assets.PrefabWindow prefabWindow = null)
{ {
if (selection == null) if (selection == null)
selection = Editor.SceneEditing.Selection; selection = Editor.SceneEditing.Selection;
if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab) if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab)
{ {
CreatePrefab(actorNode.Actor); CreatePrefab(actorNode.Actor, true, prefabWindow);
} }
} }
@@ -92,7 +88,8 @@ namespace FlaxEditor.Modules
/// </summary> /// </summary>
/// <param name="actor">The root prefab actor.</param> /// <param name="actor">The root prefab actor.</param>
/// <param name="rename">Allow renaming or not</param> /// <param name="rename">Allow renaming or not</param>
public void CreatePrefab(Actor actor, bool rename) /// <param name="prefabWindow">The prefab window that creates it.</param>
public void CreatePrefab(Actor actor, bool rename, Windows.Assets.PrefabWindow prefabWindow = null)
{ {
// Skip in invalid states // Skip in invalid states
if (!Editor.StateMachine.CurrentState.CanEditContent) if (!Editor.StateMachine.CurrentState.CanEditContent)
@@ -105,42 +102,47 @@ namespace FlaxEditor.Modules
PrefabCreating?.Invoke(actor); PrefabCreating?.Invoke(actor);
var proxy = Editor.ContentDatabase.GetProxy<Prefab>(); var proxy = Editor.ContentDatabase.GetProxy<Prefab>();
_prefabCreationActor = actor; Editor.Windows.ContentWin.NewItem(proxy, actor, contentItem => OnPrefabCreated(contentItem, actor, prefabWindow), actor.Name, rename);
Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name, rename);
} }
private void OnPrefabCreated(ContentItem contentItem) private void OnPrefabCreated(ContentItem contentItem, Actor actor, Windows.Assets.PrefabWindow prefabWindow)
{ {
if (contentItem is PrefabItem prefabItem) if (contentItem is PrefabItem prefabItem)
{ {
PrefabCreated?.Invoke(prefabItem); PrefabCreated?.Invoke(prefabItem);
} }
// Skip in invalid states Undo undo = null;
if (!Editor.StateMachine.CurrentState.CanEditScene) if (prefabWindow != null)
return; {
prefabWindow.MarkAsEdited();
undo = prefabWindow.Undo;
}
else
{
// Skip in invalid states
if (!Editor.StateMachine.CurrentState.CanEditScene)
return;
undo = Editor.Undo;
}
// Record undo for prefab creating (backend links the target instance with the prefab) // Record undo for prefab creating (backend links the target instance with the prefab)
if (Editor.Undo.Enabled) if (undo.Enabled)
{ {
if (!_prefabCreationActor) if (!actor)
return; return;
var actorsList = new List<Actor>(); var actorsList = new List<Actor>();
Utilities.Utils.GetActorsTree(actorsList, _prefabCreationActor); Utilities.Utils.GetActorsTree(actorsList, actor);
var actions = new IUndoAction[actorsList.Count]; var actions = new IUndoAction[actorsList.Count];
for (int i = 0; i < actorsList.Count; i++) for (int i = 0; i < actorsList.Count; i++)
{ actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]);
var action = BreakPrefabLinkAction.Linked(actorsList[i]); undo.AddAction(new MultiUndoAction(actions));
actions[i] = action;
}
Undo.AddAction(new MultiUndoAction(actions));
_prefabCreationActor = null;
} }
Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout(); Editor.Windows.PropertiesWin.Presenter.BuildLayout();
if (prefabWindow != null)
prefabWindow.Presenter.BuildLayout();
} }
/// <summary> /// <summary>

View File

@@ -233,7 +233,7 @@ namespace FlaxEditor.Windows.Assets
contextMenu.AddSeparator(); contextMenu.AddSeparator();
b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection)); b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection, this));
b.Enabled = isSingleActorSelected && b.Enabled = isSingleActorSelected &&
(Selection[0] as ActorNode).CanCreatePrefab && (Selection[0] as ActorNode).CanCreatePrefab &&
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;

View File

@@ -53,6 +53,11 @@ namespace FlaxEditor.Windows.Assets
/// </summary> /// </summary>
public PrefabWindowViewport Viewport => _viewport; public PrefabWindowViewport Viewport => _viewport;
/// <summary>
/// Gets the prefab objects properties editor.
/// </summary>
public CustomEditorPresenter Presenter => _propertiesEditor;
/// <summary> /// <summary>
/// Gets the undo system used by this window for changes tracking. /// Gets the undo system used by this window for changes tracking.
/// </summary> /// </summary>

View File

@@ -321,6 +321,8 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
// Serialize to json data // Serialize to json data
ASSERT(!IsCreatingPrefab); ASSERT(!IsCreatingPrefab);
IsCreatingPrefab = true; IsCreatingPrefab = true;
const Guid targetPrefabId = targetActor->GetPrefabID();
const bool hasTargetPrefabId = targetPrefabId.IsValid();
rapidjson_flax::StringBuffer actorsDataBuffer; rapidjson_flax::StringBuffer actorsDataBuffer;
{ {
CompactJsonWriter writerObj(actorsDataBuffer); CompactJsonWriter writerObj(actorsDataBuffer);
@@ -329,7 +331,27 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
for (int32 i = 0; i < sceneObjects->Count(); i++) for (int32 i = 0; i < sceneObjects->Count(); i++)
{ {
SceneObject* obj = sceneObjects->At(i); SceneObject* obj = sceneObjects->At(i);
// Detect when creating prefab from object that is already part of prefab then serialize it as unlinked
const Guid prefabId = obj->GetPrefabID();
const Guid prefabObjectId = obj->GetPrefabObjectID();
bool isObjectFromPrefab = targetPrefabId == prefabId && prefabId.IsValid(); // Allow to use other nested prefabs properly (ignore only root object's prefab link)
if (isObjectFromPrefab)
{
//obj->BreakPrefabLink();
obj->_prefabID = Guid::Empty;
obj->_prefabObjectID = Guid::Empty;
}
writer.SceneObject(obj); writer.SceneObject(obj);
// Restore broken link
if (hasTargetPrefabId)
{
//obj->LinkPrefab(prefabId, prefabObjectId);
obj->_prefabID = prefabId;
obj->_prefabObjectID = prefabObjectId;
}
} }
writer.EndArray(); writer.EndArray();
} }
@@ -395,7 +417,6 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
{ {
SceneObject* obj = sceneObjects->At(i); SceneObject* obj = sceneObjects->At(i);
Guid prefabObjectId; Guid prefabObjectId;
if (objectInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId)) if (objectInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId))
{ {
obj->LinkPrefab(assetInfo.ID, prefabObjectId); obj->LinkPrefab(assetInfo.ID, prefabObjectId);

View File

@@ -81,7 +81,6 @@ void JsonTools::ChangeIds(Document& doc, const Dictionary<Guid, Guid>& mapping)
::ChangeIds(doc, doc, mapping); ::ChangeIds(doc, doc, mapping);
} }
Float2 JsonTools::GetFloat2(const Value& value) Float2 JsonTools::GetFloat2(const Value& value)
{ {
Float2 result; Float2 result;