UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
AnimCustomInstanceHelper.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 This is a binding/unbinding functions for custom instances, specially for Sequencer tracks
5
6 This is complicated because Sequencer Animation Track and ControlRig Tracks could be supported through this interface
7 and it encapsulates lots of complications inside.
8
9 You can use one Animation Track - it's because this track doesn't take input from other pose, so this is always source
10 You can use multiple ControlRig Tracks - this is because ControlRig could take inputs from other sources
11
12 However this is not end of it. The way sequencer works is to allow you to add/remove anytime or any place.
13
14 So this behaves binding/unbinding depending on if you're source (Animation Track) or not (ControlRig).
15
16 If you want to be used by Animation Track, you should derive from ISequencerAnimationSupport and implement proper interfaces.
17 Now, you want to support layering, you'll have to support DoesSupportDifferentSourceAnimInstance to be true, and allow it to be used as source input.
18
19 1. ControlRigLayerInstance : this does support different source anim instance, and use it as a source of animation
20 2. AnimSequencerInstance: this does not support different source anim instance, this acts as one.
21
22 The code is to support, depending what role you have, you can be bound differently, so that you don't disturb what's currently available
23
24=============================================================================*/
25
26
27#pragma once
31#include "Engine/World.h"
33
34
35
37{
38public:
43 template<typename InstanceClassType>
45 {
46 bOutWasCreated = false;
47 // make sure to tick and refresh all the time when ticks
48 // @TODO: this needs restoring post-binding
50#if WITH_EDITOR
51 InSkeletalMeshComponent->SetUpdateAnimationInEditor(true);
52 InSkeletalMeshComponent->SetUpdateClothInEditor(true);
53#endif
55 InSkeletalMeshComponent->GetChildrenComponents(true, ChildComponents);
56 for (USceneComponent* ChildComponent : ChildComponents)
57 {
58 USkeletalMeshComponent* ChildSkelMeshComp = Cast<USkeletalMeshComponent>(ChildComponent);
60 {
62#if WITH_EDITOR
63 ChildSkelMeshComp->SetUpdateAnimationInEditor(true);
64 ChildSkelMeshComp->SetUpdateClothInEditor(true);
65#endif
66 }
67 }
68 // we use sequence instance if it's using anim blueprint that matches. Otherwise, we create sequence player
69 // this might need more check - i.e. making sure if it's same skeleton and so on,
70 // Ideally we could just call NeedToSpawnAnimScriptInstance call, which is protected now
71 const bool bShouldCreateCustomInstance = ShouldCreateCustomInstancePlayer(InSkeletalMeshComponent);
73 // See if we have SequencerInterface from current instance
76 const bool bCreateSequencerInterface = InstanceClassType::StaticClass()->ImplementsInterface(USequencerAnimationSupport::StaticClass());
78
80 {
81 InstanceClassType* DefaultObject = InstanceClassType::StaticClass()->template GetDefaultObject<InstanceClassType>();
83 bSupportDifferentSourceAnimInstance = SequencerSupporter && SequencerSupporter->DoesSupportDifferentSourceAnimInstance();
84 }
85
86 // if it should use sequence instance and current one doesn't support Sequencer Interface, we fall back to old behavior
88 {
89 // this has to wrap around with this because we don't want to reinitialize everytime they come here
90 // SetAnimationMode will reinitiaize it even if it's same, so make sure we just call SetAnimationMode if not AnimationCustomMode
91 if (InSkeletalMeshComponent->GetAnimationMode() != EAnimationMode::AnimationCustomMode)
92 {
93 InSkeletalMeshComponent->SetAnimationMode(EAnimationMode::AnimationCustomMode);
94 }
95
96 if (Cast<InstanceClassType>(InSkeletalMeshComponent->AnimScriptInstance) == nullptr || !InSkeletalMeshComponent->AnimScriptInstance->GetClass()->IsChildOf(InstanceClassType::StaticClass()))
97
98 {
100 InSkeletalMeshComponent->AnimScriptInstance = SequencerInstance;
101 InSkeletalMeshComponent->AnimScriptInstance->InitializeAnimation();
102 bOutWasCreated = true;
103 return SequencerInstance;
104 }
105 // if it's the same type it's expecting, returns the one
106 else if (InSkeletalMeshComponent->AnimScriptInstance->GetClass()->IsChildOf(InstanceClassType::StaticClass()))
107 {
108 return Cast<InstanceClassType>(InSkeletalMeshComponent->AnimScriptInstance);
109 }
110 }
111 else
112 {
113 // if it's the same type it's expecting, returns the one
114 if (CurrentAnimInstance->GetClass()->IsChildOf(InstanceClassType::StaticClass()))
115 {
116 return Cast<InstanceClassType>(InSkeletalMeshComponent->AnimScriptInstance);
117 }
118 // if currently it is sequencer interface, check to see if
120 {
121 // this is a case where SequencerInstance is created later, currently it has SequencerInteface
123 // if no source, create new one, and assign the new instance if current sequencer interface supports
124 if (CurSourceInstance == nullptr)
125 {
126 // if current doesn't have source instance and if it does support different source animation
127 if (CurrentSequencerInterface->DoesSupportDifferentSourceAnimInstance())
128 {
129 // create new one requested, and set to new source
131 //if it's sequencer inteface, create one, and assign
132 NewSequencerInstance->InitializeAnimation();
133 CurrentSequencerInterface->SetSourceAnimInstance(NewSequencerInstance);
134 bOutWasCreated = true;
136 }
137 else
138 {
139 UE_LOG(LogAnimation, Warning, TEXT("Currently Sequencer doesn't support Source Instance. They're not compatible to work together."));
140 }
141 }
142 // if source is same as what's requested, just return this.
143 else if (CurSourceInstance->GetClass()->IsChildOf(InstanceClassType::StaticClass()))
144 {
145 // nothing to do?
146 // it has already same type
148 }
149 // if this doesn't support different source anim instances, but the new class does
150 // see if we could switch it up
151 else if (bCreateSequencerInterface && bSupportDifferentSourceAnimInstance && !CurrentSequencerInterface->DoesSupportDifferentSourceAnimInstance())
152 {
156 if (NewSequencerInterface->DoesSupportDifferentSourceAnimInstance())
157 {
158 InSkeletalMeshComponent->AnimScriptInstance = NewInstance;
160 NewSequencerInterface->SetSourceAnimInstance(CurSourceInstance);
161
162 InSkeletalMeshComponent->AnimScriptInstance->InitializeAnimation();
163 bOutWasCreated = true;
164 return Cast<InstanceClassType>(InSkeletalMeshComponent->AnimScriptInstance);
165 }
166 }
167 }
168 // if requested one is support sequencer animation?
170 {
174 if (NewSequencerInterface->DoesSupportDifferentSourceAnimInstance())
175 {
176 InSkeletalMeshComponent->AnimScriptInstance = NewInstance;
177 NewSequencerInterface->SetSourceAnimInstance(CurrentAnimInstance);
178
179 InSkeletalMeshComponent->AnimScriptInstance->InitializeAnimation();
180 bOutWasCreated = true;
181 return Cast<InstanceClassType>(InSkeletalMeshComponent->AnimScriptInstance);
182 }
183 }
184 }
185
186 return nullptr;
187 }
188
190 template<typename InstanceClassType>
191 static void UnbindFromSkeletalMeshComponent(USkeletalMeshComponent* InSkeletalMeshComponent)
192 {
193#if WITH_EDITOR
194 InSkeletalMeshComponent->SetUpdateAnimationInEditor(false);
195 InSkeletalMeshComponent->SetUpdateClothInEditor(false);
196#endif
197
198 if (InSkeletalMeshComponent->GetAnimationMode() == EAnimationMode::Type::AnimationCustomMode)
199 {
201 // if same type, we're fine
204 {
205 bool bClearAnimScriptInstance = true;
206
208 {
209 if (SequencerInterface->DoesSupportDifferentSourceAnimInstance())
210 {
211 UAnimInstance* SourceAnimInstance = SequencerInterface->GetSourceAnimInstance();
212 // if we have source, replace with it
214 {
215 // clear before you remove
216 SequencerInterface->SetSourceAnimInstance(nullptr);
217 InSkeletalMeshComponent->AnimScriptInstance = SourceAnimInstance;
219 }
220 }
221 }
222
224 {
225 InSkeletalMeshComponent->ClearAnimScriptInstance();
226 }
227 }
228 else // if not, we'd like to see if SequencerSupport
229 {
231 if (SequencerInterface && SequencerInterface->DoesSupportDifferentSourceAnimInstance())
232 {
233 UAnimInstance* SourceInstance = SequencerInterface->GetSourceAnimInstance();
235
237 {
238 SequencerInterface->SetSourceAnimInstance(nullptr);
239 }
240 // this can be animBP, we want to return that
241 else if (SourceInstance)
242 {
243 SequencerInterface->SetSourceAnimInstance(nullptr);
244 InSkeletalMeshComponent->AnimScriptInstance = SourceInstance;
245 }
246 }
247 }
248 }
249 else if (InSkeletalMeshComponent->GetAnimationMode() == EAnimationMode::Type::AnimationBlueprint)
250 {
253 if (SequencerInterface && SequencerInterface->DoesSupportDifferentSourceAnimInstance())
254 {
255 UAnimInstance* SourceInstance = SequencerInterface->GetSourceAnimInstance();
256 if (SourceInstance)
257 {
258 SequencerInterface->SetSourceAnimInstance(nullptr);
259 InSkeletalMeshComponent->AnimScriptInstance = SourceInstance;
261 }
262 }
263
264 if (AnimInstance)
265 {
266 const TArray<UAnimInstance*>& LinkedInstances = const_cast<const USkeletalMeshComponent*>(InSkeletalMeshComponent)->GetLinkedAnimInstances();
268 {
269 // Sub anim instances are always forced to do a parallel update
271 }
272
273 AnimInstance->UpdateAnimation(0.0f, false);
274 }
275
276 // Update space bases to reset it back to ref pose
277 InSkeletalMeshComponent->RefreshBoneTransforms();
278 InSkeletalMeshComponent->RefreshFollowerComponents();
279 InSkeletalMeshComponent->UpdateComponentToWorld();
280 }
281
282 // if not game world, don't clean this up
283 if (InSkeletalMeshComponent->GetWorld() != nullptr && InSkeletalMeshComponent->GetWorld()->IsGameWorld() == false)
284 {
285 InSkeletalMeshComponent->ClearMotionVector();
286 }
287 }
288
289private:
291 static ANIMGRAPHRUNTIME_API bool ShouldCreateCustomInstancePlayer(const USkeletalMeshComponent* SkeletalMeshComponent);
292};
#define ensureAlways( InExpression)
Definition AssertionMacros.h:466
#define check(expr)
Definition AssertionMacros.h:314
#define TEXT(x)
Definition Platform.h:1272
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
Definition AnimCustomInstanceHelper.h:37
static void UnbindFromSkeletalMeshComponent(USkeletalMeshComponent *InSkeletalMeshComponent)
Definition AnimCustomInstanceHelper.h:191
static InstanceClassType * BindToSkeletalMeshComponent(USkeletalMeshComponent *InSkeletalMeshComponent, bool &bOutWasCreated)
Definition AnimCustomInstanceHelper.h:44
Definition SequencerAnimationSupport.h:28
Definition Array.h:670
Definition AnimInstance.h:353