UE4骨骼共享插件AnimationSharingPlugin
前言
从遥远的2019年搬运而来, 作为记录, 不确定是否有更新
- 引用文档的结构图
- 插件已经启用,如果关闭到插件栏启用
Setup文件
- 简单配置以后如下图
- 一个SkeltonSetup数据在UAnimationSharingManager::Register方法里会生成一个UObject<UAnimationSharingInstance*>,这个UObj会spawn一个场景里的单例Actor,同时存放所有实例对象以及对应的所有参数;这个Actor会根据上图中AnimationStates的数据量创建若干USkeletalMeshComponent维护动画
AnimationBlurprintForBlending(图中的ABP_Trans)
- 这个蓝图简单理解为用于动画切换,owner就是那个Actor单例
- 逻辑很简单,直接拷贝就可以
StateProcessorClass
必须配置一个枚举,对应所有状态,否则无法配置动画(AnimationStates数据)
每隔场景中的实例都会拥有一个对应的Object,一直在Tick得到这个实例的状态,主要逻辑如下
1 | void UStatProc::ProcessActorState_Implementation(int32& OutState, AActor* InActor, uint8 CurrentState, uint8 OnDemandState, bool& bShouldProcess) |
- 这个函数可以理解为状态的起始点,这个方法决定了每个实例的具体状态枚举,从而才有动画
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到控制实例的状态,下面是头文件所有申明,当然可以魔改掉插件内容
1 | struct ANIMATIONSHARING_API FAdditiveAnimationInstance |
姿势扩展比如Idle叠加一些额外动作可以使用这一套方案,但是,有必要用专门一个枚举来提供这个动作??
一个的方案是提供接口可以得到控制实例的状态,但是跨度有点大,可能会有更多没想到的问题
Bug/缺陷
Demand动画设置了大于0的BlendTime,如果在BlendTime期间切换了,Demand动画大概率出不去;怀疑是普通动画等待Demand动画结束的通知跟动画本身结束的时间点发生时间方面的错误
- 多次测试发现,2个动画(无所谓是否Demand)发生混合,要么都设置BlendTime=0,要么都设置非0切相等,则不会发生卡顿或者卡住;如果参数不一样,会发生切换的时候卡顿
特殊情况,demand情况下的ReturnToPreviousStat(结束以后返回之前的状态)或者SetNextState(结束以后进入下一个指定状态)有问题;参考Processor的默认代码如下
1 | void UStatProc::ProcessActorState_Implementation(int32& OutState, AActor* InActor, uint8 CurrentState, uint8 OnDemandState, bool& bShouldProcess) |
解释:
发起点是Actor中自己维护的状态枚举,通过设置到OutState然后引起一系列的动画计算和播放,然后下一帧才会通过2个uint8反馈过来
如果用默认方案,ReturnToPreviousStat等方式在动画最后一帧确实把动画的状态设置了,但是第二帧又被Actor的状态给覆盖了
解决方案是如果是特殊情况(ReturnToPreviousStat或者SetNextState),需要反向设置Actor的状态枚举,但是默认没法得到这个特殊情况变量,而且反向设置可能会导致一系列的问题
manager里面的有一个每个骨骼的对应的Instance数据是Protected的,也没有方法可以拿到
1
2
3
4
5
6AnimationSharingManager.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;