Thank you for your input. Those are interesting test results, I was worried about performance as it seems like it would be worse using the proxy as it’s causing me to have some duplicate variables that already exists on my anim instance.
But as you said, the proxy is the only way to go. If you attempt to access properties outside the anim instance it will crash at runtime. Using the proxy I have not had any issues. Although its still not clear if I’m doing everything correctly, but I was able to get this working. (At least it appears to be).
The docs are actually incorrect, it wont compile if you copy what they do. Below is a simplified example of what I found to work. I also removed the proxy from being accessible in blueprints as it makes no sense because you can just use the property access node in blueprints.
Anim Instance Header:
#include "Animation/AnimInstanceProxy.h"
USTRUCT()
struct FCoreAnimInstanceProxy : public FAnimInstanceProxy
{
GENERATED_BODY()
virtual void InitializeObjects(UAnimInstance* InAnimInstance) override;
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
virtual void Update(float DeltaSeconds) override;
public:
// Owner Data
UPROPERTY(Transient)
APawn* Owner;
UPROPERTY(Transient)
FVector ActorLocation;
// Movement Component Data
UPROPERTY(Transient)
UCharacterMovementComponent* MovementComponent;
UPROPERTY(Transient)
FVector CurrentAcceleration;
// Anim Instance Data
UPROPERTY(Transient)
bool bIsAnyMontagePlaying;
};
UCLASS()
class COREV5_API UCoreAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UCoreAnimInstance(const FObjectInitializer& ObjectInitializer);
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
private:
///////////////////////////////////////////////////
// Thread Safe Proxy
///////////////////////////////////////////////////
UPROPERTY(Transient)
FCoreAnimInstanceProxy Proxy;
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override { return &Proxy; }
virtual void DestroyAnimInstanceProxy(FAnimInstanceProxy* InProxy) override {}
friend struct FExampleAnimInstanceProxy;
};
Anim Instance Cpp:
void FCoreAnimInstanceProxy::InitializeObjects(UAnimInstance* InAnimInstance)
{
FAnimInstanceProxy::InitializeObjects(InAnimInstance);
Owner = InAnimInstance->TryGetPawnOwner();
if (Owner == nullptr) { return; }
MovementComponent = Cast<UCharacterMovementComponent>(Owner->GetMovementComponent());
}
void FCoreAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
{
FAnimInstanceProxy::PreUpdate(InAnimInstance, DeltaSeconds);
if (InAnimInstance)
{
bIsAnyMontagePlaying = InAnimInstance->IsAnyMontagePlaying();
}
if (Owner)
{
ActorLocation = Owner->GetActorLocation();
}
if (MovementComponent)
{
CurrentAcceleration = MovementComponent->GetCurrentAcceleration();
}
}
void FCoreAnimInstanceProxy::Update(float DeltaSeconds)
{
FAnimInstanceProxy::Update(DeltaSeconds);
}
Just for clarity for future readers, to access an updated property of the proxy, in your anim instance you simply ask the proxy:
Location = Proxy.ActorLocation;
Although there is still many questions about this proxy and the “correct” way to use it:
-
When to use PreUpdate, Update, or PostUpdate to update variables? I am currently doing everything with PreUpdate as it gives me access to the InAnimInstance.
-
Can I use the proxy to fill the variables directly on my anim instance instead of having a copy variable on the proxy that is being filled?
-
Can I access something on the Anim Instance directly in a thread safe function without the proxy? Like bIsAnyMontagePlaying.
-
Is it safe to use InitializeObjects to cast to the correct anim instance, pawn owner, and movement component, and stores those values on the proxy without issues, or should we just grab, cast, and check for null each time from the InAnimInstance on PreUpdate?