Duplicating data between levels

I have an Actor with a UPROPERTY pointer to a UObject. Both objects “Outer” is the level.
If you duplicate (Alt drag) the Actor from one level to another then the UPROPERTY pointer to the UObject comes through as null.

This is as expected due to it being invalid to point to a UObject in another level. What I want to do is when the Actor is duplicated I want to create a copy of the original UObject into my new level and set my pointer to that. To be able to achieve this I need some way to get the UE4 duplication code to copy me a reference to my original object.

How would you suggest I achieve this?

Hi Lenny,

We’ll respond as soon as possible. Thank you for your patience.

TJ

You might be able to do this in the PostDuplicate or PostEditImport (i think this is the one you actually want) call on your actor. You may have to also override Serialize and store the reference to the other actor in a way that will persist across the duplicate level boundary as it’s very likely the reference will be null by the time either of those functions is called. Like store it in a UPROPERTY() TLazyObjectPtr OldActor, object. Then after you’ve been duplicated check it and use that to duplicate the other actor and move it also into the new level.

This is essentially how I original tried to solve the problem. Unfortunately the TLazyObjectPtr does not uniquely reference a UObject across levels. The method works until you have two actors with the same name in two different levels. If you duplicate one of these objects it is undefined as to which actor it would resolve to on the other end (In the PostDuplicate call). Given that names are not unique across levels then I need a more reliable solution.

Any chance you could make the other actor a component instead? If there’s always a 1:1 pairing, that might make more sense.

The only other option I could think of would be to add some new serialization functions like PreSerialize, and PostSerialize that are called on the originating actors that lets it move the other actor to become outered to it, and then move it back to being outered to the level in PostSerialize.

In my actual situation it is a UObject that I am referencing (Not an actor). The actor that is being duplicated is also a transient actor (IE not saved out with the level) as it is a stub actor that is only used for editing reasons; The actual data about the object is stored in the UObject that is being referenced.

My original question was posed to be simpler than my actual situation. If the simple question could be answered then I should be able to use the same method to fix my actual, more complicated situation.

The Pre/post serialise functions do not currently exist so I guess this would be a hypothetical solution as opposed to a concrete solution that can be achieved in the current code base?

Perhaps you could do something along these lines:

In the Actor’s Serialize function if the Archive has PPF_Duplicate in its PortFlags you could either cache off the properties you care about in to a struct or possibly even duplicate the referenced object as an owned object (another uproperty and now Outer is the Actor), this would need to be done before Super::Serialize so that the normal tagged properties magic picks it up.

At that point when the new Actor is created it would either have the cached properties or a duplicated instance of the object.

At that point in PostDuplicate for the new Actor you could look at the original object pointer and if it is null you know you ended up in another level and at that point you can rename the duplicated object to have the new level as its outer and fix up pointers as appropriate or allocate a new object and copy the properties in.

Keep in mind, however, that this approach might not work for copy/paste since that goes via T3D text export and may not end up entirely in the same function path.

I guess this is the most likely solution so far. As you say though the copy/paste code paths may not work as well as I will be left with an object on the original object that now needs remove (Post duplicate) which I am unsure where the best/most reliable place would be to clean things up.
There does not seem to be a very elegant solution.