🎮 UBTT_ActivateAbility.h
UCLASS() class BOSSFIGHT_API UBTT_ActivateAbility : public UBTTaskNode { GENERATED_BODY() private: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Value", meta = (AllowPrivateAccess = "true")) FGameplayTagContainer TagContainer; UBTT_ActivateAbility(); virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override; virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override; };🎮 UBTT_ActivateAbility.cpp
UBTT_ActivateAbility::UBTT_ActivateAbility() { NodeName = "Activte Ability"; bNotifyTick = true; } EBTNodeResult::Type UBTT_ActivateAbility::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) { Super::ExecuteTask(OwnerComp, NodeMemory); ABFBaseCharacter* Character = Cast<ABFBaseCharacter>(OwnerComp.GetAIOwner()->GetCharacter()); if (UBFAbilitySystemComponent* ASC = Character->GetBFAbilitySystemComponent()) { ASC->TryActivateAbilitiesByTag(TagContainer); return EBTNodeResult::InProgress; } else { return EBTNodeResult::Failed; } } void UBTT_ActivateAbility::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) { Super::TickTask(OwnerComp, NodeMemory, DeltaSeconds); AEnemyAIController* AI = Cast<AEnemyAIController>(OwnerComp.GetAIOwner()); if (AI->GetBlackboardComponent()->GetValueAsEnum(AI->AIState) == uint8(EBFEnemyAIState::Idle)) { FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded); return; } }위와같이 UBTTaskNode를 상속 받아 ActiavteAbilityTask를 작성 하였다.
기본적으로 AbilitySystemComponent를 통해 Ability를 작동 시키고, Task를 InProgress상태로 유지하여 Ability작동 후 해당 Ability가 종료 될때까지 대기 시키도록 구현 하였다. 그후 TickTask()를 통해 AIState를 확인하여 Idle상태로 돌아오면, Succeeded를 반환 하여 성공적으로 Task를 종료 하도록 구현 하였다.
// Enemy Tags BOSSFIGHT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Ability_Attack); BOSSFIGHT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Ability_Attack_1); BOSSFIGHT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Ability_Attack_2); BOSSFIGHT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Ability_Attack_3);// Enemy Tags UE_DEFINE_GAMEPLAY_TAG(Enemy_Ability_Attack, "Enemy.Ability.Attack"); UE_DEFINE_GAMEPLAY_TAG(Enemy_Ability_Attack_1, "Enemy.Ability.Attack.1"); UE_DEFINE_GAMEPLAY_TAG(Enemy_Ability_Attack_2, "Enemy.Ability.Attack.2"); UE_DEFINE_GAMEPLAY_TAG(Enemy_Ability_Attack_3, "Enemy.Ability.Attack.3");우선 공격에 해당하는 Tag만 정의 해주었다.
🎮 UBFEnemyAbility.h
UCLASS() class BOSSFIGHT_API UBFEnemyAbility : public UBFGameplayAbility { GENERATED_BODY() // 중략 AEnemyAIController* GetAIControllerFromActorInfo(); UFUNCTION(BlueprintCallable, Category = "BossFight | Ability", meta = (ExpandEnumAsExecs = "OutConfirmType")) void ChangeAIState(EBFEnemyAIState AIState, EBFConfirmType& OutConfirmType); private: TWeakObjectPtr<AEnemyAIController> CachedEnemyAIController; };🎮 UBFEnemyAbility.cpp
AEnemyAIController* UBFEnemyAbility::GetAIControllerFromActorInfo() { if (!CachedEnemyCharacter.IsValid()) { CachedEnemyCharacter = Cast<ABFEnemyCharacter>(CurrentActorInfo->AvatarActor); } if (!CachedEnemyAIController.IsValid()) { CachedEnemyAIController = Cast<AEnemyAIController>(CachedEnemyCharacter->GetController()); } return CachedEnemyAIController.IsValid() ? CachedEnemyAIController.Get() : nullptr; } void UBFEnemyAbility::ChangeAIState(EBFEnemyAIState AIState, EBFConfirmType& OutConfirmType) { if (AEnemyAIController* AI = GetAIControllerFromActorInfo()) { AI->GetBlackboardComponent()->SetValueAsEnum(AI->AIState, uint8(AIState)); EBFConfirmType::Yes; } else { EBFConfirmType::No; } }GetAIControllerFromActorInfo()함수를 통해 AIController를 저장 및 반환 하고 ChangeAIState()함수를 통해 입력된 AIState로 현재 AIState를 변경하도록 코드를 구현 하였다.
또한 EBFConfirmType을 통해 AEnemyAIController가 유효하여 값의 변경에 성공하면 Yes를 반환 아니면 No를 반환하여, 흐름을 제어 할수 있도록 하였다.


