From 42fc62eb68670888a2d5e8243c6a127a4051911e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 31 Jan 2025 10:21:05 +0100 Subject: [PATCH] Add `Actor.Clone` for actors duplication at runtime (including scripts and children) #2012 --- Source/Engine/Level/Actor.cpp | 39 +++++++++++++++++++++++++++++++++-- Source/Engine/Level/Actor.h | 5 +++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index bbc90ab7f..107e370e6 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -3,6 +3,7 @@ #include "Actor.h" #include "ActorsCache.h" #include "Level.h" +#include "SceneQuery.h" #include "SceneObjectsFactory.h" #include "Scene/Scene.h" #include "Prefabs/Prefab.h" @@ -1880,8 +1881,7 @@ String Actor::ToJson() CompactJsonWriter writer(buffer); writer.SceneObject(this); String result; - const char* c = buffer.GetString(); - result.SetUTF8(c, (int32)buffer.GetSize()); + result.SetUTF8(buffer.GetString(), (int32)buffer.GetSize()); return result; } @@ -1909,6 +1909,41 @@ void Actor::FromJson(const StringAnsiView& json) OnTransformChanged(); } +Actor* Actor::Clone() +{ + // Collect actors to clone + auto actors = ActorsCache::ActorsListCache.Get(); + actors->Add(this); + SceneQuery::GetAllActors(this, *actors); + + // Serialize objects + MemoryWriteStream stream; + if (ToBytes(*actors, stream)) + return nullptr; + + // Remap object ids into a new ones + auto modifier = Cache::ISerializeModifier.Get(); + for (int32 i = 0; i < actors->Count(); i++) + { + auto actor = actors->At(i); + if (!actor) + continue; + modifier->IdsMapping.Add(actor->GetID(), Guid::New()); + for (int32 j = 0; j < actor->Scripts.Count(); j++) + { + const auto script = actor->Scripts[j]; + if (script) + modifier->IdsMapping.Add(script->GetID(), Guid::New()); + } + } + + // Deserialize objects + Array output; + if (FromBytes(ToSpan(stream.GetHandle(), (int32)stream.GetPosition()), output, modifier.Value) || output.IsEmpty()) + return nullptr; + return output[0]; +} + void Actor::SetPhysicsScene(PhysicsScene* scene) { CHECK(scene); diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 715784d9e..9629c5b29 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -982,6 +982,11 @@ public: /// The serialized actor data (state). API_FUNCTION() void FromJson(const StringAnsiView& json); + /// + /// Clones actor including all scripts and any child actors (whole scene tree). Objects are duplicated via serialization (any transient/non-saved state is ignored). + /// + API_FUNCTION() Actor* Clone(); + public: /// /// Called when actor gets added to game systems. Occurs on BeginPlay event or when actor gets activated in hierarchy. Use this event to register object to other game system (eg. audio).