[ Patrol ]
원하는 지점을 정해서 몬스터들이 그 지점을 거치면서 순찰하도록 함.
(언리얼 에디터) C++클래스 -> 우클릭_새 C++클래스 -> 부모 클래스 : "Actor" 이름 : "PatrolPoint"
(언리얼 에디터) Monster -> 우클릭_새 블루프린트 클래스 -> 부모 클래스 : "PatrolPoint" 이름 : "BPPatrolPoint"
(언리얼 에디터) BPPatrolPoint 배치
(언리얼 에디터) BPSpawnPoint -> 디테일-Spawn Point-Patrol Point Array : PatrolPoint를 지정할 만큼 추가 -> 스포이드를 사용해서 순서에 맞게 PatrolPoint를 연결
(언리얼 에디터-BTMonster) Selector -> Sequence 연결
(언리얼 에디터-BTMonster) Sequence -> 우클릭_데코레이터 : "Blackboard" 추가
(언리얼 에디터-BTMonster) Blackboard -> 디테일-Blackboard-Key Query : "Is Not Set"
(언리얼 에디터-BTMonster) Blackboard -> 디테일-Description-Node Name : "Target"
(언리얼 에디터-BTMonster) Blackboard "Target" -> Wait 연결
(언리얼 에디터-BTMonster) Wait -> 디테일-Wait-Wait Time : "2.0"
(언리얼 에디터-BBMonster) 새 키 -> Object "PatrolPoint""
(언리얼 에디터-BBMonster) Object-PatrolPoint -> 블랙보드 디테일-Base Class : "PatrolPoint"
(언리얼 에디터) C++클래스 -> 우클릭_새 C++클래스 -> 부모 클래스 : "BTTaskNode" 이름 : "BTTask_Patrol"
(언리얼 에디터-BTMonster) Blackboard "Target" -> Patrol 연결(Wait보다 왼쪽에 배치)
(언리얼 에디터) C++클래스 -> 우클릭_새 C++클래스 -> 부모 클래스 : "BTDecorator" 이름 : "BTDecorator_PatrolEnable"
(언리얼 에디터-BTMonster) Sequence-Blackboard "Target" -> 디테일-Flow Control-Notify Observer : "On Value Change"
// On Value Change : 블랙보드의 값이 바뀔 때마다
// On Result Change : 결과값이 바뀔 때마다
(언리얼 에디터-BTMonster) Battle-Blackboard "Target" -> 디테일-Flow Control-관찰자 중단 : "Self"
(언리얼 에디터-BTMonster) Sequence-Blackboard "Target" -> 디테일-Flow Control-관찰자 중단 : "Self"
(언리얼 에디터-BPMinion) Mesh -> 디테일-콜리전-Collision Presets : "NoCollision"
(언리얼 에디터) 세팅-프로젝트 세팅 -> 엔진-콜리전 -> Preset-Player : Camera : "무시"
(언리얼 에디터) 세팅-프로젝트 세팅 -> 엔진-콜리전 -> Preset-Monster : Camera : "무시"
(언리얼 에디터) 세팅-프로젝트 세팅 -> 엔진-콜리전 -> Preset-Player Attack : Visibility, Camera : "무시"
(언리얼 에디터-BPWukong) Mesh -> 디테일-콜리전-Collision Presets : "NoCollision"
(언리얼 에디터-BPWukong) Weapon -> 디테일-콜리전-Can Character Step Up On : "No"
(언리얼 에디터-BPWukong) Weapon -> 디테일-콜리전-Collision Presets : "Custom"
(C++-Monster.h)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "SpawnPoint.h"
#include "GameFramework/Character.h"
#include "Monster.generated.h"
DECLARE_MULTICAST_DELEGATE(FOnAttackEndDelegate)
UCLASS()
class UE7PROJECT_API AMonster : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMonster();
~AMonster();
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
ASpawnPoint* SpawnPoint;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
float fTraceRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
float fAttackDist;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
TArray<APatrolPoint*> PatrolPointArray; // 패트롤 포인트 담을 공간.
int32 iMovePoint;
int32 iMovePointDir; // 숫자를 1씩 증가시키거나 감소시키기 위해.
bool bAttack;
FOnAttackEndDelegate OnAttackEnd;
TArray<FDelegateHandle> AttackEndHandle;
public:
void AddPatrolPoint(APatrolPoint* point);
int32 GetPatrolPointCount() const
{
return PatrolPointArray.Num();
}
APatrolPoint* GetPatrolPoint()
{
FString strText = FString::Printf(TEXT("%s MovePoint : %d"), *GetName(), iMovePoint);
PrintViewport(0.5f, FColor::Green, strText);
return PatrolPointArray[iMovePoint];
}
void NextPatrolPoint()
{
iMovePoint += iMovePointDir;
if (iMovePoint == PatrolPointArray.Num())
{
iMovePointDir = -1;
iMovePoint = PatrolPointArray.Num() - 2;
}
else if (iMovePoint == -1)
{
iMovePointDir = 1;
iMovePoint = 0;
}
}
public:
template <typename T>
void AddAttackEndDelegate(T* pObj, void(T::* pFunc)())
{
FDelegateHandle handle = OnAttackEnd.AddUObject(pObj, pFunc);
AttackEndHandle.Add(handle);
}
public:
float GetTraceRange() const
{
return fTraceRange;
}
float GetAttackDistance() const
{
return fAttackDist;
}
void Attack()
{
bAttack = true;
}
bool IsAttack() const
{
return bAttack;
}
public:
void SetSpawnPoint(ASpawnPoint* Point)
{
SpawnPoint = Point;
}
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
public:
virtual void ChangeAnim(EMonsterAnim eAnim);
public:
void AttackEnd();
};
(C++-Monster.cpp)
// Fill out your copyright notice in the Description page of Project Settings.
#include "Monster.h"
// Sets default values
AMonster::AMonster()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SpawnPoint = nullptr;
bAttack = false;
GetCapsuleComponent()->SetCollisionProfileName(TEXT("Monster"));
GetMesh()->SetReceivesDecals(false);
fTraceRange = 1000.f;
fAttackDist = 200.f;
iMovePoint = 0;
iMovePointDir = 1;
}
AMonster::~AMonster()
{
if (SpawnPoint)
SpawnPoint->Respawn();
//OnAttackEnd.Clear();
//AttackEndHandle.Reset(0);
}
void AMonster::AddPatrolPoint(APatrolPoint* point)
{
PatrolPointArray.Add(point);
}
// Called when the game starts or when spawned
void AMonster::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMonster::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AMonster::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
void AMonster::ChangeAnim(EMonsterAnim eAnim)
{
}
void AMonster::AttackEnd()
{
bAttack = false;
OnAttackEnd.Broadcast();
for (FDelegateHandle& handle : AttackEndHandle)
{
OnAttackEnd.Remove(handle);
}
}
(C++-PatrolPoint.h)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameInfo.h"
#include "GameFramework/Actor.h"
#include "PatrolPoint.generated.h"
UCLASS()
class UE7PROJECT_API APatrolPoint : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
APatrolPoint();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
(C++-PatrolPoint.cpp)
// Fill out your copyright notice in the Description page of Project Settings.
#include "PatrolPoint.h"
// Sets default values
APatrolPoint::APatrolPoint()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void APatrolPoint::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void APatrolPoint::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
(C++-SpawnPoint.h)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameInfo.h"
#include "PatrolPoint.h"
#include "GameFramework/Actor.h"
#include "SpawnPoint.generated.h"
UCLASS()
class UE7PROJECT_API ASpawnPoint : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASpawnPoint();
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
TSubclassOf<class AMonster> SpawnType;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
float SpawnTime;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
bool bInfinity;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
TArray<APatrolPoint*> PatrolPointArray;
...
(C++-SpawnPoint.cpp)
// Fill out your copyright notice in the Description page of Project Settings.
#include "SpawnPoint.h"
#include "Monster.h"
...
// Called every frame
void ASpawnPoint::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (IsValid(SpawnType))
{
SpawnDuration += DeltaTime;
if (SpawnDuration >= SpawnTime)
{
SpawnDuration = 0.f;
if (!bInfinity)
{
if (bSpawnEnable)
{
bSpawnEnable = false;
FActorSpawnParameters params;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
SpawnMonster = GetWorld()->SpawnActor<AMonster>(SpawnType, GetActorLocation(), GetActorRotation(), params);
SpawnMonster->SetSpawnPoint(this);
for (APatrolPoint* point : PatrolPointArray)
{
SpawnMonster->AddPatrolPoint(point); // 몬스터가 패트롤 포인터를 가지고 있도록 추가.
}
}
}
else
{
FActorSpawnParameters params;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
SpawnMonster = GetWorld()->SpawnActor<AMonster>(SpawnType, GetActorLocation(), GetActorRotation(), params);
SpawnMonster->SetSpawnPoint(this);
for (APatrolPoint* point : PatrolPointArray)
{
SpawnMonster->AddPatrolPoint(point); // 몬스터가 패트롤 포인터를 가지고 있도록 추가.
}
}
}
}
}
(C++-BTTask_Patrol.h)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameInfo.h"
#include "BehaviorTree/BTTaskNode.h"
#include "BTTask_Patrol.generated.h"
/**
*
*/
UCLASS()
class UE7PROJECT_API UBTTask_Patrol : public UBTTaskNode
{
GENERATED_BODY()
public:
UBTTask_Patrol();
protected:
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory);
virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds);
};
(C++-BTTask_Patrol.cpp)
// Fill out your copyright notice in the Description page of Project Settings.
#include "BTTask_Patrol.h"
#include "MonsterAIController.h"
#include "Monster.h"
UBTTask_Patrol::UBTTask_Patrol()
{
NodeName = TEXT("Patrol");
bNotifyTick = true;
}
EBTNodeResult::Type UBTTask_Patrol::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
EBTNodeResult::Type eResult = Super::ExecuteTask(OwnerComp, NodeMemory);
return EBTNodeResult::InProgress;
}
void UBTTask_Patrol::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
Super::TickTask(OwnerComp, NodeMemory, DeltaSeconds);
AMonsterAIController* pController = Cast<AMonsterAIController>(OwnerComp.GetAIOwner());
AMonster* pMonster = Cast<AMonster>(pController->GetPawn());
// 몬스터가 사망시 이동을 멈춤.
// 몬스터가 살아있다면 이동을 시작.
FVector vMonsterLoc = pMonster->GetActorLocation();
FVector vPatrolPointLoc = pMonster->GetPatrolPoint()->GetActorLocation();
vPatrolPointLoc.Z = vMonsterLoc.Z;
pController->MoveToActor(pMonster->GetPatrolPoint(), -1.f, false, true);
// 몬스터가 해당 위치에 도착했는지 판단.
// UAIBlueprintHelperLibrary::SimpleMoveToLocation(pController, vPatrolPointLoc);
MonsterLoc.Z = 0.f;
vPatrolPointLoc.Z = 0.f;
float fDist = FVector::Distance(vMonsterLoc, vPatrolPointLoc);
LOG(TEXT("%s Dist : %.5f"), *pMonster->GetName(), fDist);
if (fDist <= 5.f)
{
pMonster->ChangeAnim(EMonsterAnim::Idle);
pController->StopMovement();
LOG(TEXT("%s 도착"), *pMonster->GetName());
pMonster->NextPatrolPoint();
FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
}
}
(C++-BTDecorator_PatrolEnable.h)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameInfo.h"
#include "BehaviorTree/BTDecorator.h"
#include "BTDecorator_PatrolEnable.generated.h"
/**
*
*/
UCLASS()
class UE7PROJECT_API UBTDecorator_PatrolEnable : public UBTDecorator
{
GENERATED_BODY()
public:
UBTDecorator_PatrolEnable();
protected:
virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const;
};
(C++-BTDecorator_PatrolEnable.cpp)
// Fill out your copyright notice in the Description page of Project Settings.
#include "BTDecorator_PatrolEnable.h"
#include "MonsterAIController.h"
#include "Monster.h"
UBTDecorator_PatrolEnable::UBTDecorator_PatrolEnable()
{
NodeName = TEXT("PatrolEnable");
}
bool UBTDecorator_PatrolEnable::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
bool bResult = Super::CalculateRawConditionValue(OwnerComp, NodeMemory);
AMonsterAIController* pController = Cast<AMonsterAIController>(OwnerComp.GetAIOwner());
if (!IsValid(pController))
return false;
AMonster* pMonster = Cast<AMonster>(pController->GetPawn());
if (!IsValid(pMonster))
return false;
if (pMonster->GetPatrolPointCount() < 2) // 패트롤 포인트가 2개이상이 아닐 경우
return false;
return true;
}
'Study > Unreal' 카테고리의 다른 글
[Unreal/정리] HPBar (0) | 2020.11.04 |
---|---|
[Unreal/정리] 몬스터 상태 정보 (0) | 2020.11.03 |
[Unreal/정리] 행동트리 (0) | 2020.10.26 |
[Unreal/정리] 랜드 스케이프 레이어 (0) | 2020.10.23 |
[Unreal/정리] 리타겟팅 (0) | 2020.10.21 |
댓글