Add support for loading prefab instance if the root was changed or deleted

#2050
This commit is contained in:
Wojtek Figat
2023-12-14 10:47:22 +01:00
parent 1874382816
commit e0a085adfe
9 changed files with 360 additions and 115 deletions

View File

@@ -87,17 +87,12 @@ SceneObject* Prefab::GetDefaultInstance(const Guid& objectId)
const auto result = GetDefaultInstance();
if (!result)
return nullptr;
if (objectId.IsValid())
{
SceneObject* object;
if (ObjectsCache.TryGet(objectId, object))
{
// Actor or Script
return object;
}
}
return result;
}

View File

@@ -94,8 +94,8 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
LOG(Warning, "Waiting for prefab asset be loaded failed. {0}", prefab->ToString());
return nullptr;
}
const int32 objectsCount = prefab->ObjectsCount;
if (objectsCount == 0)
const int32 dataCount = prefab->ObjectsCount;
if (dataCount == 0)
{
LOG(Warning, "Prefab has no objects. {0}", prefab->ToString());
return nullptr;
@@ -107,7 +107,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
// Prepare
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
sceneObjects->Resize(objectsCount);
sceneObjects->Resize(dataCount);
CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback>::ScopeCache modifier = Cache::ISerializeModifier.Get();
modifier->EngineBuild = prefab->DataEngineBuild;
modifier->IdsMapping.EnsureCapacity(prefab->ObjectsIds.Count() * 4);
@@ -126,7 +126,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
// Deserialize prefab objects
auto prevIdMapping = Scripting::ObjectsLookupIdMapping.Get();
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
for (int32 i = 0; i < objectsCount; i++)
for (int32 i = 0; i < dataCount; i++)
{
auto& stream = data[i];
SceneObject* obj = SceneObjectsFactory::Spawn(context, stream);
@@ -145,7 +145,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
SceneObjectsFactory::SynchronizeNewPrefabInstances(context, prefabSyncData);
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
}
for (int32 i = 0; i < objectsCount; i++)
for (int32 i = 0; i < dataCount; i++)
{
auto& stream = data[i];
SceneObject* obj = sceneObjects->At(i);
@@ -154,20 +154,29 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
}
Scripting::ObjectsLookupIdMapping.Set(prevIdMapping);
// Pick prefab root object
if (sceneObjects->IsEmpty())
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
if (withSynchronization)
{
LOG(Warning, "No valid objects in prefab. {0}", prefab->ToString());
return nullptr;
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
}
// Pick prefab root object
Actor* root = nullptr;
const Guid prefabRootObjectId = prefab->GetRootObjectId();
for (int32 i = 0; i < objectsCount; i++)
for (int32 i = 0; i < dataCount && !root; i++)
{
if (JsonTools::GetGuid(data[i], "ID") == prefabRootObjectId)
{
root = dynamic_cast<Actor*>(sceneObjects->At(i));
break;
}
if (!root)
{
// Fallback to the first actor that has no parent
for (int32 i = 0; i < sceneObjects->Count() && !root; i++)
{
SceneObject* obj = sceneObjects->At(i);
if (obj && !obj->GetParent())
root = dynamic_cast<Actor*>(obj);
}
}
if (!root)
@@ -176,13 +185,6 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
return nullptr;
}
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
if (withSynchronization)
{
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
}
// Prepare parent linkage for prefab root actor
if (root->_parent)
root->_parent->Children.Remove(root);
@@ -264,7 +266,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
}
// Link objects to prefab (only deserialized from prefab data)
for (int32 i = 0; i < objectsCount; i++)
for (int32 i = 0; i < dataCount; i++)
{
auto& stream = data[i];
SceneObject* obj = sceneObjects->At(i);

View File

@@ -89,7 +89,7 @@ API_CLASS(Static) class FLAXENGINE_API PrefabManager
/// <param name="objectsCache">The options output objects cache that can be filled with prefab object id mapping to deserialized object (actor or script).</param>
/// <param name="withSynchronization">True if perform prefab changes synchronization for the spawned objects. It will check if need to add new objects due to nested prefab modifications.</param>
/// <returns>The created actor (root) or null if failed.</returns>
static Actor* SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid, SceneObject*, HeapAllocation>* objectsCache, bool withSynchronization = false);
static Actor* SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid, SceneObject*, HeapAllocation>* objectsCache, bool withSynchronization = true);
/// <summary>
/// Spawns the instance of the prefab objects. If parent actor is specified then created actors are fully initialized (OnLoad event and BeginPlay is called if parent actor is already during gameplay).
@@ -100,7 +100,7 @@ API_CLASS(Static) class FLAXENGINE_API PrefabManager
/// <param name="objectsCache">The options output objects cache that can be filled with prefab object id mapping to deserialized object (actor or script).</param>
/// <param name="withSynchronization">True if perform prefab changes synchronization for the spawned objects. It will check if need to add new objects due to nested prefab modifications.</param>
/// <returns>The created actor (root) or null if failed.</returns>
static Actor* SpawnPrefab(Prefab* prefab, const Transform& transform, Actor* parent, Dictionary<Guid, SceneObject*, HeapAllocation>* objectsCache, bool withSynchronization = false);
static Actor* SpawnPrefab(Prefab* prefab, const Transform& transform, Actor* parent, Dictionary<Guid, SceneObject*, HeapAllocation>* objectsCache, bool withSynchronization = true);
#if USE_EDITOR