BTTaskNode 클래스를 상속받는 PatrolClass 생성 및 초기화
BTTaskNode를 C++프로젝트에서 사용하기위해서는 3가지 모듈을 Build.cs에서 추가해야한다.
"NavigationSystem", "AIModule", "GameplayTasks"
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG", "NavigationSystem", "AIModule", "GameplayTasks" });
블랙보드에서 정의한 키값의 접근을 가독성을 위해서 전처리기로 정의하는편이 좋다.
#define BBKEY_HOMEPOS TEXT("HomePos")
#define BBKEY_PATROLPOS TEXT("PatrolPos")
#define BBKEY_TARGET TEXT("Target")
블랙보드 키값 초기화
Blackboard->SetValueAsVector(BBKEY_HOMEPOS, GetPawn()->GetActorLocation());
PatrolClass 구현
#include "AI/UBTTask_Patrol.h"
#include "ABAI.h"
#include "AIController.h"
#include "NavigationSystem.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Interface/CharacterAIInterface.h" //상위 레이어 접근
UBTTask_Patrol::UBTTask_Patrol()
{
}
//오버라이딩 함수
EBTNodeResult::Type UBTTask_Patrol::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory);
//컨트롤러의 폰 레퍼런스 캐싱
APawn* ControllingPawn = OwnerComp.GetAIOwner()->GetPawn();
if (nullptr == ControllingPawn)
{
return EBTNodeResult::Failed;
}
//네비시스템 버전업으로 바뀐 기능 언리얼 5.3에서도 정상동작한다.
UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetNavigationSystem(ControllingPawn->GetWorld());
if (nullptr == NavSystem)
{
return EBTNodeResult::Failed;
}
//상위레이어의 캐릭터를 간접접근하기위해 인터페이스 사용
//Ai 인터페이스의 역할은 각기 다른 폰의 이동범위의 호출
ICharacterAIInterface* AIPawn = Cast<IABCharacterAIInterface>(ControllingPawn);
if (nullptr == AIPawn)
{
return EBTNodeResult::Failed;
}
FVector Origin = OwnerComp.GetBlackboardComponent()->GetValueAsVector(BBKEY_HOMEPOS);
float PatrolRadius = AIPawn->GetAIPatrolRadius();
//이동 목적지를 저장할 지역변수 선언
FNavLocation NextPatrolPos;
//인자값(라디어스의 중점 기준,범위길이,랜덤한 위치 데이터)
//home에 저장된값이 Origin으로 중점이된다,인터페이스에서 구한 폰의 이동범위값, 저장
if (NavSystem->GetRandomPointInNavigableRadius(Origin, PatrolRadius, NextPatrolPos))
{
//초기화된 목적지 위치값을 블랙보드 키값에 저장
OwnerComp.GetBlackboardComponent()->SetValueAsVector(BBKEY_PATROLPOS, NextPatrolPos.Location);
return EBTNodeResult::Succeeded;
}
return EBTNodeResult::Failed;
}
비헤비어 트리 enter -> 마우스 우클릭 -> tasks -> Patrol테스크 생성
BT 서비스 클래스
정찰중에 플레이어를 감지할수있는 기능을 구현
Detecting을 상시적으로 체크하기위해서는 서비스 노드로 구현해야한다.
서비스노드 내부 구조
서비스는 TickNode함수가 존재하며
Interval(주기)를 가지고 TickNode를 호출한다
BTService_Detect
#include "AI/BTService_Detect.h"
#include "ABAI.h"
#include "AIController.h"
#include "Interface/ABCharacterAIInterface.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Physics/ABCollision.h"
#include "DrawDebugHelpers.h"
UBTService_Detect::UBTService_Detect()
{
//비헤비어내에서 탐색되는 이름
NodeName = TEXT("Detect");
//Tick노드 호출주기
Interval = 1.0f;
}
void UBTService_Detect::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);
//컨트롤러폰 캐싱
APawn* ControllingPawn = OwnerComp.GetAIOwner()->GetPawn();
if (nullptr == ControllingPawn)
{
return;
}
//기준값을 구하기위해 폰 위치값 저장
FVector Center = ControllingPawn->GetActorLocation();
//폰을 포함시키는 월드의 레퍼런스 캐싱
UWorld* World = ControllingPawn->GetWorld();
if (nullptr == World)
{
return;
}
//Ai 인터페이스
IABCharacterAIInterface* AIPawn = Cast<IABCharacterAIInterface>(ControllingPawn);
if (nullptr == AIPawn)
{
return;
}
//감지범위 구하기
float DetectRadius = AIPawn->GetAIDetectRange();
//#### 충돌감지 #####
//충돌된 대상의 정보'들'을 저장할 변수
TArray<FOverlapResult> OverlapResults;
FCollisionQueryParams CollisionQueryParam(SCENE_QUERY_STAT(Detect), false, ControllingPawn);
//bResult는 충돌이 감지되면 true를 반환
bool bResult = World->OverlapMultiByChannel(
OverlapResults, //충돌된 대상의 정보'들'을 정의할 변수
Center,//충돌구체 기준값
FQuat::Identity,
CCHANNEL_ABACTION,//적용할 체널액션
FCollisionShape::MakeSphere(DetectRadius), //구체를 DetectRadius를 반지름 기준으로 생성
CollisionQueryParam
);
if (bResult)
{
for (auto const& OverlapResult : OverlapResults)
{
//충돌 대상 폰으로 캐스팅 및 레퍼런스 캐싱
APawn* Pawn = Cast<APawn>(OverlapResult.GetActor());
//플레이어 인지 확인
if (Pawn && Pawn->GetController()->IsPlayerController())
{
//블랙보드 타겟을 플레이로 초기화
OwnerComp.GetBlackboardComponent()->SetValueAsObject(BBKEY_TARGET, Pawn);
//디버깅용 구체 그리기
DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Green, false, 0.2f);
//플레이어 기준으로 점 그리기
DrawDebugPoint(World, Pawn->GetActorLocation(), 10.0f, FColor::Green, false, 0.2f);
//컨트롤대상(몬스터)에서 부터 플레이어까지 선그리기
DrawDebugLine(World, ControllingPawn->GetActorLocation(), Pawn->GetActorLocation(), FColor::Green, false, 0.27f);
return;
}
}
}
//리턴이 안됬다면 플레이어 탐지 실패 타겟을 null로 정의
OwnerComp.GetBlackboardComponent()->SetValueAsObject(BBKEY_TARGET, nullptr);
//플레이어 탐지 범위를 빨간색으로 그리기
DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Red, false, 0.2f);
}
비헤비어 트리 디자인
비헤비어트리 Enter -> 대상 시퀀스 마우스 우클릭 ->Add Service -> Detect추가
중단자 사용법
비헤비어트리 Enter -> 대상 컴포짓 클릭 -> 디테일 최상단 확인
On Value Change
On Result Change | 조건이 변경될 때만 재평가합니다. |
On Value Change | 관찰된 블랙보드 키가 변경될 때만 재평가합니다. |
Observer aborts
None | 아무것도 중단하지 않습니다. |
Self | 자신 및 이 노드 아래에서 실행 중인 모든 서브트리를 중단합니다. |
Lower Priority | 이 노드의 오른쪽에 있는 모든 노드를 중단합니다. |
Both | 자신, 이 노드 아래에서 실행 중인 모든 서브트리, 이 노드의 오른쪽에 있는 모든 노드를 중단합니다. |
플레이어 바라보기
float TurnSpeed = AIPawn->GetAITurnSpeed();
//몬스터에서 플레이어를 향하는 벡터
FVector LookVector = TargetPawn->GetActorLocation() - ControllingPawn->GetActorLocation();
//벡터에서 필요없는 높이값 0으로 초기화
LookVector.Z = 0.0f;
//플레이어를 향하는 로테이터 생성 바라보는 기준은 X(정면)값이다.
FRotator TargetRot = FRotationMatrix::MakeFromX(LookVector).Rotator();
//턴시간을 기준으로 스무스하게 회전
ControllingPawn->SetActorRotation(FMath::RInterpTo(ControllingPawn->GetActorRotation(), TargetRot, GetWorld()->GetDeltaSeconds(), TurnSpeed));
'언리얼5 > 공부기록' 카테고리의 다른 글
UE 어빌리티 시스템 1_1 (정리) ASC , GA TAG (2) | 2024.03.29 |
---|---|
UE 콜리전 사용법 정리 (0) | 2024.03.27 |
UE 액터와 컴포넌트 이해 (0) | 2024.03.26 |
UE Ai Behavior Tree 기본용어 (공부 정리) (0) | 2024.03.24 |
UE 게임 데이터 관리 (0) | 2024.03.24 |