Add SpawnOptions container for more robust prefabs spawning
This commit is contained in:
@@ -181,6 +181,18 @@ public:
|
||||
obj->SetParent(nullptr);
|
||||
obj->DeleteObject();
|
||||
}
|
||||
|
||||
static void SerializeObjects(const ActorsCache::SceneObjectsListType& sceneObjects, JsonWriter& writer)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
writer.StartArray();
|
||||
for (int32 i = 0; i < sceneObjects.Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects[i];
|
||||
writer.SceneObject(obj);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
};
|
||||
|
||||
void PrefabInstanceData::CollectPrefabInstances(PrefabInstancesData& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor)
|
||||
@@ -235,14 +247,7 @@ void PrefabInstanceData::SerializePrefabInstances(PrefabInstancesData& prefabIns
|
||||
// Serialize
|
||||
tmpBuffer.Clear();
|
||||
CompactJsonWriter writerObj(tmpBuffer);
|
||||
JsonWriter& writer = writerObj;
|
||||
writer.StartArray();
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects.Value->At(i);
|
||||
writer.SceneObject(obj);
|
||||
}
|
||||
writer.EndArray();
|
||||
SerializeObjects(*sceneObjects.Value, writerObj);
|
||||
|
||||
// Parse json to get DOM
|
||||
{
|
||||
@@ -746,14 +751,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
rapidjson_flax::StringBuffer dataBuffer;
|
||||
{
|
||||
CompactJsonWriter writerObj(dataBuffer);
|
||||
JsonWriter& writer = writerObj;
|
||||
writer.StartArray();
|
||||
for (int32 i = 0; i < targetObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = targetObjects.Value->At(i);
|
||||
writer.SceneObject(obj);
|
||||
}
|
||||
writer.EndArray();
|
||||
PrefabInstanceData::SerializeObjects(*targetObjects.Value, writerObj);
|
||||
}
|
||||
|
||||
// Parse json document and modify serialized data to extract only modified properties
|
||||
@@ -1079,14 +1077,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
{
|
||||
tmpBuffer.Clear();
|
||||
PrettyJsonWriter writerObj(tmpBuffer);
|
||||
JsonWriter& writer = writerObj;
|
||||
writer.StartArray();
|
||||
for (int32 i = 0; i < defaultInstanceObjects.Count(); i++)
|
||||
{
|
||||
auto obj = defaultInstanceObjects.At(i);
|
||||
writer.SceneObject(obj);
|
||||
}
|
||||
writer.EndArray();
|
||||
PrefabInstanceData::SerializeObjects(defaultInstanceObjects, writerObj);
|
||||
}
|
||||
|
||||
LOG(Info, "Updating prefab data");
|
||||
|
||||
@@ -40,7 +40,9 @@ PrefabManagerService PrefabManagerServiceInstance;
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab)
|
||||
{
|
||||
Actor* parent = Level::Scenes.Count() != 0 ? Level::Scenes.Get()[0] : nullptr;
|
||||
return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, nullptr);
|
||||
SpawnOptions options;
|
||||
options.Parent = parent;
|
||||
return SpawnPrefab(prefab, options);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Vector3& position)
|
||||
@@ -69,20 +71,39 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform)
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, const Transform& transform)
|
||||
{
|
||||
return SpawnPrefab(prefab, transform, parent, nullptr);
|
||||
SpawnOptions options;
|
||||
options.Transform = &transform;
|
||||
options.Parent = parent;
|
||||
return SpawnPrefab(prefab, options);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent)
|
||||
{
|
||||
return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, nullptr);
|
||||
SpawnOptions options;
|
||||
options.Parent = parent;
|
||||
return SpawnPrefab(prefab, options);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid, SceneObject*>* objectsCache, bool withSynchronization)
|
||||
{
|
||||
return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, objectsCache, withSynchronization);
|
||||
SpawnOptions options;
|
||||
options.Parent = parent;
|
||||
options.ObjectsCache = objectsCache;
|
||||
options.WithSync = withSynchronization;
|
||||
return SpawnPrefab(prefab, options);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Actor* parent, Dictionary<Guid, SceneObject*>* objectsCache, bool withSynchronization)
|
||||
{
|
||||
SpawnOptions options;
|
||||
options.Transform = &transform;
|
||||
options.Parent = parent;
|
||||
options.ObjectsCache = objectsCache;
|
||||
options.WithSync = withSynchronization;
|
||||
return SpawnPrefab(prefab, options);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const SpawnOptions& options)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Prefab.Spawn");
|
||||
if (prefab == nullptr)
|
||||
@@ -111,15 +132,27 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
sceneObjects->Resize(dataCount);
|
||||
CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback>::ScopeCache modifier = Cache::ISerializeModifier.Get();
|
||||
modifier->EngineBuild = prefab->DataEngineBuild;
|
||||
for (int32 i = 0; i < prefab->ObjectsIds.Count(); i++)
|
||||
modifier->IdsMapping.EnsureCapacity(prefab->ObjectsIds.Count());
|
||||
if (options.IDs)
|
||||
{
|
||||
modifier->IdsMapping.Add(prefab->ObjectsIds[i], Guid::New());
|
||||
for (int32 i = 0; i < prefab->ObjectsIds.Count(); i++)
|
||||
{
|
||||
Guid prefabObjectId = prefab->ObjectsIds[i];
|
||||
Guid id;
|
||||
if (!options.IDs->TryGet(prefabObjectId, id))
|
||||
id = Guid::New();
|
||||
modifier->IdsMapping.Add(id, id);
|
||||
}
|
||||
}
|
||||
if (objectsCache)
|
||||
else
|
||||
{
|
||||
objectsCache->Clear();
|
||||
objectsCache->SetCapacity(prefab->ObjectsDataCache.Capacity());
|
||||
for (int32 i = 0; i < prefab->ObjectsIds.Count(); i++)
|
||||
modifier->IdsMapping.Add(prefab->ObjectsIds[i], Guid::New());
|
||||
}
|
||||
if (options.ObjectsCache)
|
||||
{
|
||||
options.ObjectsCache->Clear();
|
||||
options.ObjectsCache->SetCapacity(prefab->ObjectsDataCache.Count());
|
||||
}
|
||||
auto& data = *prefab->Data;
|
||||
SceneObjectsFactory::Context context(modifier.Value);
|
||||
@@ -139,7 +172,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, data, modifier.Value);
|
||||
if (withSynchronization)
|
||||
if (options.WithSync)
|
||||
{
|
||||
// Synchronize new prefab instances (prefab may have new objects added so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync prefabs during game cooking so this step could be skipped in game
|
||||
@@ -157,7 +190,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
Scripting::ObjectsLookupIdMapping.Set(prevIdMapping);
|
||||
|
||||
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
|
||||
if (withSynchronization)
|
||||
if (options.WithSync)
|
||||
{
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
|
||||
@@ -190,13 +223,13 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
// Prepare parent linkage for prefab root actor
|
||||
if (root->_parent)
|
||||
root->_parent->Children.Remove(root);
|
||||
root->_parent = parent;
|
||||
if (parent)
|
||||
parent->Children.Add(root);
|
||||
root->_parent = options.Parent;
|
||||
if (options.Parent)
|
||||
options.Parent->Children.Add(root);
|
||||
|
||||
// Move root to the right location
|
||||
if (transform.Translation != Vector3::Minimum)
|
||||
root->SetTransform(transform);
|
||||
if (options.Transform)
|
||||
root->SetTransform(*options.Transform);
|
||||
|
||||
// Link actors hierarchy
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
@@ -268,24 +301,39 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
}
|
||||
|
||||
// Link objects to prefab (only deserialized from prefab data)
|
||||
for (int32 i = 0; i < dataCount; i++)
|
||||
if (options.WithLink)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (!obj)
|
||||
continue;
|
||||
for (int32 i = 0; i < dataCount; i++)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
const Guid prefabObjectId = JsonTools::GetGuid(stream, "ID");
|
||||
if (objectsCache)
|
||||
objectsCache->Add(prefabObjectId, obj);
|
||||
obj->LinkPrefab(prefabId, prefabObjectId);
|
||||
const Guid prefabObjectId = JsonTools::GetGuid(stream, "ID");
|
||||
if (options.ObjectsCache)
|
||||
options.ObjectsCache->Add(prefabObjectId, obj);
|
||||
obj->LinkPrefab(prefabId, prefabObjectId);
|
||||
}
|
||||
}
|
||||
else if (options.ObjectsCache)
|
||||
{
|
||||
for (int32 i = 0; i < dataCount; i++)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (!obj)
|
||||
continue;
|
||||
const Guid prefabObjectId = JsonTools::GetGuid(stream, "ID");
|
||||
options.ObjectsCache->Add(prefabObjectId, obj);
|
||||
}
|
||||
}
|
||||
|
||||
// Update transformations
|
||||
root->OnTransformChanged();
|
||||
|
||||
// Spawn if need to
|
||||
if (parent && parent->IsDuringPlay())
|
||||
if (options.Parent && options.Parent->IsDuringPlay())
|
||||
{
|
||||
// Begin play
|
||||
SceneBeginData beginData;
|
||||
|
||||
@@ -102,6 +102,30 @@ API_CLASS(Static) class FLAXENGINE_API PrefabManager
|
||||
/// <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 = true);
|
||||
|
||||
struct FLAXENGINE_API SpawnOptions
|
||||
{
|
||||
// Spawn transformation.
|
||||
const Transform* Transform = nullptr;
|
||||
// The parent actor to add spawned object instance. Can be null to just deserialize contents of the prefab.
|
||||
Actor* Parent = nullptr;
|
||||
// Custom objects mapping (maps prefab objects into spawned objects).
|
||||
const Dictionary<Guid, Guid, HeapAllocation>* IDs = nullptr;
|
||||
// Output objects cache that can be filled with prefab object id mapping to deserialized object (actor or script).
|
||||
Dictionary<Guid, SceneObject*, HeapAllocation>* ObjectsCache = nullptr;
|
||||
// if perform prefab changes synchronization for the spawned objects. It will check if need to add new objects due to nested prefab modifications.
|
||||
bool WithSync = true;
|
||||
// True if linked spawned prefab objects with the source prefab, otherwise links will be valid only for nested prefab objects.
|
||||
bool WithLink = 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).
|
||||
/// </summary>
|
||||
/// <param name="prefab">The prefab asset.</param>
|
||||
/// <param name="options">The spawn options container.</param>
|
||||
/// <returns>The created actor (root) or null if failed.</returns>
|
||||
static Actor* SpawnPrefab(Prefab* prefab, const SpawnOptions& options);
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user