-
-
Notifications
You must be signed in to change notification settings - Fork 99
Get Started with the Scripting API
Most Projects with Turbo Sequence require a custom Animation Controller which is invoking animations, IK and Root Motion or more. In order to get started, here is the most minimal basic setup which every Animation Controller needs:
.h:
#pragma once
#include "CoreMinimal.h"
#include "TurboSequence_MinimalData_Lf.h"
#include "GameFramework/Actor.h"
#include "ADemoMeshTester.generated.h"
UCLASS()
class TURBOSEQUENCE_LF_API ADemoMeshTester : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ADemoMeshTester();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY(EditAnywhere, Category="Mesh Tester")
// The spawn data to give spawn info in the Actor Details panel
FTurboSequence_MeshSpawnData_Lf SpawnData;
UPROPERTY(EditAnywhere, Category="Mesh Tester")
// The Animation to Play
TObjectPtr<UAnimSequence> MeshAnimation;
UPROPERTY(EditAnywhere, Category="Mesh Tester")
// The Animation settings to play
FTurboSequence_AnimPlaySettings_Lf MeshAnimationSettings = FTurboSequence_AnimPlaySettings_Lf();
};.cpp:
#include "ADemoMeshTester.h"
#include "TurboSequence_Manager_Lf.h"
// Sets default values
ADemoMeshTester::ADemoMeshTester()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ADemoMeshTester::BeginPlay()
{
Super::BeginPlay();
// Create the Instance
const FTurboSequence_MinimalMeshData_Lf& Instance = ATurboSequence_Manager_Lf::AddSkinnedMeshInstance_GameThread(SpawnData, GetActorTransform(), GetWorld());
// When the Mesh Instance is valid, do stuff with it..
if (Instance.IsMeshDataValid())
{
// Add it to an Update Group to solve it later in groups
ATurboSequence_Manager_Lf::AddInstanceToUpdateGroup_Concurrent(0, Instance);
// PLay an animation with the settings
const FTurboSequence_AnimMinimalCollection_Lf& Animation = ATurboSequence_Manager_Lf::PlayAnimation_Concurrent(Instance, MeshAnimation, MeshAnimationSettings);
}
}
// Called every frame
void ADemoMeshTester::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Create an Update Context
FTurboSequence_UpdateContext_Lf UpdateContext = FTurboSequence_UpdateContext_Lf();
// Here we use the same index we specified when creating the instance, more info about update groups in the docs
UpdateContext.GroupIndex = 0;
// Solve this group
ATurboSequence_Manager_Lf::SolveMeshes_GameThread(DeltaTime, GetWorld(), UpdateContext);
}This code above will generate one Mesh Instance and play an animation to solve it.
TS best practice of writing the code structure is like following:
// Code Flow
+--------------------+
| Game Thread Logic..|
+--------------------+
|
v
+--------------------+
| for loop (all |
| instances) |
+--------------------+
|
v
+--------------------+
| Solve Update Group |
+--------------------+
|
v
+--------------------+
| Game Thread Logic..|
+--------------------+
|
v
So basically, you update all instance at once, in a big loop which can be multithreaded and after this loop ends you need to solve the animations per update group.
ECS is totally fine, you can update all instances in an ECS Loop, however make sure to call
ATurboSequence_Manager_Lf::SolveMeshes_GameThread(DeltaTime, GetWorld(), UpdateContext);one time per update group, one time a frame
This is optional and only needed if you encounter too high CPU Times.
In order to get better CPU Times, the instances can get updated in Update Groups, which mean:
+--------------------+
| Group 1 |
+--------------------+
|
v
+--------------------+
| Frame 1 Ends |
+--------------------+
|
v
+--------------------+
| Group 2 |
+--------------------+
|
v
+--------------------+
| Frame 2 Ends |
+--------------------+
|
v
+--------------------+
| Group N |
+--------------------+
|
v
+--------------------+
| Frame N Ends |
+--------------------+
|
v
It will make Group 1 Lag N Amounts of Frames behind the current Frame Group, which will look like slow update rates of the Animations, however if the Units are 50 Meters away from the Camera, the "Lag" is not noticeable.
With this way, it's possible to increase the number of instances in the map up to 200k or more, depending on the target device.
Update Groups are not managed by the system, you have to design the logic yourself
In order to add an instance to a group, you call:
ATurboSequence_Manager_Lf::AddInstanceToUpdateGroup_Concurrent(MeshUpdateContext.GroupIndex, Instance);To remove one instance to a group call:
ATurboSequence_Manager_Lf::RemoveInstanceFromUpdateGroup_Concurrent(MeshUpdateContext.GroupIndex, Instance);NOTE: It's self-managed for more control, that means you have to add and remove it yourself when adding and removing an instance.
- When Having multiple Update Groups, the DeltaTime is too small which mean you have to Accumulate it over the frames which are not part of this Group
The whole Turbo Sequence API is inside 2 Files: