Proper way to delete tagged bindings from C++

Hey there,

In our project, we generate entire dialogue sequence proceduraly from what we call “Dialogue Preset” sequences.

Those sequences contain character replaceables, as well as a large set of cameras to film about any scene.

When generating, we are not trying to clean all characters and cameras we know will not be used (missing character, irrelevant context, etc). We can easily identify which bindings are to delete as we extensively tag the characters replaceables and camera spawnables.

This seems like a straightforward thing to do, but we’re having trouble navigating the bindings to delete all relevant tracks/possessables/spawnables/etc.

We use AllTaggedBindings to identify which bindings to remove, then use a combination of UMovieScene::FindBinding, UMovieScene::RemoveTrack, UMovieScene::RemovePossessable, UMovieScene::RemoveSpawnable, and then iterate over all remaining Possessables to Remove and Unbind the ones for whom a parent was removed as long as there are modifications.

However, this approach appears flawed as there still remains some components.

To sum it up, we need to delete all data children to a Replaceable/Spawnable track identified via tag, only with the MovieSequence, and without Sequencer (as we are in a generation flow without Sequencer at that point).

How would you approach this?

Steps to Reproduce
Try to delete a Replaceable or Spawnable from C++ without leftover tracks.

Hi Karim,

Sorry about the delay. I’m looking into this now and should get back to you soon.

Best regards,

Vitor

Hi Karim,

Sorry about the extended delay. I understand you do not have Sequencer available at the time your generator runs. If you at least have access to instances of UMovieSceneSequence, which is the base class of ULevelSequence assets, you can do the following:

`void DeleteTaggedFromSequence (UMovieSceneSequence* MovieSceneSequence, FName Tag)
{
const TArray& BindingIds = MovieSceneSequence->FindBindingsByTag(Tag);
for (const FMovieSceneObjectBindingID& BindingId : BindingIds)
{
FMovieSceneBindingProxy BindingProxy = UMovieSceneSequenceExtensions::ResolveBindingID(MovieSceneSequence, BindingId);
const TArray& Children = UMovieSceneBindingExtensions::GetChildPossessables(BindingProxy);

for (FMovieSceneBindingProxy Child : Children)
UMovieSceneBindingExtensions::Remove(Child);

UMovieSceneBindingExtensions::Remove(BindingProxy);
}
}`The code above makes use of methods from classes UMovieSceneSequenceExtensions and UMovieSceneBindingExtensions, which were designed to help scripting the Editor and are unfortunately not DLL-exposed. However, for C++ usage, you can simply copy the body of the required methods, and everything should work correctly.

Now, if you must work with only UMovieScene and not UMovieSceneSequence, this can still be done for each movie scene independently, but I believe you won’t be able to access subsequence bindings correctly. Here is the code I used here in a test project:

`TArray GetChildPossessables(UMovieScene* MovieScene, const FGuid& InBinding)
{
TArray Result;

if (MovieScene)
{
FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(InBinding);
if (Spawnable)
{
for (const FGuid& ChildGuid : Spawnable->GetChildPossessables())
{
Result.Add(ChildGuid);
}
return Result;
}

const int32 Count = MovieScene->GetPossessableCount();
for (int32 i = 0; i < Count; ++i)
{
FMovieScenePossessable& PossessableChild = MovieScene->GetPossessable(i);
if (PossessableChild.GetParent() == InBinding)
{
Result.Add(PossessableChild.GetGuid());
}
}
}
return Result;
}

void DeleteTaggedFromScene (UMovieScene* MovieScene, FName Tag)
{
const FMovieSceneObjectBindingIDs* BindingIDs = MovieScene->AllTaggedBindings().Find(Tag);
if (!BindingIDs)
return;

for (const FMovieSceneObjectBindingID& BindingId : BindingIDs->IDs)
{
if (BindingId.GetRelativeSequenceID() != MovieSceneSequenceID::Root)
continue;

TArray Children = GetChildPossessables(MovieScene, BindingId.GetGuid());
for (const FGuid& Child : Children)
{
if (!MovieScene->RemovePossessable(Child))
MovieScene->RemoveSpawnable(Child);
}

if (!MovieScene->RemovePossessable(BindingId.GetGuid()))
MovieScene->RemoveSpawnable(BindingId.GetGuid());
}
}`In both snippets above, functions GetChildPossessables() are responsible for getting the component bindings parented to an object that is tagged for deletion. Then, removal of possessables and spawnables should also clear their other tracks.

Please let me know if one of these solutions work for you, and feel free to ask if you still have any difficulty.

Best regards,

Vitor

Hi Vitor,

Thanks for the detailed answer, it reassured me that we were taking the proper route.

We managed to get the behavior we were looking for by using the first snippet (and recreating the methods), as well as clearing the references in the SortedReferences and Folders as follows:

MovieSceneSequence->UnbindPossessableObjects(BindingId.GetGuid());

for (UMovieSceneFolder* Folder : RootFolders)

{

RemoveBindingFromFolderRecursive(Folder, BindingId.GetGuid()); // Calls Folder->RemoveChildObjectBinding(Guid) on folder and GetChildFolders recursively

}

As well as the inner bindings of :

TMap<FName, FMovieSceneObjectBindingIDs> AllBindings = MovieScene->AllTaggedBindings();

using UntagBinding.

Best,

Karim