【UE4 C++ 】动画与行为树相关模块初步使用
注意添加模块 "AIModule", "GamePlayTasks", "NavigationSystem"
动画资源 UAnimationAsset
类层级结构
- UObjectBase
- UObjectBaseUtility
- UObject
- UAnimationAsset
- UAnimSequenceBase
- UAnimCompositeBase
- UAnimComposite 动画合成
- UAnimMontage 动画蒙太奇
- UAnimSequence 动画序列 (注意 root motion)
- UAnimStreamable
- UAnimCompositeBase
- UBlendSpaceBase
- UBlendSpace 混合空间
- UAimOffsetBlendSpace 瞄准偏移混合空间
- UBlendSpace1D 一维混合空间
- UAimOffsetBlendSpace1D 一维瞄准偏移混合空间
- UBlendSpace 混合空间
- UPoseAsset
- UAnimSequenceBase
- UAnimationAsset
- UObject
- UObjectBaseUtility
C++ 调用
UAnimationAsset
-
加载的是如动画序列、动画蒙太奇等派生类,可直接播放
// 加载 UAnimationAsset* AnimDead_I; AnimDead_I = LoadObject<UAnimationAsset>(nullptr, TEXT("AnimSequence'/Game/Res/PolygonAdventure/Mannequin/Enemy/Animation/FightGroup/Enemy_Dead_I.Enemy_Dead_I'")); // 加载 构造函数使用 UAnimationAsset* DeadAnim ; static ConstructorHelpers::FObjectFinder<UAnimationAsset> StaticDeadAnim(TEXT("AnimSequence'/Game/Res/PolygonAdventure/Mannequin/Player/Animation/Player_Death.Player_Death'")); DeadAnim = StaticDeadAnim.Object; // 播放,可以保留最后一帧 GetMesh()->PlayAnimation(DeadAnim, false); // 获取时长 AnimDead_I->GetMaxCurrentTime()
UAnimSequence
-
可直接播放
-
返回的时长可用于行为树等待时间
-
返回的 transform 可以用于 Modify bone,如果有需要的话
-
不设置循环播放,可以定格在最后一帧
// 加载 static ConstructorHelpers::FObjectFinder<UAnimSequence> StaticAnimAttackSeq_III(TEXT("AnimSequence'/Game/Res/PolygonAdventure/Mannequin/Enemy/Animation/FightGroup/Enemy_Attack_III.Enemy_Attack_III'")); UAnimSequence* AnimAttackSeq_III; AnimAttackSeq_III = StaticAnimAttackSeq_III.Object; // 获取时长 AnimAttackSeq_III->GetPlayLength() // 获取 transform AnimAttackSeq_III->GetBoneTransform(OutputTrans, 0.f, CurrentPlayTime, true);
UAnimMontage
-
可以通过slot,实现动画融合
-
如果需要的话,需要设置使其定格最后一帧;或者在属性设置里不勾选 "enable auto blend out"
-
可配合 对应 UAnimSequence 的长度用于行为树等待时间
-
可在每帧进行判断后播放;或者想要调用时播放,例如characer、UBTTaskNode
//播放 Montage_Play(AnimHurt); // 判断是否播放 Montage_IsPlaying(AnimHurt) //停止蒙太奇 void Montage_Stop(float InBlendOutTime, const UAnimMontage* Montage = NULL); Montage_Stop(0); // 停止所有蒙太奇 // 与定格最后一帧相关的几种方式 GetMesh()->GlobalAnimRateScale = 0.f; GetMesh()->bNoSkeletonUpdate = true; GetMesh()->bPauseAnims = true;
动画实例UAnimInstance 与 动画蓝图UAnimBlueprint
本文地址 https://www.cnblogs.com/shiroe/p/15634220.html
简述
- 一般创建动画蓝图时需要选择 UAnimInstance 的Parent Class
- 因此创建 UAnimInstance 后,在其内部写各种变量和方法,再通过动画蓝图可以实现C++与蓝图之间的通信。比如变量用于动画状态机,事件用于动画通知的调用
- 刚开始使用 Montage 时,一般会在 character 里调用,后来发现其实也可以放进 UAnimInstance 里面使用
C++ 调用
-
重写 NativeUpdateAnimation 函数,类似Tick
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
-
加载动画蓝图
// 加载与设置 static ConstructorHelpers::FClassFinder<UAnimInstance> StaticEnemyAnim(TEXT("AnimBlueprint'/Game/Blueprints/Enemy/BPA_Eneymy.BPA_Eneymy_C'")); // GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint); GetMesh()->SetAnimClass(StaticEnemyAnim.Class); // character 获取**UAnimInstance** SEAnim = Cast<UEnemyAnim>(GetMesh()->GetAnimInstance());
行为树相关模块
-
UBTService 可用于数据更新
-
UBTDecorator
-
UBlackboardData 黑板数据,Editor下可以使用 Data Asset 继承之
-
可重载
virtual void PostLoad() override;
-
添加数据,几种类型
//等待时长 FBlackboardEntry WaitTime; WaitTime.EntryName = FName(TEXT("WaitTime")); UBlackboardKeyType_Float* WaitTimeKeyType = NewObject<UBlackboardKeyType_Float>(); WaitTime.KeyType = WaitTimeKeyType; Keys.Add(WaitTime); //敌人攻击类型 FBlackboardEntry AttackType; AttackType.EntryName = FName(TEXT("AttackType")); UBlackboardKeyType_Enum* EnemyAttackTypeKeyType = NewObject<UBlackboardKeyType_Enum>(); // 通过反射获取枚举类型 EnemyAttackTypeKeyType->EnumType = FindObject<UEnum>(ANY_PACKAGE, *FString("EEnemyAttackType"), true); EnemyAttackTypeKeyType->EnumName = FString("EEnemyAttackType"); AttackType.KeyType = EnemyAttackTypeKeyType; Keys.Add(AttackType); // 玩家指针 FBlackboardEntry PlayerPawn; PlayerPawn.EntryName = FName(TEXT("PlayerPawn")); UBlackboardKeyType_Object* PlayerPawnKeyType = NewObject<UBlackboardKeyType_Object>(); PlayerPawnKeyType->BaseClass = ASLAiPlayerCharacter::StaticClass(); PlayerPawn.KeyType = PlayerPawnKeyType; Keys.Add(PlayerPawn); //某一个动作是否完成 FBlackboardEntry ProcessFinish; ProcessFinish.EntryName = FName(TEXT("ProcessFinish")); ProcessFinish.KeyType = NewObject<UBlackboardKeyType_Bool>(); Keys.Add(ProcessFinish);
-
-
UBTTaskNode
-
可重写函数
// 重写执行函数 virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; //重写终止函数 virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; // 设置要关联的黑板数据 UPROPERTY(EditAnywhere,Category="Blackboard") struct FBlackboardKeySelector WaitTime; UPROPERTY(EditAnywhere,Category="Blackboard") struct FBlackboardKeySelector Destination; UPROPERTY(EditAnywhere,Category="Blackboard") struct FBlackboardKeySelector IsActionFinish;
-
ExecuteTask
- 可通过参数 OwnerComp获取 character 和 controller,进而访问相关函数或数据
- 可更改黑板数据
SEController = Cast<AEnemyController>(OwnerComp.GetAIOwner()); SECharacter = Cast<AEnemyCharacter>(SEController->GetPawn()); // 使用导航系统获取随机点 UNavigationSystemV1::K2_GetRandomReachablePointInRadius(SEController, WanderOrigin,DesLoc, WanderRadius); OwnerComp.GetBlackboardComponent()->SetValueAsVector(Destination.SelectedKeyName, DesLoc); // 播放动画,返回时长 float AttackDuration = SECharacter->PlayAttackAction(EEnemyAttackType::EA_Normal); OwnerComp.GetBlackboardComponent()->SetValueAsFloat(WaitTime.SelectedKeyName, AttackDuration);
-
AbortTask
- 一般用于收尾工作,比如清除定时器
-
返回值 EBTNodeResult
- 根据返回值,让行为树继续执行子节点或者跳出返回父节点
// 返回值 return EBTNodeResult::Failed; return EBTNodeResult::Succeeded; return EBTNodeResult::Aborted;
-
作者:砥才人
出处:https://www.cnblogs.com/shiroe
本系列文章为笔者整理原创,只发表在博客园上,欢迎分享本文链接,如需转载,请注明出处!