Download

Can you pass multiple timeline float outputs to a function as different arguments to that function?

I am working on converting a pure Blueprint project over to C++ just for learning purposes and gaining engine familiarity, and have run up against something I can’t easily solve.

The blueprint I’m stuck on uses a timeline in the following manner:


This timeline is called as part of another function in the Event Graph of the blueprint.

I’ve done a basic example of a timeline in C++ before, but when attempting this one I quickly realized that there seems to be no apparent way to bind one of the output tracks of the timeline to a specific argument of a multiple-argument function. Basically, there are three curves that all return different values, and the output from each curve needs to be fed to CalculateTransform simultaneously but as a different argument as the timeline runs. Exactly how the image shows.

But every example I’ve found online uses the following syntax:



FOnTimelineFloat curveRightUpdate;
curveRightUpdate.BindUFunction(this, FName("CalculateTransform"));


And the problem with this is that BindUFunction takes no arguments other than a function name.

I have tried a few different things, but none of them seem to work or be the right way to approach this. I can give more details if anyone replies and has questions, but for now for the sake of brevity: Does anyone know how you’d recreate what’s in the image in code? Or at least know how to point me in the right direction, or put me in contact with someone who might know?

Here’s what I’ve figured out thus far. It is impossible with the current API to do what I described in my first post exactly as Blueprint does it. But after scouring Timeline.cpp for a while, I think there is a possible path forward.

There is this function in UTimelineComponent:


 /** Set the delegate to call after each timeline tick */
ENGINE_API void SetTimelinePostUpdateFunc(FOnTimelineEvent NewTimelinePostUpdateFunc);
 

While I cannot bind the individual float track outputs to a function argument, I may still be able to use this to call a function on every tick of the timeline, and then assuming I can extract the values at that point in playback, I can set them aside as variables and then pass them to the CalculateTransform() function.

So I created a timeline event as follows and bound it to my new function:



FOnTimelineEvent timelineProgress;
timelineProgress.BindUFunction(this, FName("TimelineTickValues"));
returnTimeline->SetTimelinePostUpdateFunc(timelineProgress);


This function will then make use of the GetAllCurves function in TimelineComponent, which seems to be the only way you can get access to the different curves/tracks.



UTimelineComponent::GetAllCurves(TSet<class UCurveBase*>& InOutCurves)


But then look at that TSet type… right when I saw it I knew things weren’t going to be easy (like they ever were to begin with on this coding adventure).

My “tick” function is as follows:



void ALeviathanAxe::TimelineTickValues()
{ returnTimeline->GetAllCurves(timelineCurves);
UCurveFloat* mainProgress = Cast<UCurveFloat>(timelineCurves.Find(mainProgressCurve));
UCurveFloat* curveRight = Cast<UCurveFloat>(timelineCurves.Find(returnRightCurve));
UCurveFloat* rotation = Cast<UCurveFloat>(timelineCurves.Find(returnRotationCurve));

CalculateTransform
(
  [INDENT=2]mainProgress->GetFloatValue(returnTimeline->GetPlaybackPosition()),
curveRight->GetFloatValue(returnTimeline->GetPlaybackPosition()),
rotation->GetFloatValue(returnTimeline->GetPlaybackPosition())[/INDENT]
  );
 
 }


And here’s where I’m stuck right now. In theory it seems to make sense. As long as the timeline calls this function every tick, and I can get the data flowing correctly, it seems like it SHOULD work. But as I suspected, the lines where I’m trying to cast from a UCurveBase* to a UCurveFloat* are incorrect and that’s what I have yet to figure out. I’ve tried changing things a bit, like making it cast to a UCurveFloat*, but had no luck.

Output log error:



C:\Program Files\Epic Games\UE_4.25\Engine\Source\Runtime\CoreUObject\Public\Templates/Casts.h(154): note: could be 'To *TCastImpl<From,To,ECastType::UObjectToUObject>:DoCast(FField *)'
with
 To=UCurveFloat,
From=UCurveBase *
 
 ]


Final update with resolution, just in case it can be useful for someone stumbling onto this in the future.

As seems to be common with experimentation like this, I made things more complicated than they needed to be, but my theory was sound.

Short version: Using the SetTimelinePostUpdateFunc() is the way to accomplish outputting multiple float tracks simultaneously into a function with multiple arguments, just like the blueprint image I posted was doing.

The key is that, unlike the BP where it can just pull a pin directly from a float track, you have to bind a function that will be called on every tick of the timeline, and then handle linking up the float data to the function there manually. In this case, I made a function called TimelineTickValues() and bound this to SetTimelinePostUpdateFunc(). In TimelineTickValues, I was then able to extract the three floats I needed from the separate curves using this kind of syntax:



MainProgressCurve->GetFloatValue(ReturnTimeline->GetPlaybackPosition())


Thus, as the playback position moved forward, I could extract the float values from the three separate curves and pass them simultaneously into the CalculateTransform function, successfully mimicking the behavior seen in the blueprint nodes.

No need to get all complicated with GetAllCurves and trying to cast UCurveBases into UCurveFloats and all that mess.