NPC가 정찰 중에 플레이어를 발견하면 플레이러를 추격하도록 구현
- 플레이어 정보를 블랙보드에 저장하도록 Object 타입으로 Target 변수를 생성
- Object 타입에서는 기반 클래스를 ABCharacter로 지정
- 행동패턴은 플레이어를 발견했는지, 못했는지에 따라 추격과 정찰로 구분한다.
- 그래서 셀렉터로 확장한다.
- 추격과 정찰 중 추격에 더 우선권을 주고, 추격로직은 Target을 행헤 이동하도록 설계한다
- 그리고 BTService를 부모로하는 BTService_Detect 클래스를 생성
- 행동트리 서비스노드는 자신이 속한 컴포짓 노드가 활성화 될 경우 주기적으로 TickNOde 함수를 호출한다. 호출주기는 노드 내부에 설정된 Interval 속성 값으로 지정 할 수 있다.
- TickNode 함수에는 NPC의 위치를 기준으로 반경 6미터 내에 캐릭터가 있는지 감지하는 기능을 넣는다. 반경 내에 모든 캐릭터를 감지하는 OverlapMultiByChannel 함수를 사용한다.
- 감지된 모든 캐릭터 정보는 목록을 관리하는 데 적압한 TArry로 전달된다.
- 만든 후에 Selecter 에 Detect를 선택해 컴포짓에 부착한다
//h
class ARENABATTLE_API UBTService_Detect : public UBTService
{
GENERATED_BODY()
public:
UBTService_Detect();
protected:
virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
};
//cpp
#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();
FVector Center = ControllingPawn->GetActorLocation();
float DetectRadius = 600.0f;
if (nullptr == World) return;
TArray<FOverlapResult> OverlapResults;
FCollisionQueryParams CollisionQueryParam(NAME_None, false, ControllingPawn);
bool bResult = World->OverlapMultiByChannel(
OverlapResults,
Center,
FQuat::Identity,
ECollisionChannel::ECC_EngineTraceChannel2,
FCollisionShape::MakeSphere(DetectRadius),
CollisionQueryParam
);
DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Red, false, 0.2f);
}
- NPC가 탐지 영역 내의 캐릭터를 감지한다면, 그중에서 우리가 조종하는 캐릭터를 추려내야 한다.
- 캐릭터를 조종하는 컨트롤러가 플레이어 컨트롤러인지 파악할 수 있도록 IsPlayerController 함수를 사용한다
- 플레이어가 감지되면 Target 값을 플레이러로 지정하고 아니면 nullptr로 지정한다
- 감지하면 녹색으로 구체를 그리고 NPC와 연결된 선을 추가로 그린다
ABAIController
//h
static const FName TargetKey;
//cpp
const FName AABAIController::TargetKey(TEXT("Target"));
//UBTService_Detect.cpp
void UBTService_Detect::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);
bool bResult = World->OverlapMultiByChannel(
OverlapResults,
Center,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel2,
FCollisionShape::MakeSphere(DetectRadius),
CollisionQueryParam
);
if (bResult)
{
for (auto OverlapResult : OverlapResults)
{
AABCharacter* ABCharacter = Cast<AABCharacter>(OverlapResult.GetActor());
if (ABCharacter && ABCharacter->GetController()->IsPlayerController())
{
OwnerComp.GetBlackboardComponent()->SetValueAsObject(AABAIController::TargetKey, 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.27f);
return;
}
}
}
else
{
OwnerComp.GetBlackboardComponent()->SetValueAsObject(AABAIController::TargetKey, nullptr);
}
DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Red, false, 0.2f);
- 그런데 이동할 때 회전이 부자연 스럽게 꺽인다. 이를 보강하기 위해 NPC를 위한 ControlMode를 추가 * NPC 이동 방향에 따라 회전하도록 캐릭터 무브먼트 설정을 변경해본다
//h
class ARENABATTLE_API AABCharacter : public ACharacter
{
public:
virtual void PossessedBy(AController* NewController) override;
}
//cpp
void AABCharacter::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
if (IsPlayerControlled())
{
SetControlMode(EControlMode::DIABLO);
GetCharacterMovement()->MaxWalkSpeed = 600.0f;
}
else
{
SetControlMode(EControlMode::NPC);
GetCharacterMovement()->MaxWalkSpeed = 300.0f;
}
}
void AABCharacter::SetControlMode(EControlMode NewControlMode)
{
CurrentControlMode = NewControlMode;
switch (CurrentControlMode)
{
case EControlMode::NPC:
bUseControllerRotationYaw = false;
GetCharacterMovement()->bUseControllerDesiredRotation = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.0f, 480.0f, 0.0f);
break;
}
}
- 이제 서비스가 실행된 결과에 따라 셀렉터 데코레이더 왼쪽의 추격과 셀렉터 데코레이터 오른쪽의 정찰 로직이 나눠지도록 행동트리 로직을 구성한다
- 서비스 결과는 블랙보드의 Target 키에 값이 있는지, 없는지로 구분할 수 있다.
- 그래서 데코레이터 노드를 사용한다.
- 해당 키값의 변경이 감지되면 현재 컴포짓 노드의 실행을 곧바로 취소하고 노티파이 옵저버 값알 OnValue Change로 변경한다.
'Unreal > Game 1 (C++)' 카테고리의 다른 글
20. 프로젝트이 설정과 무한 맵의 제작 1 (트리거+모듈) (0) | 2019.05.08 |
---|---|
19. AI 컨트롤러와 행동트리 4 (공격) (0) | 2019.05.08 |
19. AI 컨트롤러와 행동트리 2 (정찰) + BTTask_ (1) | 2019.05.08 |
19. AI 컨트롤러와 행동트리 1 + 네비시스템 (0) | 2019.05.08 |
18.UI와 데이터의 연동 (1) | 2019.05.08 |