참고 문서
책 '이득우의 언리얼 C++ 게임 개발의 정석' Chapter 12
&
서비스 노드란?
언리얼 문서에 이렇게 설명되어있다.
These attach to Composite nodes,
and will execute at their defined frequency as long as their branch is being executed.
These are often used to make checks and to update the Blackboard.
These take the place of traditional Parallel nodes in other Behavior Tree systems.
// 번역해보면
이것들은 컴포짓 노드에 부착됩니다.
분기가 실행되는 동안 정의된 시간 간격에 따라 실행됩니다.
이러한 정보는 종종 확인 및 블랙보드를 업데이트하는 데 사용됩니다.
이러한 노드는 다른 동작 트리 시스템에서 기존의 병렬 노드를 대체합니다.
노드가 실행되는 도중에 일정한 간격에 따라 실행된다.
예시)
플레이어를 쫓다가, 멀어지면 쫓지 않는다.
(태스크) 플레이어를 쫓으면서
(서비스) 0.5초마다 플레이어와의 거리를 잰다.
서비스 노드 만들기
BTService를 부모 클래스로 하는 클래스를 생성한다.
클래스명은 BTService_Detect로 했다.
이 클래스에는 플레이어가 일정 반경 내에 있으면 감지해서 추격하는 기능을 만들 것이다.
노드를 추가하는 방법은 컴포짓에서 우클릭해서 서비스 추가하면 된다.
서비스 노드 클래스
TickNode() 함수를 오버라이딩해서 구현한다.
TickNode() 함수는 속한 컴포짓 노드가 활성화되는 경우, 주기적으로 호출된다.
UCLASS()
class ARENABATTLE_API UBTService_Detect : public UBTService
{
GENERATED_BODY()
protected:
virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
};
서비스 노드 내용 구현하기
#pragma once
#include "ArenaBattle.h"
#include "BehaviorTree/BTService.h"
#include "BTService_Detect.generated.h"
UCLASS()
class ARENABATTLE_API UBTService_Detect : public UBTService
{
GENERATED_BODY()
public:
UBTService_Detect();
protected:
virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
};
#include "BTService_Detect.h"
#include "ABAIController.h"
#include "ABCharacter.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "DrawDebugHelpers.h"
UBTService_Detect::UBTService_Detect()
{
NodeName = TEXT("Detect");
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;
UWorld* World = ControllingPawn->GetWorld();
if (nullptr == World) return;
FVector Center = ControllingPawn->GetActorLocation();
float DetectRadius = 600.0f;
// 600의 반지름을 가진 구체를 만들어서 오브젝트를 감지한다.
TArray<FOverlapResult> OverlapResults;
FCollisionQueryParams CollisionQueryParam(NAME_None, false, ControllingPawn);
bool bResult = World->OverlapMultiByChannel(
OverlapResults,
Center,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel2,
FCollisionShape::MakeSphere(DetectRadius),
CollisionQueryParam
);
// 오브젝트가 감지가 되면, 그 오브젝트가 Character인지 검사한다.
if (bResult)
{
for (FOverlapResult OverlapResult : OverlapResults)
{
AABCharacter* ABCharacter = Cast<AABCharacter>(OverlapResult.GetActor());
if (ABCharacter && ABCharacter->GetController()->IsPlayerController())
{
// Character면, 블랙보드에 저장한다.
OwnerComp.GetBlackboardComponent()->SetValueAsObject(AABAIController::Key_Target, ABCharacter);
// 디버깅 용.
DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Green, false, 0.2f);
DrawDebugPoint(World, ABCharacter->GetActorLocation(), 10.0f, FColor::Blue, false, 0.2f);
DrawDebugLine(World, ControllingPawn->GetActorLocation(), ABCharacter->GetActorLocation(), FColor::Blue, false, 0.2f);
return;
}
}
}
else
{
OwnerComp.GetBlackboardComponent()->SetValueAsObject(AABAIController::Key_Target, nullptr);
}
DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Red, false, 0.2f);
}
실행 결과
1. Selector는 자식 중 하나가 성공할 때까지, 왼쪽 노드에서 오른쪽 노드로 실행한다.
2. 왼쪽 시퀀스 실행. Target이 없으므로 실패.
3. 오른쪽 시퀀스 실행. 무난히 성공.
4. 오른쪽 시퀀스 쪽의 노드가 끝이나면, 다시 1번부터 실행한다.
5. 왼쪽 시퀀스 실행. Target이 있으므로 성공. 왼쪽 노드를 실행한다.
움짤이 이상하면, 클릭해서 보면 제대로 보입니다! (크기 2.86MB)
'Unreal' 카테고리의 다른 글
[Unreal C++] Behavior Tree 5 (데코레이터 사용하기) (0) | 2021.05.12 |
---|---|
[Unreal Error] 앱 실행 도중 pure virtual 함수가 호출되고 있었습니다 (GIsRunning == 1). (6) | 2021.05.12 |
[Unreal C++] Behavior Tree 3 (태스크 사용하기) (0) | 2021.05.11 |
[Unreal C++] Behavior Tree 2 (블랙보드 값 이용하기) (0) | 2021.05.11 |
[Unreal C++] Behavior Tree 1 (비헤이비어 트리 실행하기) (5) | 2021.05.11 |