Current Frame of Custom Sequence Track?

Hey,

I have a custom sequencertrackinstance blueprint as per Customizable Sequencer Track in Unreal Engine | Unreal Engine 5.0 Documentation.

How do I tell how far into the section it is?

I can get the start frame and end frame, but what about the current frame? How do I do this? I need to know the percent through the section, so I figure I can get the length of the section from the difference between the start and end, and divide that by the current frame - the start frame (writing fast, ignore any errors there if you spot them, not the point).

Ideally, I’d also like to know how much time has passed as well. If I can get the framerate of the sequence as well as the current frame, I can nab that.

I’m making cartoons and I want to have certain custom stuff scrubbable.

Thanks so much … this is blocking me right now. I’m transitioning here from Unity where I knew how to do all of this, and this seems so simple, but Im blocked :/.

1 Like

Okay, it seems like I can get the info I need from the sequence director. Is there any way to get access to that from the custom sequence track?

If not, the only thing I can think of is to write this stuff to the gamestate every frame using a repeating event, and then have my track which belongs to an object query the gamestate.

Which is really, really dumb. If this isn’t working yet, where do I get in touch with UE requesting this feature, since it basically destroys the ability to use this correctly if I cant get access to this data in the custom track. Something like this makes me question my switching from Unity to UE5 for creating films … since I need this to “just work”, and not have to jump through hoops for simple things like this :/.

1 Like

No responses yet?

I managed to get it working in the editor by storing the sequence directory in the gamestate, and having the custom track query the gamestate through the actor its attached to … my characters lip sync properly based on that, since I can get the current frame and the time passed that way.

However, when I use MovieRenderQueue, the characters mouth stops moving. My eyes blink so I know the morphs are working fine, so it must be this system breaks.

Definitely would love the right way to do this …

1 Like

Heya o/

So for these custom tracks since you’re using the Customizable Sequencer Tracks I should be able to help a bit.

In the OnInputAdded event you get an object reference to the Section this event is being triggered on (of type UMovieSceneSection).
If you get the Outer object of this, you can get a reference to the Track (UMovieSceneTrack).
The Outer object of the Track is the UMovieScene.
The Outer object of the MovieScene is the ULevelSequence.

Unfortunately there’s no great way I know of to get the actual LevelSequenceActor.
But you could do a Get All Actors of Class and then for each LevelSequenceActor check if the sequence they have assigned is the same as the LevelSequence of your Section.
If it is, you know this is your LevelSequenceActor (as an extra check you may want to see if the LevelSequenceActor is actually playing, otherwise you might get issues if there are multiple LevelSequenceActors in your world that are all set to play the same sequence).

Now that you have a reference to your LevelSequenceActor you can get its LevelSequencePlayer and get the current frame from there.

Bit roundabout but this has worked for me. Let me know if there are better ways to do this!

I found a way to find UMovieSceneSequencePlayer from the UMovieSceneEntitySystemLinker.
Only works with Game/PIE.

void UCustomTrackInstance::OnInputAdded(const FMovieSceneTrackInstanceInput& InInput)
{
	using namespace UE::MovieScene;

	FInstanceRegistry* InstanceRegistry = GetLinker()->GetInstanceRegistry();

	auto& SequenceInstance = InstanceRegistry->GetInstance(InInput.InstanceHandle);

	auto Player = SequenceInstance.GetPlayer();
	if (!Player)
	{
		return;
	}

	auto SequencePlayer = Cast<UMovieSceneSequencePlayer>(Player->AsUObject());
	if (!SequencePlayer)
	{
		return;
	}

	SequencePlayer->GetCurrentTime();
}

Hi there, I find a simple solution in blueprint. Since we can get current game time by “get time seconds” node, it’s possible to calculated the delta time.

  1. In your custom section, add a float variable as initial time.
  2. OnInputAdded - save current game time to the variable.
  3. OnUpdate - get the new game time, loop through each section and minus its initial time, then you will get the delta time.

Works with game and editor, but may have some problems with time warp or reverse play.

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!

1 Like