Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "LevelSequence/FlowLevelSequenceActor.h"
#include "FlowLogChannels.h"
#include "LevelSequence/FlowLevelSequencePlayer.h"
#include "Net/UnrealNetwork.h"
#include "Runtime/Launch/Resources/Version.h"

#include "DefaultLevelSequenceInstanceData.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowLevelSequenceActor)
Expand All @@ -20,6 +20,7 @@ void AFlowLevelSequenceActor::GetLifetimeReplicatedProps(TArray<FLifetimePropert
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME(AFlowLevelSequenceActor, ReplicatedLevelSequenceAsset);
DOREPLIFETIME(AFlowLevelSequenceActor, BindingEntries);
}

void AFlowLevelSequenceActor::SetPlaybackSettings(FMovieSceneSequencePlaybackSettings NewPlaybackSettings)
Expand Down Expand Up @@ -51,4 +52,52 @@ void AFlowLevelSequenceActor::OnRep_ReplicatedLevelSequenceAsset()
}

InitializePlayer();

// Re-apply bindings that replicated before the sequence player was created
OnRep_BindingEntries();
}

void AFlowLevelSequenceActor::AddBinding(FName Tag, AActor* Actor)
{
if (!HasAuthority() || !IsValid(Actor) || Tag.IsNone())
{
return;
}

FFlowSequenceBindingEntry& Entry = BindingEntries.AddDefaulted_GetRef();
Entry.BindingTag = Tag;
Entry.BoundActor = Actor;

if (IsValid(GetSequencePlayer()))
{
SetBindingByTag(Tag, {Actor}, false);
}
else
{
UE_LOG(LogFlow, Warning, TEXT("AFlowLevelSequenceActor::AddBinding - sequence player not initialized for tag '%s'; binding queued for replication but not applied locally"), *Tag.ToString());
}
}

void AFlowLevelSequenceActor::ClearAllBindings()
{
if (!HasAuthority())
{
return;
}

BindingEntries.Empty();
ResetBindings();
}

void AFlowLevelSequenceActor::OnRep_BindingEntries()
{
ResetBindings();

for (const FFlowSequenceBindingEntry& Entry : BindingEntries)
{
if (IsValid(Entry.BoundActor) && !Entry.BindingTag.IsNone())
{
SetBindingByTag(Entry.BindingTag, {Entry.BoundActor.Get()}, false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye
AActor* TransformOriginActor,
const bool bReplicates,
const bool bAlwaysRelevant,
ALevelSequenceActor*& OutActor
TObjectPtr<AFlowLevelSequenceActor>& OutActor
)
{
if (LevelSequence == nullptr)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "LevelSequence/IFlowPlayLevelSequenceAddOnInterface.h"

#include "AddOns/FlowNodeAddOn.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(IFlowPlayLevelSequenceAddOnInterface)

bool IFlowPlayLevelSequenceAddOnInterface::ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate)
{
return IsValid(AddOnTemplate) && AddOnTemplate->Implements<UFlowPlayLevelSequenceAddOnInterface>();
}
37 changes: 34 additions & 3 deletions Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
#include "FlowAsset.h"
#include "FlowLogChannels.h"
#include "FlowSubsystem.h"
#include "AddOns/FlowNodeAddOn.h"
#include "LevelSequence/FlowLevelSequenceActor.h"
#include "LevelSequence/FlowLevelSequencePlayer.h"
#include "LevelSequence/IFlowPlayLevelSequenceAddOnInterface.h"

#if WITH_EDITOR
#include "MovieScene/MovieSceneFlowTrack.h"
#include "MovieScene/MovieSceneFlowTriggerSection.h"
#endif

#include "LevelSequence.h"
#include "LevelSequenceActor.h"
#include "VisualLogger/VisualLogger.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_PlayLevelSequence)
Expand Down Expand Up @@ -148,17 +150,29 @@ void UFlowNode_PlayLevelSequence::InitializeInstance()
{
Super::InitializeInstance();

SequenceActor = nullptr;

// Cache Play Rate set by user
CachedPlayRate = PlaybackSettings.PlayRate;
}

EFlowAddOnAcceptResult UFlowNode_PlayLevelSequence::AcceptFlowNodeAddOnChild_Implementation(
const UFlowNodeAddOn* AddOnTemplate,
const TArray<UFlowNodeAddOn*>& AdditionalAddOnsToAssumeAreChildren) const
{
if (IFlowPlayLevelSequenceAddOnInterface::ImplementsInterfaceSafe(AddOnTemplate))
{
return EFlowAddOnAcceptResult::TentativeAccept;
}

return Super::AcceptFlowNodeAddOnChild_Implementation(AddOnTemplate, AdditionalAddOnsToAssumeAreChildren);
}

void UFlowNode_PlayLevelSequence::CreatePlayer()
{
LoadedSequence = Sequence.LoadSynchronous();
if (LoadedSequence)
{
ALevelSequenceActor* SequenceActor;

AActor* OwningActor = TryGetRootFlowActorOwner();

// Apply AActor::CustomTimeDilation from owner of the Root Flow
Expand All @@ -178,6 +192,17 @@ void UFlowNode_PlayLevelSequence::CreatePlayer()
SequencePlayer->SetFlowEventReceiver(this);
}

// Notify add-ons so they can apply binding overrides before Play() is called
if (AFlowLevelSequenceActor* FlowSequenceActor = SequenceActor.Get())
{
ForEachAddOnForClass<UFlowPlayLevelSequenceAddOnInterface>([this, FlowSequenceActor, OwningActor](UFlowNodeAddOn& AddOn)
{
IFlowPlayLevelSequenceAddOnInterface* Interface = CastChecked<IFlowPlayLevelSequenceAddOnInterface>(&AddOn);
Interface->OnSequencePlayerCreated(*FlowSequenceActor, OwningActor);
return EFlowForEachAddOnFunctionReturnValue::Continue;
});
}

const FFrameRate FrameRate = LoadedSequence->GetMovieScene()->GetTickResolution();
const FFrameNumber PlaybackStartFrame = LoadedSequence->GetMovieScene()->GetPlaybackRange().GetLowerBoundValue();
StartTime = FQualifiedFrameTime(FFrameTime(PlaybackStartFrame, 0.0f), FrameRate).AsSeconds();
Expand Down Expand Up @@ -319,6 +344,12 @@ void UFlowNode_PlayLevelSequence::Cleanup()
SequencePlayer = nullptr;
}

if (IsValid(SequenceActor) && SequenceActor->HasAuthority())
{
SequenceActor->Destroy();
}
SequenceActor = nullptr;

LoadedSequence = nullptr;
StartTime = 0.0f;
ElapsedTime = 0.0f;
Expand Down
25 changes: 25 additions & 0 deletions Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@

class ULevelSequence;

/** Single actor binding override entry, replicated from server to clients. */
USTRUCT()
struct FFlowSequenceBindingEntry
{
GENERATED_BODY()

UPROPERTY()
FName BindingTag;

UPROPERTY()
TObjectPtr<AActor> BoundActor;
};

/**
* Custom ALevelSequenceActor is needed to override ULevelSequencePlayer class.
*/
Expand All @@ -24,7 +37,19 @@ class FLOW_API AFlowLevelSequenceActor : public ALevelSequenceActor
void SetPlaybackSettings(FMovieSceneSequencePlaybackSettings NewPlaybackSettings);
void SetReplicatedLevelSequenceAsset(ULevelSequence* Asset);

/** Server only. Adds a binding override that replicates to clients via OnRep_BindingEntries. */
void AddBinding(FName Tag, AActor* Actor);

/** Server only. Clears all binding overrides and replicates the cleared state to clients. */
void ClearAllBindings();

protected:
UFUNCTION()
void OnRep_ReplicatedLevelSequenceAsset();

UPROPERTY(ReplicatedUsing = OnRep_BindingEntries)
TArray<FFlowSequenceBindingEntry> BindingEntries;

UFUNCTION()
void OnRep_BindingEntries();
};
3 changes: 2 additions & 1 deletion Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "LevelSequencePlayer.h"
#include "FlowLevelSequencePlayer.generated.h"

class AFlowLevelSequenceActor;
class UFlowNode;

/**
Expand All @@ -29,7 +30,7 @@ class FLOW_API UFlowLevelSequencePlayer : public ULevelSequencePlayer
AActor* TransformOriginActor,
const bool bReplicates,
const bool bAlwaysRelevant,
ALevelSequenceActor*& OutActor);
TObjectPtr<AFlowLevelSequenceActor>& OutActor);

void SetFlowEventReceiver(UFlowNode* FlowNode) { FlowEventReceiver = FlowNode; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#pragma once

#include "UObject/Interface.h"

#include "IFlowPlayLevelSequenceAddOnInterface.generated.h"

class AActor;
class AFlowLevelSequenceActor;
class UFlowNodeAddOn;

/**
* Interface for add-ons that want to apply setup (e.g. actor binding overrides) to a
* level sequence actor immediately after the sequence player is created, before Play() is called.
*
* Attach to any flow node that spawns an AFlowLevelSequenceActor and supports this interface.
*/
UINTERFACE(MinimalAPI)
class UFlowPlayLevelSequenceAddOnInterface : public UInterface
{
GENERATED_BODY()
};

class FLOW_API IFlowPlayLevelSequenceAddOnInterface
{
GENERATED_BODY()

public:

static bool ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate);

/**
* Called after the sequence player is created, before Play() is invoked.
*
* @param SequenceActor The spawned AFlowLevelSequenceActor. Never null when called.
* @param FlowOwner The actor that owns the flow graph. May be null.
*/
virtual void OnSequencePlayerCreated(AFlowLevelSequenceActor& SequenceActor, AActor* FlowOwner) {}
};
8 changes: 8 additions & 0 deletions Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Nodes/FlowNode.h"
#include "FlowNode_PlayLevelSequence.generated.h"

class AFlowLevelSequenceActor;
class UFlowLevelSequencePlayer;

DECLARE_MULTICAST_DELEGATE(FFlowNodeLevelSequenceEvent);
Expand Down Expand Up @@ -75,6 +76,9 @@ class FLOW_API UFlowNode_PlayLevelSequence
UPROPERTY()
TObjectPtr<UFlowLevelSequencePlayer> SequencePlayer;

UPROPERTY()
TObjectPtr<AFlowLevelSequenceActor> SequenceActor;

/* Play Rate set by the user in PlaybackSettings. */
float CachedPlayRate;

Expand Down Expand Up @@ -106,6 +110,10 @@ class FLOW_API UFlowNode_PlayLevelSequence
virtual void FlushContent() override;
// --

// UFlowNodeBase
virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray<UFlowNodeAddOn*>& AdditionalAddOnsToAssumeAreChildren) const override;
// --

virtual void InitializeInstance() override;
void CreatePlayer();

Expand Down