Skip to content

This code plugin provides functions that drastically improve the quality of life during the implementation of game flow in C++.

License

Notifications You must be signed in to change notification settings

scott-hf/UE4EnhancedCodeFlow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Enhanced Code Flow for Unreal Engine 4

This code plugin provides functions that drastically improve the quality of life during the implementation of game flow in C++.
It works very well with gameplay programming, UI programming with a lot of transitions or in any other situation.

UE5

This plugin works with Unreal Engine 5 too! Checkout branch ue5 to get a compatible version.

Table of content

Contact

If you have any question or suggestion regardles this plugin simply add an Issue to the github project or write an e-mail to me: zompi2@gmail.com I will try my best to answer it quickly :)

Changelog

The Changelog has been put into this file: Changelog.txt

Example Project

The example project wich uses this plugin can be found in this repository.

ecfscreen

Usage

Run the following functions to use enhanced code flow!

Note that every function must receive a pointer to an owner that runs this function in it's first argument.
The owner must be able to return a World via GetWorld() function.

Delay

Execute specified action after some time. This can be useful in many various situations. Everytime when I was using a Delay node in blueprints I wish there was an equivalent of it in c++.

FFlow::Delay(this, 2.f, [this]()
{
  // Code to execute after 2 seconds.
});

Back to actions list
Back to top

Add Ticker

Creates a ticker. It can tick specified amount of time or until it won't be stopped or when owning object won't be destroyed.
Useful for actors and components that you don't want to be tickeable, but needs one tick to do something.

Run ticker for 10 seconds

FFlow::AddTicker(this, 10.f, [this](float DeltaTime)
{
  // Code to execute every tick
});

Run ticker for 10 seconds and run a callback when it finishes

FFlow::AddTicker(this, 10.f, [this](float DeltaTime)
{
  // Code to execute every tick
}, [this]()
{
  // Code to execute when ticker finishes ticking
});

Run ticker for infinite time and stop it when you want to

FFlow::AddTicker(this, [this](float DeltaTime, FECFHandle TickerHandle)
{
  // Code to execute in every tick.

  // Use this to stop the ticker
  FFlow::StopAction(this, TickerHandle);
});

Run ticker for infinite time and something else stops it

FECFHandle TickerHandle = FFlow::AddTicker(this, [this](float DeltaTime)
{
  // Code to execute in every tick.
});

// Use this to stop the ticker
FFlow::StopAction(this, TickerHandle);

Note 1: Tickers and every other plugin actions are impacted by global time dilation.
Note 2: You can check if the ticker (or any other action) is running using FFlow::IsActionRunning(TickerHandle)

Back to actions list
Back to top

Wait and execute

Waits until specific conditions are made and then executes code.
The conditions are defined in a form of predicate.
Perfect solution if code needs a reference to an object, which spawn moment is not clearly defined.

FFlow::WaitAndExecute(this, [this]()
{
  // Write your own predicate. 
  // Return true when you want to execute the code below.
  return bIsReadyToUse;
},
[this]()
{
  // Implement code to execute when conditions are met.
});

Back to actions list
Back to top

While true execute

While the specified conditions are true tick the given code.
This one is useful when you want to write a loop that executes one run every tick until it finishes it's job.

FFlow::WhileTrueExecute(this, [this]()
{
  // Write your own predicate. 
  //Return true when you want this action to continue.
  return bIsRunning;
},
[this](float DeltaTime)
{
  // Implement code to tick when conditions are true.
});

Back to actions list
Back to top

Add timeline

Easily launch the timeline and update your game based on them. Great solution for any kind of blends and transitions. The function requires the following parameters:

  • StartValue - a value with which the timeline will begin;
  • StopValue - a value with which the timeline will end. StopValue can be lesser than StartValue;
  • Time - how long the timeline will work;
  • TickFunc - a function that will tick with the timeline. It has the following arguments:
    • Value - a current value on this timeline;
    • Time - a time that passed on this timeline;
  • CallbackFunc - a function that will run when the timeline comes to an end. Has the same arguments as TickFunc. This function is optional;
  • BlendFunc - a function that describes a shape of the timeline:
    • Linear (default)
    • Cubic
    • EaseIn
    • EaseOut
    • EaseInOut
  • BlendExp - an exponent defining a shape of EaseIn, EaseOut and EaseInOut function shapes. (default value: 1.f);
FFlow::AddTimeline(this, 0.f, 1.f, 2.f, [this](float Value, float Time)
{
  // Code to run every time the timeline tick
}, 
[this](float Value, float Time)
{
  // Code to run when timeline stops
}, 
EECFBlendFunc::ECFBlend_Linear, 2.f);

Back to actions list
Back to top

Add custom timeline

Creates a discrete timeline which shape is based on a UCurveFloat. Works like the previously described timeline, but an asset with a curve must be given.

FFlow::AddCustomTimeline(this, Curve, [this](float Value, float Time)
{
  // Code to run every time the timeline tick
}, 
[this](float Value, float Time)
{
  // Code to run when timeline stops
});

Back to actions list
Back to top

Time Lock

(Instanced)

Blocks execution of the block of code until the given time has passed.

static FECFInstanceId InstanceId = FECFInstanceId::NewId();
FFlow::TimeLock(this, 2.f, [this]()
{
  // This code will run now, and won't be able to execute for 2 seconds.
}, InstanceId);

Back to actions list
Back to top

Do Once

(Instanced)

Allow to execute the given block of code only once.

static FECFInstanceId InstanceId = FECFInstanceId::NewId();
FFlow::DoOnce(this, [this]()
{
  // This code can be run only once.
}, InstanceId);

Back to actions list
Back to top

Do N Times

(Instanced)

Allow to execute the given block of code only given amount of times.

static FECFInstanceId InstanceId = FECFInstanceId::NewId();
FFlow::DoNTimes(this, 5, [this](int32 Counter)
{
  // This code can be run only 5 times.
}, InstanceId);

Back to actions list
Back to top

Extra settings

You can define extra settings at the end of each action launch. Currently the following actions are available:

  • Time Intervals - defines the length of one tick.
  • First Delay - defines when the first tick should be performed.
  • Ignore Game Pause - it will ignore the game pause.
  • Ignore Global Time Dilation - it will ignore global time dilation when ticking.
FFlow::AddTicker(this, 10.f, [this](float DeltaTime)
{
  // Code to execute every 1 second for 10 seconds.
}, nullptr, FECFActionSettings(1.f, 0.f, false, false));
FFlow::AddTicker(this, 10.f, [this](float DeltaTime)
{
  // Code to execute every tick for 10 seconds 
  // while ignoring global time dilation.
}, nullptr, FECFActionSettings(0.f, 0.f, true, false));
FFlow::AddTicker(this, 10.f, [this](float DeltaTime)
{
  // Code to execute every 1 seconds for 10 seconds, 
  // after 5 seconds have passed, while ignoring 
  // global time dilation and pause.
}, nullptr, FECFActionSettings(1.f, 5.f, true, true));

To make defining these settings easier there are few macros that creates a settings structure with just one option:

  • ECF_TICKINTERVAL(5.f) - settings which sets tick interval to 5 second
  • ECF_DELAYFIRST(1.f) - settings which makes this action to run after 1 second delay
  • ECF_IGNOREPAUSE - settings which makes this action ignore game pause
  • ECF_IGNORETIMEDILATION - settings which makes this action ignore global time dilation
  • ECF_IGNOREPAUSEDILATION - settings which makes this action ignore pause and global time dilation
FFlow::Delay(this, 2.f, [this]()
{
  // Run this code after 2 seconds, while ignoring game pause.
}, ECF_IGNOREPAUSE);

Back to top

Instanced Actions

Some actions can be Instanced. Instanced action is an action that has valid FECFInstanceId.
Such action can be executed only once.

As long as the action with given valid FECFInstanceId is running, no other action with the same FECFInstanceId can be executed.

InstanceId has two scopes:

Object

This action is instanced per object. It means there can be multiple executions of this action, but only one per object.
This is the scope you will probably use most the time.

Global

This action is instanced globally. It means that if any object executes this action this and any other object can't execute it again.

Obtaining InstanceId

To get next valid InstanceId use the NewId() function

FECFInstanceId::NewId(); // for Object scoped InstanceId
or
FECFInstanceId::NewId(EECFInstanceIdScope::Global); // for Global scoped InstanceId

Back to top

Stopping actions

Every function described earlier returns a FECFHandle which can be used to check if the following action is running and to stop it.

FFlow::IsActionRunning(GetWorld(), Handle); // <- is this action running?
FFlow::StopAction(GetWorld(), Handle); // <- stops this action!

Note that this function requires a pointer to the existing World in order to work properly.

You can also stop all of the actions from a specific owner or from everywhere. Stopped actions can launch their completion callbacks or not, depending on the given argument:

FFlow::StopAllActions(GetWorld()); // <- stops all of the actions
FFlow::StopAllActions(GetWorld(), true); // <- stops all of the actions and launch their callbacks
FFlow::StopAllActions(GetWorld(), false, Owner); // <- stops all of the actions started from this specific owner

You can also stop a specific Instanced action with the FECFInstanceId:

FFlow::StopInstancedAction(GetWorld(), InstanceId); // <- stops all actions with this InstanceId
FFlow::StopInstancedAction(GetWorld(), InstanceId, true); // <- stops all actions with this InstanceId and launch their callbacks
FFlow::StopInstancedAction(GetWorld(), InstanceId, false, Owner); // <- stops action with this InstanceId running on this Owner

You can also stop all of the specific actions. In this case you can also optionally specifiy an owner of this actions, or simply stop all of them. You can also specify if stopped actions should launch their completion callbacks or not.

FFlow::RemoveAllDelays(GetWorld());
FFlow::RemoveAllTickers(GetWorld());
FFlow::RemoveAllWaitAndExecutes(GetWorld());
FFlow::RemoveAllWhileTrueExecutes(GetWorld());
FFlow::RemoveAllTimelines(GetWorld());
FFlow::RemoveAllCustomTimelines(GetWorld());
FFlow::RemoveAllTimeLocks(GetWorld());

Back to top

Extending plugin

If you have a source code of this plugin you can easily extend it's functionalities!

Check how other actions are made to easier understand how to extend the plugin.

  1. Create a class that inherits from UECFActionBase
  2. Implement Setup function, which accepts all parameters you want to pass to this action. Setup function must return true if the given parameters are valid.
bool Setup(int32 Param1, int32 Param2, TUniqueFunction<void()>&& Callback)
{
  CallbackFunc = MoveTemp(Callback);
  if (CallbackFunc) return true;
  return false;
}

Any callback must be passed as an r-value reference and be moved to the action's variable.

  1. Override Init and Tick functions if needed.
  2. If you want this action to be stopped while ticking - use MarkAsFinished() function.
  3. If you want to launch a callback when this action is stopped by StopAction method with bComplete set to true - override Complete() function.
  4. In the FEnhancedCodeFlow class implement static function that launches the action using AddAction function. The function must receive a pointer to the launching UObject, FECFActionSettings, FECFInstanceId and every other argument that is used in the action's Setup function in the same order. It must return FECFHandle.
FECFHandle FEnhancedCodeFlow::NewAction(UObject* InOwner, int32 Param1, int32 Param2, TUniqueFunction<void()>&& Call, const FECFActionSettings& Settings = {})
{
  if (UECFSubsystem* ECF = UECFSubsystem::Get(InOwner))
    return ECF->AddAction<UECFNewAction>(InOwner, Settings, FECFInstanceId(), Param1, Param2, MoveTemp(Call));
  else
    return FECFHandle();
}
  1. You can optionally add static function which will stop this action
void FFlow::RemoveNewActions(const UObject* WorldContextObject, bool bComplete, UObject* InOwner)
{
  if (UECFSubsystem* ECF = UECFSubsystem::Get(InOwner))
  {
    ECF->RemoveActionsOfClass<UECFNewAction>(bComplete, InOwner);
  }
}
  1. You can optionally run SetMaxActionTime in actions Init phase to determine the maximum time in seconds this action should run.

IMMPORTANT! SetMaxActionTime is only to help ticker run ticks with proper delta times.
It will not stop the action itself!

  1. For Instanced actions pass proper FECFInstanceId to the AddAction function.

  2. You can optionally override RetriggeredInstancedAction() function to add any logic that should be executed when the instanced action is called while already existing.

It is done! Now you can run your own action:

FFlow::NewAction(this, 1, 2, [this]()
{
  // Callback code.
}, ECF_IGNOREPAUSE);

Back to top

Blueprints support

Even though this was originally code only plugin I decided to move it's functions to Blueprints too:

Delay

delaynode

Add Ticker

ticker

Wait and Execute

wee

While True Execute

wte

Add Timeline

timel

Add Custom Timeline

cust

Time Lock

ecftlock

Other functions

Stopping actions

stopac

Handles and InstanceIds

handles

Back to top

Special thanks

I want to send special thanks to Monika, because she always supports me and believes in me, to Pawel, for allowing me to test this plugin on his project and to everyone that contributed to this project.
Also, I want to thank You for using this plugin! It is very important for me that my work is useful for someone!
Happy coding!

Back to top

About

This code plugin provides functions that drastically improve the quality of life during the implementation of game flow in C++.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 95.6%
  • C 4.0%
  • C# 0.4%