「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」
今日,不知写点什么,左思右想,还是来篇UE4源码分析吧~(啃啃老本)
这次,我们要讲的是UE4中的Timeline,就是蓝图中的那个Timeline节点的C++部分
友情提示:阅读本文需要UE4C++基础
结构
首先,UE4C++使用Timeline一般都是用的FTimeline这个结构体和TimelineComponent这个组件。
然后呢,TimelineCompontent是用于外部控制的一个相当于给FTimeline封装了一层,我们一般项目里面实际用的话,应该是用的TimelineComponent,对于FTimeline应该用的还算是比较少吧....
关于FTimeline的结构部分可以参照代码:Engine\Source\Runtime\Engine\Classes\Components\TimelineComponent.h源码太长就不贴了。
其实主要的结构很简单,最外层是Timeline本体,里面存在多个Track,所有的Track共用同一个Timeline配置,每个Track又对应一条曲线,每根曲线又对应若干个关键帧。
可以看到Track的结构如下:
/** Struct that contains one entry for each vector interpolation performed by the timeline */
USTRUCT()
struct FTimelineFloatTrack
{
GENERATED_USTRUCT_BODY()
/** Float curve to be evaluated */
UPROPERTY()
class UCurveFloat* FloatCurve;
/** Function that the output from ValueCurve will be passed to */
UPROPERTY()
FOnTimelineFloat InterpFunc;// 动态单播委托
/** Name of track, usually set in Timeline Editor. Used by SetInterpFloatCurve function. */
UPROPERTY()
FName TrackName;
/** 反映在蓝图节点上的Float轨道对应的输出引脚名称 */
UPROPERTY()
FName FloatPropertyName;
/** 反映在蓝图节点上的Float轨道对应的输出引脚 */
UPROPERTY(transient)
UFloatProperty* FloatProperty;
/** Static version of FOnTimelineFloat, for use with non-UObjects */
FOnTimelineFloatStatic InterpFuncStatic;
FTimelineFloatTrack()
: FloatCurve(NULL)
, FloatPropertyName(NAME_None)
, FloatProperty(NULL)
{
}
};
大致流程
调用TimelineComponent::Play,开启Component自身的Tick计时器->TimelineComponent::TickComponent开始起作用->调用FTimeline::TickTimeline->实际调用FTimeline::SetPlaybackPosition来每帧执行Timeline绑定的事件。
关键函数
TimelineComponent::TickComponent
void UTimelineComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
SCOPE_CYCLE_COUNTER(STAT_TimelineCompTick);
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (bIgnoreTimeDilation)
{
AActor* const OwnerActor = GetOwner();
if (OwnerActor)
{
DeltaTime /= OwnerActor->GetActorTimeDilation();
}
else
{
// no Actor for some reason, use the world time dilation as fallback
UWorld* const W = GetWorld();
if (W)
{
DeltaTime /= W->GetWorldSettings()->GetEffectiveTimeDilation();
}
}
}
TheTimeline.TickTimeline(DeltaTime);
if (!IsNetSimulating())
{
// Do not deactivate if we are done, since bActive is a replicated property and we shouldn't have simulating
// clients touch replicated variables.
if (!TheTimeline.IsPlaying())
{
Deactivate();
}
}
}
其他代码太长就不贴了,可以自行翻阅。
由于FTimeline本身相比UE4其他东西来说,较为简单了,所以其实也没多少好讲的,主要内容其实都集中在SetPlaybackPosition函数上,只要看懂了这个函数,FTimeline就没什么难懂的了。
抛去SetPlaybackPosition中的无关部分,主体的逻辑其实就VecEntry.InterpFunc.ExecuteIfBound(Vec);这么一句(这个是Vector类型的轨道,其他类型同理)。即,在每次设置位置的时候把当前位置的值通知出去即可。
需要注意的是,FTimeline中的Event轨道和其他类型的轨道结构不同,他只有关键帧的点,而没有曲线,因此是单独处理的,但正是因为这样,省了不少的开销呢!
虽然UE4的Timeline部分因为结构简单导致了使用方便简单,却限制了其使用场景,如果我们不考虑节点UI部分,可以将其重构,使其支持在任意地方使用。当然,UE4的Timeline结构简单必然也是经过考虑的,例如Timeline的唯一性:比如,如何通知到所有位置的同一个Timeline?必然是用多播,那么多播情况下如何确定唯一性?如果有多个Timeline,如何来管理他们,如何来确定各自的唯一性?
在下一个系列,我将会尝试去制作一个第三方Timeline来尝试解决这些问题(挖坑)。