본문 바로가기
Study/Unreal

[Unreal/정리] Patrol

by generous_jeans 2020. 11. 2.

[ 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

댓글