// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Channels/MovieSceneChannelHandle.h" #include "Channels/MovieSceneChannelTraits.h" #include "Channels/MovieSceneChannelProxy.h" #include "CoreTypes.h" #include "Generators/MovieSceneEasingCurves.h" #include "IMovieScenePlayer.h" #include "KeyParams.h" #include "MovieScene.h" #include "MovieSceneSection.h" #include "MovieSceneSequence.h" #include "Templates/EnableIf.h" #include "Templates/PointerIsConvertibleFromTo.h" #include "Tracks/MovieScenePropertyTrack.h" #include "MovieSceneTestDataBuilders.generated.h" /** * Simple type of sequence for use in automated tests. * * Bound objects are specified manually on the sequence and will be simply returned when * bindings are resolved. */ UCLASS(MinimalAPI) class UMovieSceneTestSequence : public UMovieSceneSequence { GENERATED_BODY() public: /** The movie scene */ UPROPERTY() TObjectPtr MovieScene; public: /** Initialize this test sequence */ MOVIESCENETRACKS_API void Initialize(); /** * Add an object binding to the sequnce * * @param InObject The object that will be returned when the binding is resolved * @return The ID of the new object binding */ MOVIESCENETRACKS_API FGuid AddObjectBinding(TObjectPtr InObject); public: /** UMovieSceneSequence interface */ virtual void BindPossessableObject(const FGuid& ObjectId, UObject& PossessedObject, UObject* Context) override {} virtual bool CanPossessObject(UObject& Object, UObject* InPlaybackContext) const override { return true; } MOVIESCENETRACKS_API virtual void LocateBoundObjects(const FGuid& ObjectId, UObject* Context, TArray>& OutObjects) const override; virtual UMovieScene* GetMovieScene() const override { return MovieScene; } virtual UObject* GetParentObject(UObject* Object) const override { return nullptr; } virtual void UnbindPossessableObjects(const FGuid& ObjectId) override {} virtual void UnbindObjects(const FGuid& ObjectId, const TArray& InObjects, UObject* Context) override {} virtual void UnbindInvalidObjects(const FGuid& ObjectId, UObject* Context) override {} private: UPROPERTY() TArray> BoundObjects; UPROPERTY() TArray BindingGuids; }; namespace UE::MovieScene::Test { template struct FSequenceTrackBuilder; template struct FSequenceSectionBuilder; /** * Utility class for building a test level sequence using a fluid interface API. */ struct FSequenceBuilder { TObjectPtr Sequence; FGuid CurrentBinding; FSequenceBuilder() { Sequence = NewObject(); Sequence->Initialize(); } FSequenceBuilder& AddObjectBinding(TObjectPtr InObject) { CurrentBinding = Sequence->AddObjectBinding(InObject); return *this; } FSequenceBuilder& AddObjectBinding(TObjectPtr InObject, FGuid& OutBindingID) { AddObjectBinding(InObject); OutBindingID = CurrentBinding; return *this; } template FSequenceTrackBuilder AddRootTrack() { UMovieScene* MovieScene = Sequence->GetMovieScene(); TrackClass* Track = MovieScene->AddTrack(); return FSequenceTrackBuilder(*this, Track); } template FSequenceTrackBuilder AddTrack() { checkf(CurrentBinding.IsValid(), TEXT("Specify an object binding first with AddObjectBinding")); UMovieScene* MovieScene = Sequence->GetMovieScene(); TrackClass* Track = MovieScene->AddTrack(CurrentBinding); return FSequenceTrackBuilder(*this, Track); } template FSequenceTrackBuilder AddPropertyTrack(FName InPropertyName) { checkf(CurrentBinding.IsValid(), TEXT("Specify an object binding first with AddObjectBinding")); UMovieScene* MovieScene = Sequence->GetMovieScene(); TrackClass* Track = MovieScene->AddTrack(CurrentBinding); Track->SetPropertyNameAndPath(InPropertyName, InPropertyName.ToString()); return FSequenceTrackBuilder(*this, Track); } }; /** * Utility class for building a track on a test level sequence using a fluid interface API. */ template struct FSequenceTrackBuilder { FSequenceBuilder& Parent; TObjectPtr Track; FSequenceTrackBuilder(FSequenceBuilder& InParent, TrackClass* InTrack) : Parent(InParent), Track(InTrack) {} FSequenceTrackBuilder& Assign(TrackClass*& OutTrack) { OutTrack = Track; return *this; } template FSequenceBuilder& Do(Func&& Callback) { Callback(Track); return *this; } FSequenceSectionBuilder AddSection(FFrameNumber InStart, FFrameNumber InEnd, int32 InRowIndex = INDEX_NONE) { UMovieSceneSection* NewSection = Track->CreateNewSection(); NewSection->SetRange(TRange(InStart, InEnd)); if (InRowIndex != INDEX_NONE) { NewSection->SetRowIndex(InRowIndex); } Track->AddSection(*NewSection); return FSequenceSectionBuilder(*this, NewSection); } FSequenceBuilder& Pop() { return Parent; } }; /** * Utility class for building a track section on a test level sequence using a flui interface API. */ template struct FSequenceSectionBuilder { FSequenceTrackBuilder& Parent; TObjectPtr Section; FSequenceSectionBuilder(FSequenceTrackBuilder& InParent, UMovieSceneSection* InSection) : Parent(InParent), Section(InSection) {} FSequenceSectionBuilder& Assign(UMovieSceneSection*& OutSection) { OutSection = Section; return *this; } template FSequenceSectionBuilder& Assign(SectionClass*& OutSection) { OutSection = Cast(Section); return *this; } FSequenceSectionBuilder& SetBlendType(EMovieSceneBlendType InBlendType) { Section->SetBlendType(InBlendType); return *this; } FSequenceSectionBuilder& SetEaseIn(int32 InDurationTicks, EMovieSceneBuiltInEasing InEasingType = EMovieSceneBuiltInEasing::Linear) { FMovieSceneEasingSettings& Easing = Section->Easing; Easing.bManualEaseIn = true; Easing.ManualEaseInDuration = InDurationTicks; if (UMovieSceneBuiltInEasingFunction* Function = Cast(Easing.EaseIn.GetObject())) { Function->Type = InEasingType; } return *this; } FSequenceSectionBuilder& SetEaseOut(int32 InDurationTicks, EMovieSceneBuiltInEasing InEasingType = EMovieSceneBuiltInEasing::Linear) { FMovieSceneEasingSettings& Easing = Section->Easing; Easing.bManualEaseOut = true; Easing.ManualEaseOutDuration = InDurationTicks; if (UMovieSceneBuiltInEasingFunction* Function = Cast(Easing.EaseOut.GetObject())) { Function->Type = InEasingType; } return *this; } template FSequenceSectionBuilder& AddKey( int32 InChannelIndex, FFrameNumber InTime, ValueType InValue, EMovieSceneKeyInterpolation Interpolation = EMovieSceneKeyInterpolation::Auto) { FMovieSceneChannelProxy& ChannelProxy = Section->GetChannelProxy(); ChannelType* Channel = ChannelProxy.GetChannel(InChannelIndex); return AddKeys(Channel, TArray({ InTime }), TArray({ InValue }), Interpolation); } template FSequenceSectionBuilder& AddKeys( int32 InChannelIndex, const TArray& InTimes, const TArray& InValues, EMovieSceneKeyInterpolation Interpolation = EMovieSceneKeyInterpolation::Auto) { FMovieSceneChannelProxy& ChannelProxy = Section->GetChannelProxy(); ChannelType* Channel = ChannelProxy.GetChannel(InChannelIndex); return AddKeys(Channel, InTimes, InValues, Interpolation); } #if WITH_EDITOR template FSequenceSectionBuilder& AddKeys( FName InChannelName, const TArray& InTimes, const TArray& InValues, EMovieSceneKeyInterpolation Interpolation = EMovieSceneKeyInterpolation::Auto) { FMovieSceneChannelProxy& ChannelProxy = Section->GetChannelProxy(); TMovieSceneChannelHandle ChannelHandle = ChannelProxy.GetChannelByName(InChannelName); return AddKeys(ChannelHandle.Get(), InTimes, InValues, Interpolation); } #endif template FSequenceSectionBuilder& AddKeys( ChannelType* InChannel, const TArray& InTimes, const TArray& InValues, EMovieSceneKeyInterpolation Interpolation = EMovieSceneKeyInterpolation::Auto) { ensure(InTimes.Num() == InValues.Num()); int32 Num = FMath::Min(InTimes.Num(), InValues.Num()); for (int32 Index = 0; Index < Num; ++Index) { AddKeyToChannel(InChannel, InTimes[Index], InValues[Index], Interpolation); } return *this; } template FSequenceSectionBuilder& Do(Func&& Callback) { Callback(Section); return *this; } template FSequenceSectionBuilder& Do(Func&& Callback) { Callback(Cast(Section)); return *this; } FSequenceTrackBuilder& Pop() { return Parent; } }; } // namespace UE::MovieScene::Test