Hi Guillermo,
If you find it helpful, you can programmatically open and close the Sequencer Editor for a given Sequence by using the functions from ULevelSequenceEditorBlueprintLibrary:
ULevelSequenceEditorBlueprintLibrary::OpenLevelSequence(); ULevelSequenceEditorBlueprintLibrary::CloseLevelSequence(); ULevelSequenceEditorBlueprintLibrary::GetCurrentLevelSequence();
On the other hand, if you change your mind about grabbing some code from the engine, here are some functions you can use. You will find that some of them are similar to the ones I provided you on the referenced topic, while also handling the new MovieSceneSpawnableActorBindings.
`FString UMyEditorLibrary::GetBindingName (const FMovieSceneBindingProxy& InBinding)
{
UMovieScene* MovieScene = InBinding.Sequence ? InBinding.Sequence->GetMovieScene() : nullptr;
if (MovieScene && InBinding.BindingID.IsValid())
{
FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(InBinding.BindingID);
if (Spawnable)
{
return Spawnable->GetName();
}
FMovieScenePossessable* Possessable = MovieScene->FindPossessable(InBinding.BindingID);
if (Possessable)
{
return Possessable->GetName();
}
}
return FString();
}
TArray UMyEditorLibrary::GetBindings (UMovieSceneSequence* Sequence)
{
if (!Sequence)
return {};
UMovieScene* MovieScene = Sequence->GetMovieScene();
if (!MovieScene)
return {};
TArray Found;
const TArray& Bindings = MovieScene->GetBindings();
for (const FMovieSceneBinding& Binding : Bindings)
{
Found.Emplace(Binding.GetObjectGuid(), Sequence);
}
return Found;
}
TArray UMyEditorLibrary::GetSpawnables (UMovieSceneSequence* Sequence)
{
if (!Sequence)
return {};
TArray Found;
// Find MovieSceneSpawnableActorBindings
if (const FMovieSceneBindingReferences* BindingReferences = Sequence->GetBindingReferences())
{
for (const FMovieSceneBindingReference& BindingReference : BindingReferences->GetAllReferences())
{
if (BindingReference.CustomBinding && BindingReference.CustomBinding->IsA())
{
Found.Emplace(BindingReference.ID, Sequence);
}
}
}
// Find MovieSceneSpawnables
UMovieScene* MovieScene = Sequence->GetMovieScene();
if (MovieScene)
{
int32 Count = MovieScene->GetSpawnableCount();
for (int32 i = 0; i < Count; ++i)
{
const FMovieSceneSpawnable& Spawnable = MovieScene->GetSpawnable(i);
Found.Emplace(Spawnable.GetGuid(), Sequence);
}
}
return Found;
}
TArray UMyEditorLibrary::GetPossessables (UMovieSceneSequence* Sequence)
{
if (!Sequence)
return {};
UMovieScene* MovieScene = Sequence->GetMovieScene();
if (!MovieScene)
return {};
TArray Found;
if (const FMovieSceneBindingReferences* BindingReferences = Sequence->GetBindingReferences())
{
for (const FMovieSceneBindingReference& BindingReference : BindingReferences->GetAllReferences())
{
if (!BindingReference.CustomBinding)
{
Found.Emplace(BindingReference.ID, Sequence);
}
}
}
else
{
int32 Count = MovieScene->GetPossessableCount();
for (int32 i = 0; i < Count; ++i)
{
const FMovieScenePossessable& Possessable = MovieScene->GetPossessable(i);
Found.Emplace(Possessable.GetGuid(), Sequence);
}
}
return Found;
}
TArray UMyEditorLibrary::FindBindingsByName(UMovieSceneSequence* Sequence, FString NameToFind)
{
if (!Sequence)
return {};
UMovieScene* MovieScene = Sequence->GetMovieScene();
if (!MovieScene)
return {};
TArray Found;
const TArray& Bindings = MovieScene->GetBindings();
for (const FMovieSceneBinding& Binding : Bindings)
{
if (Binding.GetName() == NameToFind)
{
Found.Emplace(Binding.GetObjectGuid(), Sequence);
}
}
return Found;
}
TArray UMyEditorLibrary::FindSpawnablesByName (UMovieSceneSequence* Sequence, FString NameToFind)
{
TArray Found;
TArray Spawnables = GetSpawnables(Sequence);
for (const FMovieSceneBindingProxy& Spawnable : Spawnables)
{
if (GetBindingName(Spawnable) == NameToFind)
{
Found.Add(Spawnable);
}
}
return Found;
}
TArray UMyEditorLibrary::FindPossessablesByName (UMovieSceneSequence* Sequence, FString NameToFind)
{
TArray Found;
TArray Possessables = GetPossessables(Sequence);
for (const FMovieSceneBindingProxy& Possessable : Possessables)
{
if (GetBindingName(Possessable) == NameToFind)
{
Found.Add(Possessable);
}
}
return Found;
}
void UMyEditorLibrary::ChangeNamedSpawnableClass (UMovieSceneSequence* Sequence, FString NameToFind, TSubclassOf NewClass)
{
// Get the MovieScene contained in this Sequence
UMovieScene* MovieScene = Sequence->GetMovieScene();
check(MovieScene);
ULevelSequenceEditorSubsystem* LevelSequenceEditorSubsystem = GEditor->GetEditorSubsystem();
// Iterate over the spawnables of this Sequence
TArray Spawnables = FindSpawnablesByName(Sequence, NameToFind);
for (const FMovieSceneBindingProxy& Spawnable : Spawnables)
{
DoChangeSpawnableClass(Sequence, Spawnable, NewClass);
}
}
void UMyEditorLibrary::DoChangeSpawnableClass(UMovieSceneSequence* Sequence, const FMovieSceneBindingProxy& Binding, TSubclassOf NewClass)
{
ILevelSequenceModule& LevelSequenceModule = FModuleManager::LoadModuleChecked(“LevelSequence”);
// Get the MovieScene contained in this Sequence
UMovieScene* MovieScene = Sequence->GetMovieScene();
check(MovieScene);
// Generate object spawners
TArray<TSharedRef> ObjectSpawners;
LevelSequenceModule.GenerateObjectSpawners(ObjectSpawners);
// Iterate over the spawners until we find one that works
for (TSharedRef Spawner : ObjectSpawners)
{
// Run the spawner
TValueOrError<FNewSpawnable, FText> Result = Spawner->CreateNewSpawnableType(*NewClass.Get(), *MovieScene, nullptr);
// Check if it worked
if (Result.IsValid())
{
UObject* ObjectTemplate = Result.GetValue().ObjectTemplate;
// Mark MovieScene dirty
MovieScene->Modify();
// Handle FMovieSceneSpawnable
if (FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(Binding.BindingID))
{
Spawnable->SetObjectTemplate(ObjectTemplate);
}
// Handle MovieSceneSpawnableActorBinding
else if (FMovieSceneBindingReferences* BindingReferences = Sequence->GetBindingReferences())
{
for (const FMovieSceneBindingReference& BindingReference : BindingReferences->GetAllReferences())
{
if (UMovieSceneSpawnableBindingBase* SpawnableBinding = (UMovieSceneSpawnableBindingBase*)(BindingReference.CustomBinding))
{
SpawnableBinding->SetObjectTemplate(ObjectTemplate);
}
}
}
}
}
}`If you are building the engine from source, you can also expose the sequencer extensions classes (such as UMovieSceneBindingExtensions) yourself. Simply open file “MovieSceneBindingExtensions.h”, for example, and add the macro “SEQUENCERSCRIPTING_API” on the class definition (between the “class” keyword and the name of the class). If you wish, I can ask someone from Epic if there are any plans to expose them on a future official release. I can tell you right away, though, that they are not currently exposed even on the latest commit of the main developmente branch, so even if there are plans to do so in the future, it might take a while until the change ends up in a release.
Oh, and about your question, yes, from the point-of-view of the UMovieScene class, UMovieSceneSpawnableActorBinding is indeed a Possessable, but one with a custom binding that creates a Spawnable to possess when evaluated. I also think this is kind of confusing, but apparently this change was meant to add more flexibility to spawnables, as per the comment above class UMovieSceneSpawnableBindingBase:
`/**
- The base class for custom spawnable bindings. A spawnable binding will spawn an object upon resolution or return a cached previously spawned object.
- UMovieSceneSpawnableActorBinding is the reimplementation of previous FMovieSceneSpawnable features and spawns an actor based on a saved template and actor class.
- Otherwise, projects are free to implement their own Spawnable bindings by overriding this class.
- In doing so, they could choose to just override GetSpawnObjectClass, PostSpawnObject, and PreDestroyObject for example to do custom post-spawn setup on a character mesh,
- or they could choose to fully override SpawnObject and DestroySpawnedObject and do their own custom logic for spawning completely.
*/`Best regards,
Vitor