C++ ULevelSequence creation from scratch with event track

Hi Devs,

I want to create a ULevelSequence from scratch in order to create automatically dialogs sequencer based on data: text + duration + other info.
This system will allow to not create manually thousand of ULevelSequence with manual tick positioning.

I start to implement a very simple system which:

  • Create LevelSequence into Actor
  • Create Track/Section
  • Configure sequence duration
  • Add an event tick at the end with call of event code.

Currently the two first points look work, if I check into editor, but I can’t change duration of track/movie or add tick.
I create this code based on many copy/past and samples because c++ doc is not very good about this module.

Questions

  • Someone have an idea why it doesn’t work ? Which methods to use ?
  • What do you think about this solution to make a generic dialog system ?

Thanks !!

The code : H

UCLASS(Blueprintable)
class AMyDialogSequencer : public ALevelSequenceActor
{
    GENERATED_BODY()

public:
    AMyDialogSequencer(const FObjectInitializer& Init);

    UFUNCTION(BlueprintCallable)
    void Initialize();

    UFUNCTION(BlueprintCallable)
    void OnFinishDialog();

protected:   
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    ULevelSequence* _sequence;
};

With cpp

AMyDialogSequencer::AMyDialogSequencer(const FObjectInitializer& Init) :
ALevelSequenceActor(Init)
{
    SetActorLabel(TEXT("Dialog Sequencer"));
}

void AMyDialogSequencer::Initialize()
{
    // Static data time
    int32 frameStart = 0;
    int32 frameEnd   = 120;

    // Build LevelSequence
    _sequence = NewObject<ULevelSequence>();
    _sequence->Initialize();

    // Configure LevelSequence
    auto scene = _sequence->GetMovieScene();
    scene->SetPlaybackRange(TRange<FFrameNumber>::Inclusive(frameStart, frameEnd));
    scene->SetWorkingRange(frameStart, frameEnd);
    scene->SetViewRange(frameStart, frameEnd);

    // Build track
    auto* track = _sequence->GetMovieScene()->AddMasterTrack<UMovieSceneEventTrack>();
    check(track);
    track->SetDisplayName(FText::FromName(TEXT("Events")));

    // Build Section
    UMovieSceneEventTriggerSection* section = Cast<UMovieSceneEventTriggerSection>(track->CreateNewSection());
    check(section);
    section->SetRange(TRange<FFrameNumber>::Inclusive(frameStart, frameEnd));

    // Add Section to main track
    track->AddSection(*section);

    // Modify section
    if(section->TryModify(true))
    {
        auto& channelProxy = section->GetChannelProxy();
        auto channel = channelProxy.GetChannel<FMovieSceneEventChannel>(0);
        check(channel);

        // Create key to call OnFinishDialog
        TMovieSceneChannelData<FMovieSceneEvent> eventData = channel->GetData();

        FMovieSceneEventPayloadVariable Payload;
        Payload.Value = "OnFinishDialog";

        FMovieSceneEvent sceneEvent;
        sceneEvent.PayloadVariables.Add(TEXT("EventTick"), Payload);

        FFrameNumber Frame;
        Frame.Value = frameEnd;
        Frame--;
        eventData.AddKey(Frame, sceneEvent);
    }

    SetSequence(_sequence);
    InitializePlayer();

    FMovieSceneSequencePlaybackSettings settings;
    settings.StartTime = 0.0f;
    FLevelSequenceCameraSettings InCameraSettings;

    check(GetSequencePlayer());
    GetSequencePlayer()->Initialize(_sequence, GetWorld()->GetCurrentLevel(), settings, InCameraSettings);
}

void AMyDialogSequencer::OnFinishDialog()
{
    UE_LOG(LogTemp, Verbose, TEXT("Finish Dialog"));
}

Creation of actor

FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AMyDialogSequencer* dialogSequencer = GetWorld()->SpawnActor<AMyDialogSequencer>(AMyDialogSequencer::StaticClass(), SpawnInfo);
    dialogSequencer->Initialize();