此图是UE 5.1 Sequence 的 Section 与对应的 Channel 记录, 也许后续会对此图进行更改,但目前5.1是这么个结构。
UE 在创建 UMovieSceneSection 子类的时候,并没有将 UMovieSceneEventSectionBase 与 UMovieSceneEventSection 置于同一条继承下
UMovieSceneEventSection 中 Channel 使用的 FEventPayload 可以很方便的利用内置的函数修改参数。
// 定义Struct类型,此处可以替换成自己的结构体。
FStruct Struct;
// 定义Payload。
FEventPayload Payload;
// 此处可以填入执行事件的名字,但我并未对此进行测试。
Payload.EventName = FName(SequencerQuickBindingResult.EventEndpoint.GetFullName());
// 将 Payload 的参数结构重新绑定至新的结构体上。
Payload.Parameters.Reassign(Struct.StaticStruct());
// 将对应结构体的实例化对象以 uint8* 的形式调用函数将其覆写,上面必须执行,不然结构不一致会出现乱码。
Payload.Parameters.OverwriteWith(reinterpret_cast<uint8*>(&Struct));
// 最后直接在 UMovieSceneSection 轨道下,对 FMovieSceneEventSectionData 进行 FEventPayload 操作
// 与图中结构是一一对应的。
EventSection->GetChannelProxy().GetChannels<FMovieSceneEventSectionData>()[0]
->GetData().UpdateOrAddKey(CrossRange.Time.FrameNumber, Payload);
但在此处,我将着重描述 UMovieSceneEventSectionBase 的使用操作。
UMovieSceneEventSectionBase 被两个常用子类所继承。
其一是 UMovieSceneEventTriggerSection
另一个是 UMovieSceneEventRepeaterSection
这两个同时对应了 Sequence 中的 Trigger 和 Repeater 两个轨道。
如图所示,如果想在 UMovieSceneEventSectionBase 的 Channel 中添加 Data。
那么需要使用 FMovieSceneEvent 类才可以。
但是在 UE 的设计中 FMovieSceneEvent 的参数是由成员变量 PayloadVariables 记录的
而此变量的结构如下:
TMap<FName, FMovieSceneEventPayloadVariable> PayloadVariables;
可以看到,FName代表了参数的名字,而右侧 FMovieSceneEventPayloadVariable
所储存的就是参数的类型与数据了。
USTRUCT(BlueprintType)
struct FMovieSceneEventPayloadVariable
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category="Sequencer|Event")
FString Value;
};
而储存对应类型与数据的类里,居然只有一个FString变量?!怎会如此。
但是不要慌张,我们可以先来看一些源码的架构。
UE 在构建右侧面板时(也就是说右键关键帧所弹出的面板)时,会通过 MovieSceneEventCustomiyation.cpp
去执行这些。
const FMovieSceneEventPayloadVariable* PayloadVariable =
EntryPoint->PayloadVariables.Find(Field->GetFName());
if (PayloadVariable)
{
AllValidNames.Add(Field->GetFName());
// We have an override for this variable
const bool bImportSuccess = FBlueprintEditorUtils::PropertyValueFromString(
Field, PayloadVariable->Value, StructData->GetStructMemory());
if (!bImportSuccess)
{
// @todo: error
}
}
可以清晰地看到,在构建面板时这里通过了 FBlueprintEditorUtils::PropertyValueFromString 函数
成功将 FMovieSceneEventPayloadVariable 中唯一的 FString 解析为了数据可以写入参数。
如此一来只需要照猫画虎:
FString StructValue;
const uint8* Container = reinterpret_cast<const uint8*>(this);
// 需要在对应的 Actor 中, 拥有打了 UPROPERTY 宏的 Struct。
const FProperty* Property = FindFProperty<FStructProperty>(
AActor::StaticClass(),
GET_MEMBER_NAME_CHECKED(AActor, Struct));
FBlueprintEditorUtils::PropertyValueToString(Property, Container, StructValue);
FMovieSceneEventPayloadVariable MovieSceneEventPayloadVariable;
MovieSceneEventPayloadVariable.Value = StructValue;
MovieSceneEvent.PayloadVariables[FName("Struct")] = MovieSceneEventPayloadVariable;
MovieSceneSection->GetChannelProxy().GetChannels<FMovieSceneEventChannel>()[0]
->GetData().UpdateOrAddKey(Time, MovieSceneEvent);
直接利用 FBlueprintEditorUtils::PropertyValueToString(); 将结构体变成 FString。
这样就可以作为参数传入了。
题外话捏:
如果想自动在 Sequence 的 Director 中添加事件+函数绑定。
需要使用
FSequencerQuickBindingResult SequencerQuickBindingResult =
USequencerToolsFunctionLibrary::CreateQuickBinding(
LevelSequence, this, "this中拥有的函数名", true);
这样就在 Director 中创建绑定了。
FMovieSceneEvent不能直接创建,需要从上面的函数中得到 FSequencerQuickBindingResult
然后直接使用
FMovieSceneEvent MovieSceneEvent = USequencerToolsFunctionLibrary::CreateEvent(ModifySequence, EventSection, SequencerQuickBindingResult, SequencerQuickBindingResult.PayloadNames);
否则无法给事件对应的绑定。