Skip to main content

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.

important

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.
    img.png
  • 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!

img_1.png

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.

img_3.png

For example, if you use the macro like this, it will return UVoltAnimation object with no module.

Making Declarative Modules

img_2.png

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)

img_4.png

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.

img_5.png

Then that empty animation now have the module inside! You can also feed multiple module in a single animations by splitting individual modules with ",".

img_6.png

This animation now have 3 different modules!

tip

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).

img_7.png

More detailed explanation about the submodule declaration and system will be described in the Better Submodule Supports Section.

Declaring Modules Arguments

note

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.

img_9.png

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)

img_10.png

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.

img_8.png

And just grab the value from the argument structure and use it feed the actual property!

img_11.png

tip

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.

declarative.png

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.

info

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.

img_18.png

important

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.

img_15.png

See how does the class definition of the UVoltAnimation looks like.

img_17.png

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.

img_19.png

Using VOLT_SUBMODULE_CONTAINER_ARGUMENT(SubModulesArgumentName) in the argument structure declaration will let you use () operator to grab submodules in the module declaration.

img_20.png

Then you can grab the submodules in the Construct(const FArguments& InArgs) function.

img_21.png

See how does the class definition of the UVolt_ASM_Simultaneous looks like.

img_16.png


Multithreading Supports For Animation Modules

img_12.png

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.

note

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!

img_14.png

You can specify the system to do or do not use the multithreading in Volt Preference section in Project Settings.

info

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!

important

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.

img_14.png


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.

info

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.

img_25.png

info

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.

img_26.png

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).

info

Volt 1.0 styled interpolation logic is pretty much same with this mode.

warning

In Volt 1.1, we renamed InterpSpeed property in those modules to RateBasedInterpSpeed. Please make sure to rename them your code.

img_24.png

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.

img_22.png


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

img_20.png

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.

img_27.png

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!

info

In the future update, we will update and show off some new examples as well! Stay tuned~


Changed


UVoltAnimationManager's Slate Assign Logic Overhauled

Now every individual Volt Interface instances will be assigned to the UVoltSubsystem, instead of UVoltAnimationManager

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
warning

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.

img_14.png


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.

img_23.png

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.

img_28.png

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())
warning

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.

info

See VoltVaribles.cpp for further details.

img_13.png


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 call UVoltAnimationManager::Tick() instead to force update the slate's actions.

  • In Volt 1.1, we renamed InterpSpeed property in those modules to RateBasedInterpSpeed. Please make sure to rename them your code.

  • Please make sure to change the old usage of VOLT_GET_ANIMATION to newer one. from VOLT_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 call VOLT_IMPLEMENT_MANAGER to make it meet the requirements to sustain.

  • Now UVoltAnimationManager doesn't provide slate querying features anymore. Please use UVoltSubsystem'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.