- Published on
UE4骨骼共享插件AnimationSharingPlugin
- Authors

- Name
- 东哥
前言
从遥远的2019年搬运而来, 作为记录, 不确定是否有更新
- 引用文档的结构图

- 插件已经启用,如果关闭到插件栏启用
Setup文件


- 简单配置以后如下图

- 一个SkeltonSetup数据在UAnimationSharingManager::Register方法里会生成一个**
UObject<UAnimationSharingInstance*>,这个UObj会spawn一个场景里的单例Actor**,同时存放所有实例对象以及对应的所有参数;这个Actor会根据上图中AnimationStates的数据量创建若干USkeletalMeshComponent维护动画
AnimationBlurprintForBlending(图中的ABP_Trans)

- 这个蓝图简单理解为用于动画切换,owner就是那个Actor单例
- 逻辑很简单,直接拷贝就可以

StateProcessorClass
-
必须配置一个枚举,对应所有状态,否则无法配置动画(AnimationStates数据)
-

-
每隔场景中的实例都会拥有一个对应的Object,一直在Tick得到这个实例的状态,主要逻辑如下

void UStatProc::ProcessActorState_Implementation(int32& OutState, AActor* InActor, uint8 CurrentState, uint8 OnDemandState, bool& bShouldProcess)
{
ATestShareRole* shareRole = `Cast<ATestShareRole>`(InActor);
if (shareRole)
{
EShareEnum stat = shareRole->GetStat();
OutState = int32(stat);
bShouldProcess = true;
return;
}
OutState =-1;
bShouldProcess = false;
}
- 这个函数可以理解为状态的起始点,这个方法决定了每个实例的具体状态枚举,从而才有动画
AnimationStates

- Processor配置的枚举对应的动画参数
AnimBlueprint
- 用于播放动画序列的简单动画蓝图,继承于AnimSharingStateInstance


- 这个ABP只负责播放动画序列,至于专门播放,可以自行扩展
- Offset参数用于控制实例动画的差异性,系统会算,对应的是NumRandomizedInstances参数
- 单例Actor会独立创建NumRandomizedInstances数量的SKMesh组件用于控制动画表现,同时设置不同的Offset参数
同一个状态设置多个动画

- 如上图,Idle设置了2个动画,同时对Run动画对应的实例数量设置了10,造成的结果是场景中大约是10比1的生成2个动画的实例,其中Run动作的偏移参数也有若干份(根据场景中的实例个数换算比例)
OnDemand(指令模式)
- 简单理解
- 如果正在播放的是普通动画,切换到指令模式的动画是瞬间切换
- 如果现在播放的是指令动画,切换到普通动画需要等指令动画播放完毕
- 如果前后都是指令动画或者普通动画,都是瞬间切换
Additve
- 基于OnDemand开启以后才能选择的模式
- 开启以后隐藏了部分Demand选项
- 开启以后上方的AdditveBlueprint可以设置
AddtiveABP


-
文档给的方案是如图的连线方式,进入此状态后StateBool就设置为true,开始叠加
-
Add的最大数量参数如果少于实例数量,部分实例就没有叠加动画

-
一些思考
-
叠加动画多数用于受伤或者姿势上面的扩展
-
叠加受伤无法用这套方案执行,甚至无法用这个插件执行,ABP无法get到控制实例的状态,下面是头文件所有申明,当然可以魔改掉插件内容
-
struct ANIMATIONSHARING_API FAdditiveAnimationInstance
{
public:
FAdditiveAnimationInstance();
void Initialise(USkeletalMeshComponent* InSkeletalMeshComponent, UClass* InAnimationBP);
void Setup(USkeletalMeshComponent* InBaseComponent, UAnimSequence* InAnimSequence);
void UpdateBaseComponent(USkeletalMeshComponent* InBaseComponent);
void Stop();
void Start();
USkeletalMeshComponent* GetComponent() const;
USkeletalMeshComponent* GetBaseComponent() const;
protected:
USkeletalMeshComponent * SkeletalMeshComponent;
UAnimSharingAdditiveInstance* AdditiveInstance;
UAnimSequence* AdditiveAnimationSequence;
USkeletalMeshComponent* BaseComponent;
bool bLoopingState;
};
-
姿势扩展比如Idle叠加一些额外动作可以使用这一套方案,但是,有必要用专门一个枚举来提供这个动作??
-
一个的方案是提供接口可以得到控制实例的状态,但是跨度有点大,可能会有更多没想到的问题
Bug/缺陷
-
Demand动画设置了大于0的BlendTime,如果在BlendTime期间切换了,Demand动画大概率出不去;怀疑是普通动画等待Demand动画结束的通知跟动画本身结束的时间点发生时间方面的错误
- 多次测试发现,2个动画(无所谓是否Demand)发生混合,要么都设置BlendTime=0,要么都设置非0切相等,则不会发生卡顿或者卡住;如果参数不一样,会发生切换的时候卡顿
-
特殊情况,demand情况下的ReturnToPreviousStat(结束以后返回之前的状态)或者SetNextState(结束以后进入下一个指定状态)有问题;参考Processor的默认代码如下
void UStatProc::ProcessActorState_Implementation(int32& OutState, AActor* InActor, uint8 CurrentState, uint8 OnDemandState, bool& bShouldProcess)
{
ATestShareRole* shareRole = `Cast<ATestShareRole>`(InActor);
if (shareRole)
{
EShareEnum stat = shareRole->GetStat();
OutState = int32(stat);
bShouldProcess = true;
return;
}
OutState =-1;
bShouldProcess = false;
}
-
解释:
-
发起点是Actor中自己维护的状态枚举,通过设置到OutState然后引起一系列的动画计算和播放,然后下一帧才会通过2个uint8反馈过来
-
如果用默认方案,ReturnToPreviousStat等方式在动画最后一帧确实把动画的状态设置了,但是第二帧又被Actor的状态给覆盖了
-
解决方案是如果是特殊情况(ReturnToPreviousStat或者SetNextState),需要反向设置Actor的状态枚举,但是默认没法得到这个特殊情况变量,而且反向设置可能会导致一系列的问题
manager里面的有一个每个骨骼的对应的Instance数据是Protected的,也没有方法可以拿到
AnimationSharingManager.h protected: //*// / Sharing data required for the unique Skeleton setups */ UPROPERTY(VisibleAnywhere, Transient, Category = AnimationSharing)
TArray<UAnimSharingInstance*>PerSkeletonData; ```这个数据里面每个UAnimaSharingInstance里有所有Setup数据
/** Actors currently registered to be animation driven by the AnimManager using this setup */ UPROPERTY(VisibleAnywhere, Transient, Category = AnimationSharing) `TArray<AActor*>` RegisteredActors; /** Per actor data, matches RegisteredActors*/ `TArray<FPerActorData>` PerActorData; /** Per component state data indexed from FPerActorData.ComponentIndices */ `TArray<FPerComponentData>` PerComponentData; /** Array of unique state data */ `TArray<FPerStateData>` PerStateData;
-