Published on

UE4骨骼共享插件AnimationSharingPlugin

Authors
  • avatar
    Name
    东哥
    Twitter

前言

从遥远的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

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到控制实例的状态,下面是头文件所有申明,当然可以魔改掉插件内容

    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;