Hey everyone,
I recently created a custom track and wanted to give this plugin a try. I quickly ran into the same issue as you guys. How to get the current time, or how to fetch the bound object using a FMovieSceneObjectBindingID
inside a track.
Let’s talk about the different solutions that have been shared…
@TalesOfTorah, I assume you’re working without a C++ project and only using Blueprints. Unfortunately, there’s no accessor exposed in Blueprint to retrieve that kind of data. But if you have someone who can help write a tiny plugin, it only takes a couple lines of C++ to get the job done.
As for the method proposed by @Kurotox or the slightly more efficient variant by @user_4d9b0a58e88575d4b40f7e7f518ed36a0f7de0e6d464be8ce8a73c, which involves grabbing the LevelSequencePlayer
, I’m not a big fan of that either. As you’ve pointed out, it doesn’t work well in the editor. But more importantly, in my case, we often use sub-level sequences, and the player only gives you the global time of the root sequence… not the local time inside a nested sub-sequence.
You could theoretically calculate the offset yourself, but that would involve analyzing the root level sequence and walking the sequence hierarchy… It’s possible, but it gets complicated and definitely requires more C++ work.
@piggest_pig, I’m also not too keen on your approach aside from the problems you already mentioned, things like slowing down the sequence or jumping around in time can break the whole setup.
My C++ solution
I decided to dig into the C++ source to see how things are structured. You can tell Epic intended for this kind of data to be accessible, but nothing is exposed cleanly for Blueprint.
If you look at the structure FSequencerTrackInstanceInput
returned by the node GetInput
and GetInputs
, you’ll see we only have the USequencerSectionBP Section
exposed to Blueprint. But there’s also a public FMovieSceneContext Context
field, which contains the current time of the track being played.
A one-liner C++ function solves the problem for the blueprint:
float UExtendedAnimationLibrary::GetTrackInstanceCurrentTimeAsSeconds(const FSequencerTrackInstanceInput& Track)
{
return Track.Context.GetTime() / Track.Context.GetFrameRate();
}
Getting the Bound Object
The second issue was how to retrieve a Bound Object using a FMovieSceneObjectBindingID
. For this, you do need to go through the linker similar to what @user_4d9b0a58e88575d4b40f7e7f518ed36a0f7de0e6d464be8ce8a73c suggested. Here’s how I handle it:
UObject* UExtendedAnimationLibrary::GetBoundObject(UMovieSceneTrackInstance* TrackInstance, const FMovieSceneObjectBindingID& ObjectBinding)
{
const TArrayView<const FMovieSceneTrackInstanceInput> MovieSceneTrackInstanceInputs = TrackInstance->GetInputs();
if (MovieSceneTrackInstanceInputs.IsEmpty())
{
return nullptr;
}
UMovieSceneEntitySystemLinker* Linker = TrackInstance->GetLinker();
if (Linker == nullptr)
{
return nullptr;
}
using namespace UE::MovieScene;
const FInstanceRegistry* InstanceRegistry = Linker->GetInstanceRegistry();
if (InstanceRegistry == nullptr)
{
return nullptr;
}
const FSequenceInstance& SequenceInstance = InstanceRegistry->GetInstance(MovieSceneTrackInstanceInputs[0].InstanceHandle);
const TArrayView<TWeakObjectPtr<UObject>> BoundedObjects = ObjectBinding.ResolveBoundObjects(SequenceInstance.GetSequenceID(), SequenceInstance.GetSharedPlaybackState());
if (BoundedObjects.IsEmpty())
{
return nullptr;
}
return BoundedObjects[0].Get();
}
Here’s how I use both functions directly in Blueprint first for current time, second for bound object:
Final thoughts
The best solution would be for Epic to show this plugin a bit of love and expose those missing elements officially. Otherwise, a small extension plugin that adds a few Blueprint-friendly quality-of-life nodes would go a long way.
Hope this helps someone out there!