Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timers and Delegates #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
95 changes: 84 additions & 11 deletions Source/SimpleActions/Private/SimpleAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ UWorld* USimpleAction::GetWorld() const
return nullptr;
}

FString USimpleAction::GetActionUID()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Store as FGuid

{

if(ActionUID.IsEmpty())
{
ActionUID = TEXT("" + FGuid::NewGuid().ToString());
}
return ActionUID;

}

#if WITH_EDITOR
void USimpleAction::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Expand Down Expand Up @@ -86,28 +97,31 @@ void USimpleAction::StartAction(UPARAM(DisplayName = "ActionActor") AActor* NewA
{
TRACE_CPUPROFILER_EVENT_SCOPE(USimpleAction::StartAction);

bool bFailed = false;
UWorld* World = GetWorld();
if (!World)
{
// Only run in valid worlds.
UE_LOG(LogSimpleAction, Warning, TEXT("StartAction: Failed. Action '%s' world not valid."), *GetName());
return;
bFailed = true;
}

if (!bAllowInEditorPreview && World->IsPreviewWorld())
if (!bFailed && !bAllowInEditorPreview && World->IsPreviewWorld())
{
// This Action isn't allowed to run in editor worlds.
UE_LOG(LogSimpleAction, Warning, TEXT("StartAction: Failed. Action '%s' not allowed in Editor Preview World: %s."), *GetName(), LexToString(GetWorld()->WorldType));
return;
bFailed = true;
}
if (!bAllowInLevelEditor && World->WorldType == EWorldType::Type::Editor)
if (!bFailed && !bAllowInLevelEditor && World->WorldType == EWorldType::Type::Editor)
{
// This Action isn't allowed to run in editor worlds.
UE_LOG(LogSimpleAction, Warning, TEXT("StartAction: Failed. Action '%s' not allowed in Editor World: %s."), *GetName(), LexToString(GetWorld()->WorldType));
return;
bFailed = true;
}


if(bFailed) {
OnActionFailed.Broadcast(this);
}

if (bActive)
{
Expand Down Expand Up @@ -135,17 +149,53 @@ void USimpleAction::StartAction(UPARAM(DisplayName = "ActionActor") AActor* NewA
}

UE_LOG(LogSimpleAction, Verbose, TEXT("StartAction: Starting. Action '%s'."), *GetName());
ActionStart();

OnActionStarted.Broadcast(this);

if(DurationOverride > 0.f)
{
const FTimerDelegate DurationOverrideDelegate = FTimerDelegate::CreateUObject(this, &USimpleAction::FinishAction, true);
GetWorld()->GetTimerManager().SetTimer(Timer_DurationOverride, DurationOverrideDelegate, DurationOverride + StartDelay, false);
}

if(StartDelay <= 0)
{
ActionStart();
} else {
const FTimerDelegate StartDelayDelegate = FTimerDelegate::CreateUObject(this, &USimpleAction::ActionStart);
GetWorld()->GetTimerManager().SetTimer(Timer_StartDelay, StartDelayDelegate, StartDelay, false);
}
}

void USimpleAction::StartAction_Bound(AActor* NewActionActor, AActor* NewInstigator, FOnActionStartedDelegate Started, FOnActionCancelDelegate Interrupted, FOnActionEndedDelegate Ended, FOnActionCompleteDelegate Complete, FOnActionFailedDelegate Failed) {

OnActionStarted.AddUnique(Started);
OnActionCancel.AddUnique(Interrupted);
OnActionEnded.AddUnique(Ended);
OnActionComplete.AddUnique(Complete);
OnActionFailed.AddUnique(Failed);

StartAction(NewActionActor, NewInstigator);
}

void USimpleAction::FinishAction(bool bSuccess)
{

// Only prevents the EndAction if there exists a DurationOverride timer active.
if(DurationOverride > 0.f) {
const FTimerManager& TimerManager = GetWorld()->GetTimerManager();
if (TimerManager.GetTimerRemaining(Timer_DurationOverride) > 0.f) {
return;
}
}

EndAction(bSuccess);
}

void USimpleAction::CancelAction()
{

OnActionCancel.Broadcast(this);
EndAction(false);
}

Expand All @@ -160,20 +210,43 @@ void USimpleAction::EndAction(bool bSuccess)
return;
}

// Kill all timers if the action is ending
if (StartDelay > 0.f) {
GetWorld()->GetTimerManager().ClearTimer(Timer_StartDelay);
}

if (DurationOverride > 0.f) {
GetWorld()->GetTimerManager().ClearTimer(Timer_DurationOverride);
}

bActive = false;

SetTickEnabled(false);
UE_LOG(LogSimpleAction, Verbose, TEXT("EndAction: Stopping. Action '%s'. Success: %s."), *GetName(), bSuccess ? TEXT("True") : TEXT("False"));

ActionStop(bSuccess);
if(bStopWhenActiveStarted)
{
ActionStop(bSuccess);
}

if(bSuccess)
{
OnActionComplete.Broadcast(this);
}
else
{
OnActionFailed.Broadcast(this);
}

OnActionEnded.Broadcast(this, bSuccess);

// These are left set until now incase the OnActionEnded broadcast wanted to have access to them.
// Final cleanup
ActionReset();

// These are left set until now in case the OnActionEnded Broadcast or ActionReset wanted to have access to them.
ActingActor = nullptr;
ActionInstigator = nullptr;

// Final cleanup
ActionReset();
}

void USimpleAction::SetTickEnabled(bool bTickEnabled)
Expand Down
74 changes: 71 additions & 3 deletions Source/SimpleActions/Public/SimpleAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,42 @@


DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActionStartedSignature, USimpleAction*, SimpleAction);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActionCancelSignature, USimpleAction*, SimpleAction);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnActionEndedSignature, USimpleAction*, SimpleAction, bool, Success);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActionCompleteSignature, USimpleAction*, SimpleAction);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActionFailedSignature, USimpleAction*, SimpleAction);

DECLARE_DYNAMIC_DELEGATE_OneParam(FOnActionStartedDelegate, USimpleAction*, SimpleAction);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnActionCancelDelegate, USimpleAction*, SimpleAction);
DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnActionEndedDelegate, USimpleAction*, SimpleAction, bool, Success);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnActionCompleteDelegate, USimpleAction*, SimpleAction);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnActionFailedDelegate, USimpleAction*, SimpleAction);

DECLARE_LOG_CATEGORY_EXTERN(LogSimpleAction, Log, All);

/**
* An action which can be performed by an actor.
* CollapseCategories,
*/
UCLASS(Blueprintable, DefaultToInstanced, EditInlineNew, Abstract, CollapseCategories, AutoExpandCategories = "Default,Simple Action", meta = (DisplayName = "Simple Action", PrioritizeCategories = "Simple Action"))
BenVlodgi marked this conversation as resolved.
Show resolved Hide resolved
class SIMPLEACTIONS_API USimpleAction : public UObject, public FTickableGameObject
{
GENERATED_BODY()


private:
norab7 marked this conversation as resolved.
Show resolved Hide resolved
UPROPERTY()
FString ActionUID;

public:
// When true: If this action is called to start while it is already active, it will start again.
// When false: Additional calls to start while the action is active will be ignored.
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Simple Action")
bool bAllowDoubleStart = true;

// When true: If the actions is called to start while already running it will call ActionStop.
norab7 marked this conversation as resolved.
Show resolved Hide resolved
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Simple Action")
bool bStopWhenActiveStarted = true;

// True if this actor ticks while active.
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Simple Action")
bool bCanTick = false;
Expand All @@ -37,7 +55,7 @@ class SIMPLEACTIONS_API USimpleAction : public UObject, public FTickableGameObje
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Simple Action", meta = (EditCondition = "bCanTick"))
bool bAutoEnableTickWhileActive = false;

// When true: This action can run in editor whild the game isn't running.
// When true: This action can run in editor while the game isn't running.
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Simple Action")
bool bAllowInLevelEditor = false;

Expand All @@ -50,6 +68,12 @@ class SIMPLEACTIONS_API USimpleAction : public UObject, public FTickableGameObje
UPROPERTY()
bool bTickManuallyEnabled = false;

// Timer Handle for Action Start Delay
FTimerHandle Timer_StartDelay;

// Timer Handle for Action Duration override
FTimerHandle Timer_DurationOverride;

public:
// True once the action starts, and false once it ends.
UPROPERTY(Transient, BlueprintReadOnly, Category = "Simple Action")
Expand All @@ -60,17 +84,53 @@ class SIMPLEACTIONS_API USimpleAction : public UObject, public FTickableGameObje
TWeakObjectPtr<AActor> ActingActor = nullptr;

// The object which triggered this action.
UPROPERTY(BlueprintReadOnly, Transient, Category = "Simple Action")
UPROPERTY(BlueprintReadOnly, Transient,Category = "Simple Action")
TWeakObjectPtr<UObject> ActionInstigator = nullptr;

// The amount of time before the Action Starts.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Simple Action")
float StartDelay;

// The amount of time the action will be forced to last.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Simple Action")
float DurationOverride;

// Called when the action is started.
UPROPERTY(BlueprintAssignable, Category = "Simple Action")
FOnActionStartedSignature OnActionStarted;

// Called when the action in Interrupted.
UPROPERTY(BlueprintAssignable, Category = "Simple Action")
FOnActionCancelSignature OnActionCancel;

// Called when the action is Ended.
UPROPERTY(BlueprintAssignable, Category = "Simple Action")
FOnActionEndedSignature OnActionEnded;

// Called when the action is Ended as successful.
UPROPERTY(BlueprintAssignable, Category = "Simple Action")
FOnActionCompleteSignature OnActionComplete;

// Called when the action has Failed to start.
UPROPERTY(BlueprintAssignable, Category = "Simple Action")
FOnActionFailedSignature OnActionFailed;

// Wrapper delegates used for binding to Multicast Delegates via blueprint exposed parameters
// Called when the action is Started
FOnActionStartedDelegate OnActionStartedDelegate;

// Called when the action is Interrupted.
FOnActionCancelDelegate OnActionCancelDelegate;

// Called when the action is Ended.
FOnActionEndedDelegate OnActionEndedDelegate;

// Called when the action is Ended as Successful.
FOnActionCompleteDelegate OnActionCompleteDelegate;

// Called when the action has failed to start.
FOnActionFailedDelegate OnActionFailedDelegate;

#if WITH_EDITORONLY_DATA
protected:
// True once the action has been initialized.
Expand All @@ -90,6 +150,10 @@ class SIMPLEACTIONS_API USimpleAction : public UObject, public FTickableGameObje
// This enables the object to use Latent Events.
virtual UWorld* GetWorld() const override;

// Gets the unique generated ID for this Simple Action
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Simple Action", meta = (ReturnDisplayName = "UID"))
virtual FString GetActionUID();

// UObject interface
/**
* Called after the C++ constructor and after the properties have been initialized, including those loaded from config.
Expand Down Expand Up @@ -118,6 +182,10 @@ class SIMPLEACTIONS_API USimpleAction : public UObject, public FTickableGameObje
UFUNCTION(BlueprintCallable, Category = "Simple Action", meta = (DefaultToSelf = "NewInstigator"))
void StartAction(UPARAM(DisplayName = "ActionActor") AActor* NewActionActor, UPARAM(DisplayName = "Instigator") AActor* NewInstigator);

// Call to start the action with given functions to bind to related delegate broadcasts
UFUNCTION(BlueprintCallable, Category = "Simple Action", meta = (DefaultToSelf = "NewInstigator"))
void StartAction_Bound(UPARAM(DisplayName = "ActionActor") AActor* NewActionActor, UPARAM(DisplayName = "Instigator") AActor* NewInstigator, FOnActionStartedDelegate Started, FOnActionCancelDelegate Interrupted, FOnActionEndedDelegate Ended, FOnActionCompleteDelegate Complete, FOnActionFailedDelegate Failed);

// Call to end the action with a status of successful.
UFUNCTION(BlueprintCallable, Category = "Simple Action", meta = (keywords = "complete, end, stop"))
void FinishAction(bool bSuccess = true);
Expand Down
7 changes: 6 additions & 1 deletion Source/SimpleActions/Public/Structs/SimpleActionList.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ struct SIMPLEACTIONS_API FSimpleActionList

public:

UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, meta = (ShowOnlyInnerProperties))
norab7 marked this conversation as resolved.
Show resolved Hide resolved
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta = (ShowOnlyInnerProperties))
TArray<TObjectPtr<USimpleAction>> Actions;

FSimpleActionList() {}
FSimpleActionList(TArray<USimpleAction*> Actions) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be

FSimpleActionList(TArray<USimpleAction*> NewActions) : Actions(NewActions) {}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't have to be New Actions though, could be an edited array of actions that were already present.
So I would disagree.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the constructor is only called for making a new object. the new was to avoid name conflicting, might not be necessary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it also called for a UE auto generated make node?

this->Actions = Actions;
}

};

7 changes: 6 additions & 1 deletion Source/SimpleActions/Public/Structs/SimpleActionSingle.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ struct SIMPLEACTIONS_API FSimpleActionSingle

public:

UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, meta = (ShowOnlyInnerProperties))
norab7 marked this conversation as resolved.
Show resolved Hide resolved
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, meta = (ShowOnlyInnerProperties))
TObjectPtr<USimpleAction> Action;

FSimpleActionSingle() : FSimpleActionSingle(nullptr){}
FSimpleActionSingle(USimpleAction* Action) {
this->Action = Action;
}

};