초보 코린이의 성장 일지

UE4 Behavior, AI Abort, Range 본문

언리얼

UE4 Behavior, AI Abort, Range

코오린이 2023. 8. 7. 17:11

Enemy가 공격하려고 할때, Player가 먼저 공격을 했을 경우 Enemy에 공격모드가 사라져버리는게 아닌, Hit후 일정시간이 지나면 바로 Enemy가 공격 행동을 할 수 있도록 구현해볼 것이다. 간격은 Behavior Tree에 Wait로 1초정도로 줄 것이기 때문에 빠르게 공격하게끔 만들어질 것이다.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Weapons/CWeaponStructures.h"
#include "CDoAction.generated.h"

UCLASS(Abstract) // 객체화 되면 안되므로 Abstract
class U2212_06_API UCDoAction : public UObject
{
	GENERATED_BODY()

public:
	FORCEINLINE bool GetBeginAction() { return bBeginAction; }

protected:
	bool bBeginAction; // 액션에 들어갔는지,

};

1. DoAction에서 bBeginAction을 사용할 수 있도록 함수로 만들어서 넘겨준다.


#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "CBTTaskNode_Action.generated.h"

UCLASS()
class U2212_06_API UCBTTaskNode_Action : public UBTTaskNode
{
	GENERATED_BODY()
    
public:
	UCBTTaskNode_Action();

protected:
	EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& Owner0Comp, uint8* NodeMemory) override;

};
#include "BehaviorTree/CBTTaskNode_Action.h"
#include "Global.h"
#include "Characters/CEnemy_AI.h"
#include "Characters/CAIController.h"
#include "Components/CStateComponent.h"
#include "Components/CWeaponComponent.h"
#include "Weapons/CDoAction.h"

UCBTTaskNode_Action::UCBTTaskNode_Action()
{
	NodeName = "Action";

	bNotifyTick = true;

}

EBTNodeResult::Type UCBTTaskNode_Action::AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
	Super::AbortTask(OwnerComp, NodeMemory);
	
	ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner());
	ACEnemy_AI* ai = Cast<ACEnemy_AI>(controller->GetPawn());

	UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(ai);
	if (weapon == nullptr) // weapon이 없다면
		return EBTNodeResult::Failed; // 이 조건은 실패조건이므로, Failed

	bool bBeginAction = weapon->GetDoAction()->GetBeginAction();
	if (bBeginAction == false) // 액션에 들어온게 아니라면,
		weapon->GetDoAction()->Begin_DoAction(); // 액션 실행

	weapon->GetDoAction()->End_DoAction(); // End로 마무리

	return EBTNodeResult::Succeeded; // Hit된 후 일정시간이 지난후 공격할수 있도록 해주기 위해 Succeeded
}

1. Action상태 인지 아닌지를 판단해주고, 조건이 일치하게 동작을 나오게 만들어준다.

2. Hit된 후에도 공격을 하게끔 만들어주기 위해 Succeeded로 끝내준다.


이번에는 원거리 공격을 하는 Enemy를 만들어 볼 것이다. 한가지 중요한점은 맵을 확장해서 작업을 진행하게 되는데, 네비매쉬가 살짝 겹치게 되어있다. 이때 AI들을 다른 매쉬까지 인지하고 침범을 하게되는데, 나중에 이 메쉬를 나눠서 사용할 수 있도록 만들어 보겠다.

1. 체크된 4개만 복사해서 Range로 이름을 변경해주고 사용할 것이다.

1. 블랙보드 Range로 변경.

1. Behavior Tree와 Controller Class도 Range로 변경.

1. Perception 감지 거리, 감지 잃는 거리, 감지할 각도를 수정해준다.

1. Range를 확인할 수 있도록, 맵도 더 넓혀준다.


1. Behavior Tree에 Wait만 남겨준다. 우선 감지를 하는지 Debug해서 확인해 보기 위해서이다.

1. 감지가 잘 되는걸 확인할 수 있다.


1. Service_Range를 만들어 준다.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTService.h"
#include "CBTService_Range.generated.h"

UCLASS()
class U2212_06_API UCBTService_Range : public UBTService
{
	GENERATED_BODY()

private:
	UPROPERTY(EditAnywhere, Category = "Action")
		float AvoidRange = 500; // 탐지 되었을때 공격 범위,

	UPROPERTY(EditAnywhere, Category = "Action")
		bool bDrawDebug;

public:
	UCBTService_Range();

protected:
	void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;

};
#include "BehaviorTree/CBTService_Range.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Characters/CAIController.h"
#include "Characters/CEnemy_AI.h"
#include "Components/CStateComponent.h"
#include "Components/CAIBehaviorComponent.h"

UCBTService_Range::UCBTService_Range()
{
	NodeName = "Range";

	Interval = 0.1f; // Tick 호출 간격
	RandomDeviation = 0.0f;

}

void UCBTService_Range::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
	Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);

	// 실행에 주체는 ACAIController가 주축이 된다.
	ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner());
	ACEnemy_AI* ai = Cast<ACEnemy_AI>(controller->GetPawn());
	UCStateComponent* state = CHelpers::GetComponent<UCStateComponent>(ai);
	UCAIBehaviorComponent* aiState = CHelpers::GetComponent<UCAIBehaviorComponent>(ai);

	if (bDrawDebug)
	{
		// 25 낮추고 아래에서 50을 올리므로, 높이가 50 나오게된다.
		FVector start = ai->GetActorLocation();
		start.Z -= 25; 

		FVector end = start;
		end.Z += 50;

		DrawDebugCylinder(ai->GetWorld(), start, end, AvoidRange, 10, FColor::Red, true, Interval);
	}

	// Hitted 되었을때,
	if (state->IsHittedMode())
	{
		aiState->SetHittedMode();

		return;
	}

	ACharacter* target = aiState->GetTarget();

	if (target == nullptr) // 감지할 Target이 없다면,
	{
		// 바라보는 Focus를 Gameplay모드에서 제거.
		controller->ClearFocus(EAIFocusPriority::Gameplay);
		aiState->SetWaitMode(); // 대기로 돌려주기.

		return;
	}

	controller->SetFocus(target); // 감지된 Target에게 Focus.

	float distance = ai->GetDistanceTo(target);
	if (distance < AvoidRange) // Target과 거리가 AvoidRange 보다 작다면,
	{
		aiState->SetAvoidMode(); // 회피모드로 변경.

		return;
	}

	aiState->SetActionMode(); // 감지가 되었는데 AvoidMode가 아니라면 공격모드,

}

1. Debug를 눈으로 볼 수 있도록 설정.

2. Target과 거리를 판단하여 회피 모드인지, 공격 모드인지 판단.


Range 설정.
Equip Bow로 설정.

1. Range 범위가 설정되었고,  Equip Bow로 설정하여 Bow를 뽑도록 만들 것이다.


1. Weapon에서 Bow DataAsset를 넣어준다.


#include "BehaviorTree/CBTTaskNode_Equip.h"
#include "Global.h"
#include "Characters/CEnemy_AI.h"
#include "Characters/CAIController.h"
#include "Components/CStateComponent.h"
#include "Components/CStatusComponent.h"
#include "Weapons/CEquipment.h"

UCBTTaskNode_Equip::UCBTTaskNode_Equip()
{
	NodeName = "Equip";

	bNotifyTick = true;
}

EBTNodeResult::Type UCBTTaskNode_Equip::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
	Super::ExecuteTask(OwnerComp, NodeMemory);

	ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner());
	ACEnemy_AI* ai = Cast<ACEnemy_AI>(controller->GetPawn());

	UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(ai);
	CheckNullResult(weapon, EBTNodeResult::Failed); // 무기가 없다면 실행 x

	// 현재 장착하고있는 무기와, 장착할 무기가 같다면 이미 성공이므로,
	if (Type == weapon->GetWeaponType()) 
		return EBTNodeResult::Succeeded;

	controller->StopMovement();
	switch (Type)
	{
		case EWeaponType::Sword: weapon->SetSwordMode(); break;
		case EWeaponType::Bow: weapon->SetBowMode(); break;

	}

	return EBTNodeResult::InProgress; // 조건이 성공했다면, 대기
}

1. EquipTask로 가서 SetBowMode를 추가해준다.

2. 이제 Enemy가 Target를 감지하게되면 Bow를 장착할 것이다.


https://www.youtube.com/watch?v=LO24eHNsTnw 

 

'언리얼' 카테고리의 다른 글

UE4 Behavior, AI Avoid EQS  (0) 2023.08.09
UE4 Behavior, AI Range, EQS  (0) 2023.08.08
UE4 Behavior, AI Equip  (0) 2023.08.03
UE4 Behavior, AI Hitted  (0) 2023.07.31
UE4 Behavior, AI Action  (2) 2023.07.27
Comments