UE4骨骼共享插件AnimationSharingPlugin

前言

从遥远的2019年搬运而来, 作为记录, 不确定是否有更新

官方文档

  • 引用文档的结构图

Alt text

  • 插件已经启用,如果关闭到插件栏启用

Setup文件

Alt text

Alt text

  • 简单配置以后如下图

image-20191223145213446

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

AnimationBlurprintForBlending(图中的ABP_Trans)

image-20191223145517613

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

StateProcessorClass

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

  • image-20191223150355965

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

image-20191223150033324

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

image-20191223150551638

  • Processor配置的枚举对应的动画参数

AnimBlueprint

  • 用于播放动画序列的简单动画蓝图,继承于AnimSharingStateInstance

image-20191223150729515

image-20191223150819957

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

同一个状态设置多个动画

image-20191223152554826

  • 如上图,Idle设置了2个动画,同时对Run动画对应的实例数量设置了10,造成的结果是场景中大约是10比1的生成2个动画的实例,其中Run动作的偏移参数也有若干份(根据场景中的实例个数换算比例)

OnDemand(指令模式)

  • 简单理解
    • 如果正在播放的是普通动画,切换到指令模式的动画是瞬间切换
    • 如果现在播放的是指令动画,切换到普通动画需要等指令动画播放完毕
    • 如果前后都是指令动画或者普通动画,都是瞬间切换

Additve

  • 基于OnDemand开启以后才能选择的模式
  • 开启以后隐藏了部分Demand选项
  • 开启以后上方的AdditveBlueprint可以设置

AddtiveABP

image-20191223160102804

image-20191223160046344

  • 文档给的方案是如图的连线方式,进入此状态后StateBool就设置为true,开始叠加

  • Add的最大数量参数如果少于实例数量,部分实例就没有叠加动画image-20191223160951498

  • 一些思考

    • 叠加动画多数用于受伤或者姿势上面的扩展

    • 叠加受伤无法用这套方案执行,甚至无法用这个插件执行,ABP无法get到控制实例的状态,下面是头文件所有申明,当然可以魔改掉插件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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的默认代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的,也没有方法可以拿到

      1
      2
      3
      4
      5
      6
      AnimationSharingManager.h
      protected:
      //***//
      /** Sharing data required for the unique Skeleton setups */
      UPROPERTY(VisibleAnywhere, Transient, Category = AnimationSharing)
      TArray<UAnimSharingInstance*> PerSkeletonData;

      这个数据里面每个UAnimaSharingInstance里有所有Setup数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      /** 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;