Volt 1.1
The development of Volt has been started as a side module of Joint projects (Joint, Joint-Native) and it has many aspects that were rather rudimentary at the time.
In Volt 1.1, we wanted to make it as a fundamental tool of future slate development of Unreal Engine, and decided to add very powerful features that can make Volt stand out even further.
This update contains a lot of breaking changes. Please visit Breaking Changes Section and see possible issues with your current system.
Added
Make Your Animation In a Second With Declarative Animation Syntax
Volt had one specific issue on its animation declaration: It must be declared just like other UObject class in the engine. This is okay for a small and relatively simple project, but if you are in a big project, it's bad:
- It's just a mess when you see the project outliner. Sometimes, animation lists are getting bigger than the content itself.
- It takes significantly longer time to iterate on development since it requires individual header and implementation code file.
- It just takes extra efforts on copy-pasting, naming the animation, checking out other animation if it's redundant or not...
- modifying existing module on the animation takes too-much efforts. It's a huge pain in everyone's ass!
New Declarative Syntax for Animations!
Here is the biggest game-changer in Volt 1.1.
Now you can make animations without messing around and making useless code files in your source folder, you can just declare one right there whenever and where-ever you want; with the power of New-Declarative-Syntax!
Making Declarative Animation
You can start to make a new animation with VOLT_MAKE_ANIMATION( AnimationType )
macro. This will return UVoltAnimation*
object with all the modules we declared in the syntax.
You can put any UVoltAnimation type class name in AnimationType
to expand that pre-defined animation further, but you can also start from an empty animation by using UVoltAnimation
as AnimationType.
For example, if you use the macro like this, it will return UVoltAnimation
object with no module.
Making Declarative Modules
You can declare a module with VOLT_MAKE_MODULE( ModuleType )
macro.
You can put any UVoltModuleItem class name in ModuleType
, but that class must have argument declaration macro inside the class definition (This will be covered shortly later)
You can type .ArgumentName(ArgumentValue)
behind the macro to feed the data and specify the animation behavior.
In the image above, we declared UVolt_ASM_InterpWidgetTransform
module and set the animation speed to 7, and made it to use start widget transform, while specifying the start widget transform and target transform as well. This module will animate the slate like, emerging from nothing while scaling its size from zero to its actual (normal) size.
Alright, then now let's feed this module declaration inside the prev animation declaration to make that animation have an actual module inside. You can put this module in the animation declaration by covering the module declaration with "( )" after the animation declaration.
Then that empty animation now have the module inside! You can also feed multiple module in a single animations by splitting individual modules with ",".
This animation now have 3 different modules!
Very important information! That "( )" operator also works for some modules that can have its own submodules. In Volt 1.1, we currently have only two official modules that can have submodules, UVolt_ASM_Sequence
and UVolt_ASM_Simultaneous
.
UVolt_ASM_Sequence
is a module that plays the submodules sequentially (One-By-One) while waiting the previous module to be ended.
UVolt_ASM_Simultaneous
is a module that plays the submodules Simultaneously (All-At-Once). It's designed to be used with UVolt_ASM_Sequence
since animation's module playback is simultaneous by default.
Look at this image and try to read how each module works: It will start to animate the slate with each module at a time (UVolt_ASM_Sequence
): It will first change the render opacity to 0, and halt for specified duration, and interpolate the render opacity to 1 again and interpolate render transform (move the slate from right to left) at the same time (UVolt_ASM_Simultaneous
).
More detailed explanation about the submodule declaration and system will be described in the Better Submodule Supports Section.
Declaring Modules Arguments
Even if you don't want to make a new module, you have to read this part because it will let you know how to check out which argument is supported and how they are actually applied in the module.
To use .ArgumentName(ArgumentValue)
to actually initialize the properties and set the module properties as well, you must use special declaration in the module's header class definition to declare argument structure inside the module!
What you have to do is very simple: you have to use VOLT_MODULE_BEGIN_ARGS( ModuleName ) {}
and VOLT_MODULE_END_ARGS()
in the class definition, and declare all the argument with VOLT_MODULE_ARGUMENT( ArgumentType, ArgumentName )
that you will take in the module declaration macro. + initialize the initial value of the arguments as well.
For example, You can see the argument declaration part of UVolt_ASM_InterpBackgroundColor
here.
You can see that arguments like StartColor
, TargetColor
have been declared with VOLT_MODULE_ARGUMENT( FLinearColor, StartColor )
, VOLT_MODULE_ARGUMENT( FLinearColor, TargetColor )
, and have been initialized with _StartColor(FLinearColor::Black)
, _TargetColor(FLinearColor::Black)
...
All the arguments that has been declared in here can be accessed with the module declaration macro with .ArgumentName(ArgumentValue)
One more thing that is very important to do is actually using that argument structure to set the value of the module. To do this, you much declare void Construct(const FArguments& InArgs);
function in module's class definition.
And just grab the value from the argument structure and use it feed the actual property!
Again, Some modules that can have submodules must declare submodule container argument with VOLT_SUBMODULE_CONTAINER_ARGUMENT(ContainerName)
here to make it support () operator
.
This will be explained further in the Better Submodule Supports Section.
How effective is it?
Just watch this image. Both codes are used to make the same animation in Joint.
3 times shorter. The difference will be bigger if you make more complex animation.
Moreover, Volt 1.0 required 2 additional files for each animation declaration, but Volt 1.1? No additional file!
It will boost your slate animation development a ton, and we hope so.
Better Submodule Supports (IVoltSubModuleInterface) Has Been Added
In Volt 1.0, There was no particular rule or interface for the properties related to Submodules. So individual classes defined their properties with no rule, and had to define its own functions with it, Which is kinda mess and redundant.
So we introduced a new interface class, called IVoltSubModuleInterface
that provides some necessary functions related to submodules property for better and easier implementation, access and control.
In current version of Volt, 3 classes (UVoltAnimation
, UVolt_ASM_Sequence
, UVolt_ASM_Simultaneous
) are derived from this interface.
You can use and implement all of those function by just deriving IVoltSubModuleInterface
and putting some necessary macros.
The property for Submodules must be TArray<UVoltModuleItem*>
type, and must have Instanced
specifier in UPROPERTY()
macro.
Plus, You must put this macro in the class definition in order to make those functions work automatically.
VOLT_DECLARE_SUBMODULE_FUNCTIONS(SubModulesPropertyName)
This macro will automatically override fundamental interface functions with the provided property name.
See how does the class definition of the UVoltAnimation
looks like.
Submodules Support For UVoltModuleItem Classes
UVoltModuleItem
with submodule functionalities also can be derived from this class, and we provide a macro that enables the syntax support for submodule for argument.
Using VOLT_SUBMODULE_CONTAINER_ARGUMENT(SubModulesArgumentName)
in the argument structure declaration will let you use () operator
to grab submodules in the module declaration.
Then you can grab the submodules in the Construct(const FArguments& InArgs)
function.
See how does the class definition of the UVolt_ASM_Simultaneous
looks like.
Multithreading Supports For Animation Modules
Volt now supports multithreading at VoltAnimationManager's module update.
Thus, now the engine's main thread (GameThread) will not lag regardless whatever you do inside your module. Mining bitcoin, rendering whole RT scene, whatever you do.. (Similar to Chaos destruction system's thread individualization)
This has been updated to knock off the possible bottleneck of the system.
Most of the actions that are available to the users are thread-safe, including playing & removing & searching animation, adding & removing & searching track, adding & removing & searching variables, etc. except for the direct iteration of the modules | tracks | variables.
I won't list here which actions are thread-safe and which are not. If you want to heavily modify the fundamental aspects of the system, Please come to our discord and discuss with us!
You can specify the system to do or do not use the multithreading in Volt Preference section in Project Settings.
Restarting editor (or game) is required for actual modification after change.
Animation Tick System Reworked
Tick system has been overhauled. Now Volt will work on every occasions on the engine by default. While in a drag & drop operation, In debugging mode, precise mouse movement and inputs.. Whatever it is!
Now the system doesn't support manual tick. Functions related to the manual tick have been removed: Please remove them from the source, otherwise it won't compile.
Slate Assignment + Query Logic Overhauled
We removed per animation manager slate assignment, instead, made the UVoltSubsystem control all the assigned slate.
This lets you create one unique IVoltInterface (typically UVoltProxy) Object per one slate object. It can save more resources and also allows the modification of the slate between multiple animation managers more consistent.
And now UVoltSubsystem provides a hash table for VoltInterfaces, allowing O(1) time querying of the provided slates. Now you can spam VOLT_FIND_OR_ASSIGN_INTERFACE_FOR()
as many times as you want.
VoltInterfaces are now garbage collected with provided intervals. You can specify the interval with Volt Subsystem Clean Up Interval
property from Volt Preference section in Project Settings.
Better Interp Functions For Modules Have Been Introduced
Now every module that supports interp feature have been reworked to supports more options on the interpolation method other than linear interpolation.
You can check out how each mode of interpolation work like with transform update in our new VoltStarship, where we show off the module features in.
All the modules that support reworked interpolation system are in this list below:
- UVolt_ASM_InterpBackgroundColor
- UVolt_ASM_InterpBoxProperties
- UVolt_ASM_InterpChildSlotPadding
- UVolt_ASM_InterpColor
- UVolt_ASM_InterpForegroundColor
- UVolt_ASM_InterpRenderOpacity
- UVolt_ASM_InterpWidgetTransform
In the reworked interpolation module, we introduced a mode enum to make you select the action type of the module as you want.
Including the mode enum, all the properties (mentioned below, and start & targetValue etc...) are exposed as argument. You can set them in the declarative animation as well.
Rate Based Interpolation
EVoltInterpMode::RateBased refers to a mode that interpolates animation with specified update rate.
This mode is useful when you don't know the duration or alpha of the interpolation, but it supports only 2 interp functions (const, linear).
Volt 1.0 styled interpolation logic is pretty much same with this mode.
In Volt 1.1, we renamed InterpSpeed
property in those modules to RateBasedInterpSpeed
. Please make sure to rename them your code.
Alpha Based Interpolation
EVoltInterpMode::AlphaBased refers to a mode that Interpolate animation with specified alpha (or by duration) between A to B. This mode is useful when you want to animate something with specific duration.
This mode supports much more interp functions (16~) but you must take more care on the animation duration.
New Modules Have Been Added
UVolt_ASM_Simultaneous
UVolt_ASM_Simultaneous
is a module that plays the submodules Simultaneously (All-At-Once). Typically, it has been designed to be used together with UVolt_ASM_Sequence
UVolt_ASM_InterpForegroundColor
UVolt_ASM_InterpForegroundColor
is a module that changes ForegroundColor of particular slates. It changes UVoltVar_ForegroundColor
.
UVolt_ASM_Delay
UVolt_ASM_Delay
is a module that will be active for the specified duration. This is useful to use with UVolt_ASM_Sequence
to create delayed animation.
UVolt_ASM_InterpBoxProperties
UVoltVar_Box
is a module that interpolate the properties of specified SBox
. It changes UVoltVar_Box
.
New Variables (+ Variable Action) Have Been Added
UVoltVar_Box
UVoltVar_Box
is a variable that controls properties of SBox
. It includes WidthOverride, HeightOverride, MinDesiredWidth, MinDesiredHeight, MinDesiredHeight, MaxDesiredHeight, MinAspectRatio, MaxAspectRatio of SBox
UVoltVar_ForegroundColor
UVoltVar_ForegroundColor
is a variable that controls the foreground color of the slates. In Volt 1.1, it supports SBorder and SButton.
Added Editor Module
I really didn't want to add this, but some of the new features must be separated from the runtime module itself, so here we go!
Volt Starship
Now Volt provide a built-in page for the animation showcase & use-cases inside the engine.
This is also a great boilerplate for your system!
In the future update, we will update and show off some new examples as well! Stay tuned~
Changed
UVoltAnimationManager Related
UVoltAnimationManager's Slate Assign Logic Overhauled
Now every individual Volt Interface instances will be assigned to the UVoltSubsystem
, instead of UVoltAnimationManager
See Slate Assignment + Query Logic Overhauled section for further details.
UVoltAnimationManager's GC Logic Overhauled
Since we don't support assigning the slates to the UVoltAnimationManager, we changed the way we use to make the system know whether an animation manager is abandoned or not.
UVoltAnimationManager
now has OwnerVoltInterface property that indicates the owner volt interface of the animation manager, And now you must provide one of those to prevent your animation manager from being GC'ed :
- a valid outer other than UVoltSubsystem
- a valid VoltInterface
thus, VOLT_IMPLEMENT_MANAGER()'s signatures have been changed to take at least one of those. Please change your code if it spill out some errors
Plus, now destruction check logic doesn't be executed in every tick, it now takes certain interval. It follows the value of Volt Subsystem Clean Up Interval
property from Volt Preference section in Project Settings.
UVoltModuleItem Related
UVoltModuleItem's New Lifecycle Events
Now UVoltModuleItem
has much more proper lifecycle events. You can override OnModuleBeginPlay()
and OnModuleEndPlay()
to apply some initial processes or clean up the value of modules.
For example, In interp type modules, we use OnModuleBeginPlay()
to apply start value or grab start value from the variable before we actually move on the real processing.
VOLT_GET_ANIMATION's Signatures have been changed
Function VOLT_GET_ANIMATION got another overload that will save your life, Now you don't need to explicitly grab the static class from the animation class like this
UVolt_ASA_Emerge* Anim_Emerge = VOLT_GET_ANIMATION<UVolt_ASA_Emerge>(UVolt_ASA_Emerge::StaticClass());
Instead, now you can do it like this. (Most recommended)
UVolt_ASA_Emerge* Anim_Emerge = VOLT_GET_ANIMATION<UVolt_ASA_Emerge>()
Or this.
UVoltAnimation* Anim_Emerge = VOLT_GET_ANIMATION(UVolt_ASA_Emerge::StaticClass())
Old signature still compiles, but not recommended to use since it takes the animation class as its parent. Please change them to one of those new signature.
VariableActions Allocation Logic Of Variables Has Been Changed
Now Volt Setting doesn't contain a list of variable actions for each variable. Instead, we use MACRO_REGISTER_VARIABLE_ACTION
to manually cache the action to the variable.
See VoltVaribles.cpp for further details.
Fixed
An issue that FVoltAnimationTrack's GUID is not handled appropriately and resulting in hiding some animations when multiple animation has been played with the same Volt has been fixed.
Breaking Changes
Most of them has some explanation on the text above. If you need further details, please read the main text and feel free to ask us your question about the changes.
-
VoltVariableActions.cpp & .h
,VoltVariables.cpp & .h
have been moved to Volt from VoltCore. We decided to move the variables and actions to the content module, since those are not the fundamental of the system itself. Please fix the module dependency and includes if needed. -
Now the system doesn't support manual tick. Functions related to the manual tick have been removed. Please remove any code that work with them from the source, and if your slate override tick() function for the manual tick feature, you're okay to remove that overriding.
-
UVoltAnimationManager::UpdateAnimation()
has been deprecated. Please callUVoltAnimationManager::Tick()
instead to force update the slate's actions. -
In Volt 1.1, we renamed
InterpSpeed
property in those modules toRateBasedInterpSpeed
. Please make sure to rename them your code. -
Please make sure to change the old usage of
VOLT_GET_ANIMATION
to newer one. fromVOLT_GET_ANIMATION<UVolt_ASA_Emerge>(UVolt_ASA_Emerge::StaticClass());
to:VOLT_GET_ANIMATION<UVolt_ASA_Emerge>()
VOLT_GET_ANIMATION(UVolt_ASA_Emerge::StaticClass())
-
The logic of the
UVoltAnimationManager
clean up (GC) has been changed, thus you need to be more careful when you callVOLT_IMPLEMENT_MANAGER
to make it meet the requirements to sustain. -
Now
UVoltAnimationManager
doesn't provide slate querying features anymore. Please useUVoltSubsystem
's equivalent functions instead. -
Now
UVoltSetting
doesn't take "variable actions for each variable types list" anymore. Please change your code to make your new variable actions be assigned directly in the variable's declaration.