[UE] Behavior Tree: 워프(Warp), EQS
궁수 AI를 만들어 Player를 향해 화살을 쏘아 날리는 것을 구현하였다. 게임의 재미를 더하기 위해 궁수 AI 회피를 추가할 것이다. 적이 궁수AI에 접근하여 일정거리에 도달하면 궁수 AI는 워프 스킬을 시전하여 달아날 것이다. EQS를 사용하여 적의 위치의 뒷공간 지정범위 내에서 랜덤으로 워프 이동할 것이다.
목차
Plugins |
||||
Weapon |
||||
Resource |
||||
Icon128.png weapon_thumbnail_icon.png |
||||
Source |
||||
Weapon | ||||
SWeaponCheckBoxes.h .cpp SWeaponDetailsView.h .cpp SWeaponDoActionData.h .cpp SWeaponEquipmentData.h .cpp SWeaponHitData.h .cpp SWeaponLeftArea.h .cpp Weapon.Build.cs WeaponAssetEditor.h .cpp WeaponAssetFactory.h .cpp WeaponCommand.h .cpp WeaponContextMenu.h .cpp WeaponModule.h .cpp WeaponStyle.h .cpp |
||||
Source | ||
BehaviorTree | ||
CBTService_Melee.h .cpp CBTService_Range.h .cpp CBTTaskNode_Action.h .cpp CBTTaskNode_Equip.h .cpp CBTTaskNode_Hitted.h .cpp CBTTaskNode_Patrol.h .cpp CBTTaskNode_Speed.h .cpp CEnvQueryContext_Target.h .cpp CPatrol.h .cpp |
||
Characters | ||
CAIController.h .cpp CAnimInstance.h .cpp CEnemy.h .cpp CEnemy_AI.h.cpp CPlayer.h.cpp ICharacter.h .cpp |
||
Components | ||
CAIBehaviorComponent.h .cpp CFeetComponent.h .cpp CMontagesComponent.h .cpp CMovementComponent.h .cpp CStateComponent.h .cpp CStatusComponent.h .cpp CWeaponComponent.h.cpp CZoomComponent.h .cpp |
||
Notifies | ||
CAnimNotifyState_BeginAction.h .cpp CAnimNotifyState_BowString.h .cpp CAnimNotify_CameraShake.h .cpp CAnimNotify_End_Parkour.h .cpp CAnimNotifyState_EndAction.h .cpp CAnimNotify_EndState.h .cpp CAnimNotifyState.h .cpp CAnimNotifyState_CameraAnim.h .cpp CAnimNotifyState_Collision.h .cpp CAnimNotifyState_Combo.h .cpp CAnimNotifyState_Equip.h .cpp CAnimNotifyState_SubAction.h .cpp |
||
Parkour | ||
CParkourComponent.h .cpp | ||
Utilities | ||
CHelper.h CLog.h .cpp |
||
Weapons | ||
CArrow.h .cpp CAura.h .cpp CCamerModifier.h .cpp CGhostTrail.h .cpp CRotate_Object.h .cpp CThornObject.h .cpp CAnimInstance_Bow.h .cpp CAttachment_Bow.h .cpp CDoAction_Around.h .cpp CDoAction_Bow.h .cpp CDoAction_Combo.h .cpp CDoAction_Warp.h .cpp CSubAction_Around.h .cpp CSubAction_Bow.h .cpp CSubAction_Fist.h .cpp CSubAction_Hammer.h .cpp CSubAction_Sword.h .cpp CDoAction_Warp.h .cpp CAttachment.h .cpp CDoAction.h .cpp CEquipment.h .cpp CSubAction.h .cpp CWeaponAsset.h .cpp CWeaponData.h .cpp CWeaponStructures.h .cpp |
||
Global.h CGameMode.h .cpp U2212_06.Build.cs |
||
U2212_06.uproject | ||
워프(Warp) 구현하기
CBTTaskNode_Equip
CBTTaskNode_Equip.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "Components/CWeaponComponent.h"
#include "CBTTaskNode_Equip.generated.h"
UCLASS()
class U2212_06_API UCBTTaskNode_Equip : public UBTTaskNode
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, Category = "Type")
EWeaponType Type;
public:
UCBTTaskNode_Equip();
protected:
EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
};
CBTTaskNode_Equip.cpp
더보기
#include "BehaviorTree/CBTTaskNode_Equip.h"
#include "Global.h"
#include "Characters/CEnemy_AI.h"
#include "Characters/CAIController.h"
#include "Components/CStateComponent.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);//weapon이 없다면 EBTNodeResult::Failed 리턴
if (Type == weapon->GetWeaponType())//장착될 무기 == 현재 장착된 무기
return EBTNodeResult::Succeeded;//새로 장착시킬 필요 없으니 바로 Succeeded를 리턴
switch (Type)
{
case EWeaponType::Sword: weapon->SetSwordMode(); break;//Sword 장착
case EWeaponType::Bow: weapon->SetBowMode(); break;//Bow 장착
case EWeaponType::Warp: weapon->SetWarpMode(); break;//Warp 장착
}
return EBTNodeResult::InProgress;//장착 동작이 나올 수 있도록 InProgress 리턴
}
void UCBTTaskNode_Equip::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
Super::TickTask(OwnerComp, NodeMemory, DeltaSeconds);
ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner());
ACEnemy_AI* ai = Cast<ACEnemy_AI>(controller->GetPawn());
UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(ai);//weapon을 가져온다.
const bool* bEquipped = weapon->GetEquipment()->GetEquipped();
UCStateComponent* state = CHelpers::GetComponent<UCStateComponent>(ai);
if(state->IsIdleMode() && *bEquipped)
{
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);//Succeeded로 끝낸다. 리턴이 없는 Tick 같은 곳에서 사용한다.
return;
}
}
EBTNodeResult::Type UCBTTaskNode_Equip::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)//무기가 없다면
return EBTNodeResult::Failed;//실패 리턴
bool bBeginEquip = weapon->GetEquipment()->GetBeginEquip();//GetBeginEquip()으로 CEquipment의 bBeginEquip를 넣어준다. bBeginEquip=true면 Equip이 시작되었다는 의미다.
if (bBeginEquip == false)
weapon->GetEquipment()->Begin_Equip();
weapon->GetEquipment()->End_Equip();
return EBTNodeResult::Aborted;//Aborted는 취소 마감, 위(Root)로 올라감. Succeeded는 성공종료.
}
무기 장착에 Warp 추가
- EBTNodeResult::Type UCBTTaskNode_Equip::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
- switch (Type)
{
case EWeaponType::Sword: weapon->SetSwordMode(); break;
case EWeaponType::Bow: weapon->SetBowMode(); break;
case EWeaponType::Warp: weapon->SetWarpMode(); break;
}
- switch (Type)
CAIBehaviorComponent
CAIBehaviorComponent.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CAIBehaviorComponent.generated.h"
UENUM(BlueprintType)
enum class EAIStateType : uint8
{
Wait = 0, Approach, Action, Patrol, Hitted, Avoid, Dead, Max,
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAIStateTypeChanged, EAIStateType, InPrevType, EAIStateType, InNewType);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class U2212_06_API UCAIBehaviorComponent : public UActorComponent
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, Category = "Key")
FName AIStateTypeKey = "AIState";
UPROPERTY(EditAnywhere, Category = "Key")
FName TargetKey = "Target";
UPROPERTY(EditAnywhere, Category = "Key")
FName PatrolLocationKey = "Patrol_Location";
UPROPERTY(EditAnywhere, Category = "Key")
FName AvoidLocationKey = "Avoid_Location";
private:
EAIStateType GetType();
public:
bool IsWaitMode();
bool IsApproachMode();
bool IsActionMode();
bool IsPatrolMode();
bool IsHittedMode();
bool IsAvoidMode();
bool IsDeadMode();
public:
UCAIBehaviorComponent();
protected:
virtual void BeginPlay() override;
public:
FORCEINLINE void SetBlackboard(class UBlackboardComponent* InBlackboard) { Blackboard = InBlackboard; }
public:
class ACharacter* GetTarget();
public:
FVector GetPatrolLocation();
void SetPatrolLocation(const FVector& InLocation);
public:
FVector GetAvoidLocation();
public:
void SetWaitMode();
void SetApproachMode();
void SetActionMode();
void SetPatrolMode();
void SetHittedMode();
void SetAvoidMode();
void SetDeadMode();
private:
void ChangeType(EAIStateType InType);
public:
FAIStateTypeChanged OnAIStateTypeChanged;
private:
class UBlackboardComponent* Blackboard;
};
변수 추가
- UPROPERTY(EditAnywhere, Category = "Key")
FName AvoidLocationKey = "Avoid_Location";
함수 추가
- FVector GetAvoidLocation();
CAIBehaviorComponent.cpp
더보기
#include "Components/CAIBehaviorComponent.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "BehaviorTree/BlackboardComponent.h"
UCAIBehaviorComponent::UCAIBehaviorComponent()
{
}
void UCAIBehaviorComponent::BeginPlay()
{
Super::BeginPlay();
}
EAIStateType UCAIBehaviorComponent::GetType()
{
return (EAIStateType)Blackboard->GetValueAsEnum(AIStateTypeKey);
}
bool UCAIBehaviorComponent::IsWaitMode()
{
return GetType() == EAIStateType::Wait;
}
bool UCAIBehaviorComponent::IsApproachMode()
{
return GetType() == EAIStateType::Approach;
}
bool UCAIBehaviorComponent::IsActionMode()
{
return GetType() == EAIStateType::Action;
}
bool UCAIBehaviorComponent::IsPatrolMode()
{
return GetType() == EAIStateType::Patrol;
}
bool UCAIBehaviorComponent::IsHittedMode()
{
return GetType() == EAIStateType::Hitted;
}
bool UCAIBehaviorComponent::IsAvoidMode()
{
return GetType() == EAIStateType::Avoid;
}
bool UCAIBehaviorComponent::IsDeadMode()
{
return GetType() == EAIStateType::Dead;
}
ACharacter* UCAIBehaviorComponent::GetTarget()
{
//Blackboard 내의 TargetKey를 리턴해준다.
return Cast<ACharacter>(Blackboard->GetValueAsObject(TargetKey));
}
FVector UCAIBehaviorComponent::GetPatrolLocation()
{
return Blackboard->GetValueAsVector(PatrolLocationKey);
}
void UCAIBehaviorComponent::SetPatrolLocation(const FVector& InLocation)
{
Blackboard->SetValueAsVector(PatrolLocationKey, InLocation);
}
FVector UCAIBehaviorComponent::GetAvoidLocation()
{
return Blackboard->GetValueAsVector(AvoidLocationKey);
}
void UCAIBehaviorComponent::SetWaitMode()
{
ChangeType(EAIStateType::Wait);
}
void UCAIBehaviorComponent::SetApproachMode()
{
ChangeType(EAIStateType::Approach);
}
void UCAIBehaviorComponent::SetActionMode()
{
ChangeType(EAIStateType::Action);
}
void UCAIBehaviorComponent::SetPatrolMode()
{
ChangeType(EAIStateType::Patrol);
}
void UCAIBehaviorComponent::SetHittedMode()
{
ChangeType(EAIStateType::Hitted);
}
void UCAIBehaviorComponent::SetAvoidMode()
{
ChangeType(EAIStateType::Avoid);
}
void UCAIBehaviorComponent::SetDeadMode()
{
ChangeType(EAIStateType::Dead);
}
void UCAIBehaviorComponent::ChangeType(EAIStateType InType)
{
EAIStateType prevType = GetType();
Blackboard->SetValueAsEnum(AIStateTypeKey, (uint8)InType);
if (OnAIStateTypeChanged.IsBound())
OnAIStateTypeChanged.Broadcast(prevType, InType);
}
함수 정의
- FVector UCAIBehaviorComponent::GetAvoidLocation()
- return Blackboard->GetValueAsVector(AvoidLocationKey);
CDo_Action_Warp
CDo_Action_Warp.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CDoAction.h"
#include "CDoAction_Warp.generated.h"
UCLASS(Blueprintable)
class U2212_06_API UCDoAction_Warp : public UCDoAction
{
GENERATED_BODY()
public:
UCDoAction_Warp();
virtual void BeginPlay
(
class ACAttachment* InAttachment,
class UCEquipment* InEquipment,
class ACharacter* InOwner,
const TArray<FDoActionData>& InDoActionData,
const TArray<FHitData>& InHitData
);
void Tick(float InDeltaTime) override;
public:
void DoAction() override;
void Begin_DoAction() override;
private:
bool GetCursorLocationAndRotation(FVector& OutLocation, FRotator& OutRotation);
private:
class APlayerController* PlayerController;
class UDecalComponent* Decal;
class UCAIBehaviorComponent* Behavior;//UCAIBehaviorComponent(=Behavior 변수) 유무에 따라 Enemy인지 Player인지 구분할 수 있다.
private:
FVector MoveToLocation;
};
변수 추가
- class UCAIBehaviorComponent* Behavior;
- UCAIBehaviorComponent(=Behavior 변수) 유무에 따라 Enemy인지 Player인지 구분할 수 있다.
CDo_Action_Warp.cpp
더보기
#include "Weapons/DoActions/CDoAction_Warp.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "GameFramework/PlayerController.h"
#include "Components/CStateComponent.h"
#include "Components/DecalComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/CAIBehaviorComponent.h"
#include "Weapons/CAttachment.h"
UCDoAction_Warp::UCDoAction_Warp()
{
}
void UCDoAction_Warp::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionData, const TArray<FHitData>& InHitData)
{
Super::BeginPlay(InAttachment, InEquipment, InOwner, InDoActionData, InHitData);
Decal = CHelpers::GetComponent<UDecalComponent>(InAttachment);
PlayerController = OwnerCharacter->GetController<APlayerController>();
Behavior = CHelpers::GetComponent<UCAIBehaviorComponent>(InOwner);
}
void UCDoAction_Warp::Tick(float InDeltaTime)
{
Super::Tick(InDeltaTime);
FVector location = FVector::ZeroVector;
FRotator rotation = FRotator::ZeroRotator;
//GetCursorLocationAndRotation이 false면 hit가 안 된 것이다.
if (GetCursorLocationAndRotation(location, rotation) == false)
{
Decal->SetVisibility(false);
return;
}
//Warp 실행 중에는 Cursor가 움직이지 않도록 만든다.
if (bInAction)//Action이 실행중이라면 실행을 할 필요가 없으므로
return;//리턴
Decal->SetVisibility(true);
//Decal이 그려지는 위치와 회전값을 설정해준다.
Decal->SetWorldLocation(location);
Decal->SetWorldRotation(rotation);
}
void UCDoAction_Warp::DoAction()
{
CheckFalse(DoActionDatas.Num() > 0);
CheckFalse(State->IsIdleMode());
Super::DoAction();
FRotator rotation;
if (GetCursorLocationAndRotation(MoveToLocation, rotation))
{
//땅에 뭍히는것을 방지하기 위해 CapsuleHalfHeight만큼 높이를 보정해준다.
float height = OwnerCharacter->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
MoveToLocation = FVector(MoveToLocation.X, MoveToLocation.Y, MoveToLocation.Z + height);
float yaw = UKismetMathLibrary::FindLookAtRotation(OwnerCharacter->GetActorLocation(), MoveToLocation).Yaw;
OwnerCharacter->SetActorRotation(FRotator(0, yaw, 0));
}
DoActionDatas[0].DoAction(OwnerCharacter);
}
void UCDoAction_Warp::Begin_DoAction()
{
Super::Begin_DoAction();
//Player
if (!!PlayerController)
{
OwnerCharacter->SetActorLocation(MoveToLocation);//DoAction에서 설정한 MoveToLocation 위치로 이동한다.
MoveToLocation = FVector::ZeroVector;//이동 후에 MoveToLocation 위치를 ZeroVector로 초기화해준다.
return;//밑의 Enemy AI 기준의 Warp를 실행하지 않기 위해 리턴.
}
//Enemy AI
CheckNull(Behavior);
OwnerCharacter->SetActorLocation(Behavior->GetAvoidLocation());
}
bool UCDoAction_Warp::GetCursorLocationAndRotation(FVector& OutLocation, FRotator& OutRotation)
{
CheckNullResult(PlayerController, false);//PlayerController가 없으면 실행하면 안 되니 체크해준다.
FHitResult hitResult;
PlayerController->GetHitResultUnderCursorByChannel(ETraceTypeQuery::TraceTypeQuery1, false, hitResult);
CheckFalseResult(hitResult.bBlockingHit, false);//부딪히는게 없다면 false 리턴.
OutLocation = hitResult.Location;//hit된 위치
OutRotation = hitResult.ImpactNormal.Rotation();//hit된 Normal의 회전값
return true;
}
헤더 추가
- #include "Components/CAIBehaviorComponent.h"
void UCDoAction_Warp::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionData, const TArray<FHitData>& InHitData)
- Behavior = CHelpers::GetComponent<UCAIBehaviorComponent>(InOwner);
- UCAIBehaviorComponent* Behavior 생성
void UCDoAction_Warp::Begin_DoAction()
- CheckNull(Behavior);
- Behavior를 가지고 있으면 Enemy라는 간주할 수 있다.
- OwnerCharacter->SetActorLocation(Behavior->GetAvoidLocation());
- CAIBehaviorComponent.h.cpp에 만든 GetAvoidLocation() 함수를 호출하여 Blackboard->GetValueAsVector(AvoidLocationKey);를 가져온다. (=Avoid_Location을 가져온다).
BP_CEnemy_Range
Weapon
- Data Asset
- DA_Warp 추가
BT_Range
실행화면
'⭐ Unreal Engine > UE RPG AI' 카테고리의 다른 글
[UE] Behavior Tree: 활 조준 및 발사하기 (0) | 2023.08.08 |
---|---|
[UE] Behavior Tree: 활 Enemy 생성, Player 바라보기 (0) | 2023.08.07 |
[UE] Behavior Tree: 무기 장착 Abort (0) | 2023.08.01 |
[UE] Behavior Tree: 피격(Hitted) (0) | 2023.07.31 |
[UE] Behavior Tree: 무기 장착 및 공격 (0) | 2023.07.27 |
댓글
이 글 공유하기
다른 글
-
[UE] Behavior Tree: 활 조준 및 발사하기
[UE] Behavior Tree: 활 조준 및 발사하기
2023.08.08 -
[UE] Behavior Tree: 활 Enemy 생성, Player 바라보기
[UE] Behavior Tree: 활 Enemy 생성, Player 바라보기
2023.08.07 -
[UE] Behavior Tree: 무기 장착 Abort
[UE] Behavior Tree: 무기 장착 Abort
2023.08.01 -
[UE] Behavior Tree: 피격(Hitted)
[UE] Behavior Tree: 피격(Hitted)
2023.07.31