본문 바로가기
Study/Unreal

[Unreal/정리] 몬스터 상태 정보

by generous_jeans 2020. 11. 3.

[ 몬스터 상태 정보 ]

- 몬스터 상태 정보 구조체 생성 - 

(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)

USTRUCT(Atomic, BlueprintType)
struct FMonsterState
{
    GENERATED_USTRUCT_BODY()

public:
    FMonsterState()
    {
    }

public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    float fAttack;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    float fArmor;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 iHP;
   
   UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 iHPMax;
   
   UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 iMP;
   
   UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 iMPMax;
};

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;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    FMonsterState MonsterState;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    FName MonsterName;
    
    int32 iMovePoint;
    int32 iMovePointDir;

    bool bAttack;

    FOnAttackEndDelegate OnAttackEnd;
    TArray<FDelegateHandle>	AttackEndHandle;

public:
    void AddPatrolPoint(APatrolPoint* point);
    
    int32 GetPatrolPointCount()	const
    {
        return PatrolPointArray.Num();
    }

    APatrolPoint* GetPatrolPoint()
    {
        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();
    
};

- GameInstance 생성 - 

(언리얼 에디터) C++클래스 -> 우클릭_새 C++클래스 -> 부모 클래스 : "GameInstance" 이름 : "UE7GameInstance"

 

(C++-GameInfo.h)

#pragma once

#include "EngineMinimal.h"
#include "EngineGlobals.h"
#include "DestructibleComponent.h"
#include "Engine.h"
#include "Engine/AssetManager.h"
#include "Runtime/Engine/Classes/Engine/GameEngine.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "NavigationSystem/Public/NavigationPath.h"
#include "Engine/DataTable.h"  // 추가.
...

(C++-UE7GameInstance.h)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameInfo.h"
#include "Engine/GameInstance.h"
#include "UE7GameInstance.generated.h"

USTRUCT(BlueprintType)
struct FMonsterInfo :
    public FTableRowBase
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    float Attack;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    float Armor;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 HP;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 HPMax;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 MP;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    int32 iMPMax;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    float TraceRange;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
    float AttackRange;
};


UCLASS()
class UE7PROJECT_API UUE7GameInstance : public UGameInstance
{
    GENERATED_BODY()
	
public:
    UUE7GameInstance();
    ~UUE7GameInstance();

private:
    UPROPERTY()
    UDataTable* MonsterInfoTable;

protected:
    virtual void Init();

public:
    const FMonsterInfo* FindMonsterInfo(const FName& key) const;
    
};

(C++-UE7GameInstance.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "UE7GameInstance.h"

UUE7GameInstance::UUE7GameInstance()
{
    static ConstructorHelpers::FObjectFinder<UDataTable> MonsterInfoAsset(TEXT("DataTable'/Game/Monster/MonsterInfo.MonsterInfo'"));

    if (MonsterInfoAsset.Succeeded())
        MonsterInfoTable = MonsterInfoAsset.Object;
}

UUE7GameInstance::~UUE7GameInstance()
{
}

void UUE7GameInstance::Init()
{
    Super::Init();
}

const FMonsterInfo* UUE7GameInstance::FindMonsterInfo(const FName& key)	const
{
    if (!IsValid(MonsterInfoTable))
        return nullptr;

    	return MonsterInfoTable->FindRow<FMonsterInfo>(key, TEXT(""));
}

(언리얼 에디터) 세팅-프로젝트 세팅 -> 프로젝트-맵&모드-게임 인스턴스 클래스 : "UE7GameInstance"

// 게임 인스턴스 클래스 : GameInstance  : 프로젝트 세팅에 들어와있는 클래스로 프로젝트에서 한 개만 생성이 됨.

 

- 데이터 테이블 -

// 언리얼에서 데이터 테이블 생성. 

(언리얼 에디터) Monster -> 우클릭_기타-데이터 테이블 -> 행 구조체 : "MonsterInfo" 이름 : "MonsterInfoTable"

(언리얼 에디터-MonsterInfoTable) 추가 -> 행이름 및 세부 정보 설정

// 설정된 정보는 구조체에 들어가게 됨. 

 

// 엑셀 파일을 사용해서 생성. 

(언리얼 에디터) MonsterInfoTable -> 우클릭_CSV로 익스포트 -> 원하는 위치에 생성

// 생성된 엑셀 파일을 수정해서 정보를 넣을 수 있음. 

 

(언리얼 에디터) 생성한 엑셀 파일을 언리얼 에디터 Monster에 드래그

(언리얼 에디터) 임포트 대상 : Data Table

(언리얼 에디터) 데이터 테이블 행 유형 선택 : MonsterInfo

 

(C++-Monster.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "Monster.h"
#include "UE7GameInstance.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();
}

void AMonster::AddPatrolPoint(APatrolPoint* point)
{
    PatrolPointArray.Add(point);
    
}

// Called when the game starts or when spawned
void AMonster::BeginPlay()
{
    Super::BeginPlay();
    
    UUE7GameInstance* GameInst = GetGameInstance<UUE7GameInstance>();

    const FMonsterInfo* pMonsterInfo = GameInst->FindMonsterInfo(MonsterName);

    if (pMonsterInfo)
    {
        /*
        float fAttack;
        float fArmor;
        int32 iHP;
        int32 iHPMax;
        int32 iMP;
        int32 iMPMax;
        */
        
        MonsterState.fAttack = pMonsterInfo->Attack;
        MonsterState.fArmor = pMonsterInfo->Armor;
        MonsterState.iHP = pMonsterInfo->HP;
        MonsterState.iHPMax = pMonsterInfo->HPMax;
        MonsterState.iMP = pMonsterInfo->MP;
        MonsterState.iMPMax = pMonsterInfo->iMPMax;
        fTraceRange = pMonsterInfo->TraceRange;
        fAttackDist = pMonsterInfo->AttackRange;
    }
}

...

 

(C++-Minion.cpp)

 

// Fill out your copyright notice in the Description page of Project Settings.


#include "Minion.h"
#include "MinionAnim.h"

AMinion::AMinion()
{
    PrimaryActorTick.bCanEverTick = true;

    static ConstructorHelpers::FObjectFinder<USkeletalMesh> MeshAsset(TEXT("SkeletalMesh'/Game/ParagonMinions/Characters/Minions/Down_Minions/Meshes/Minion_Lane_Melee_Core_Dawn.Minion_Lane_Melee_Core_Dawn'"));

    if (MeshAsset.Succeeded())
        GetMesh()->SetSkeletalMesh(MeshAsset.Object);

    // AnimInstance 설정
    static ConstructorHelpers::FClassFinder<UMinionAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Monster/BPMinionAnim.BPMinionAnim_C'"));

    if (AnimAsset.Succeeded())
        GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

    GetMesh()->SetRelativeLocation(FVector(0.f, 0.f, -70.f));
    GetMesh()->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));

    fAttackDist = 200.f;

    MonsterName = TEXT("Minion");  // 이름 설정. 
    
}
...

(C++-MinionRanger.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "MinionRanger.h"
#include "MinionRangerAnim.h"

AMinionRanger::AMinionRanger()
{
    PrimaryActorTick.bCanEverTick = true;

    static ConstructorHelpers::FObjectFinder<USkeletalMesh> MeshAsset(TEXT("SkeletalMesh'/Game/ParagonMinions/Characters/Minions/Down_Minions/Meshes/Minion_Lane_Ranged_Core_Dawn.Minion_Lane_Ranged_Core_Dawn'"));

    if (MeshAsset.Succeeded())
        GetMesh()->SetSkeletalMesh(MeshAsset.Object);

    // AnimInstance 설정
    static ConstructorHelpers::FClassFinder<UMinionRangerAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Monster/BPMinionRangerAnim.BPMinionRangerAnim_C'"));

    if (AnimAsset.Succeeded())
        GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

    GetMesh()->SetRelativeLocation(FVector(0.f, 0.f, -70.f));
    GetMesh()->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));

    fAttackDist = 500.f;

    MonsterName = TEXT("MinionRanger");  // 이름 설정.
    
}
...

- 플레이어 무기와 몬스터 충돌 처리 오류 해결 - 

(언리얼 에디터-BPWukong) WeaponBox -> 디테일-콜리전-Collision Presets : "PlayerAttack"

 

(언리얼 에디터) 세팅-프로젝트 세팅 -> 엔진-콜리전 -> Preset-Monster : Player, Monster : "무시" PlayerAttack : "겹칩" 나머지 : "블록"

(언리얼 에디터) 세팅-프로젝트 세팅 -> 엔진-콜리전 -> Preset-PlayerAttack : Player, PlayerAttack : "무시" Monster : "겹침" 나머지 : "블록"

 

- 데미지 처리 -

(언리얼 에디터-BPMinionAnim) Death_A : 노티파이 추가 : "DeathEnd"

 

(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"

...

UCLASS()
class UE7PROJECT_API AMonster : public ACharacter
{
    GENERATED_BODY()
    
    ...

public:	
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser);

public:
    virtual void ChangeAnim(EMonsterAnim eAnim);

public:
    void AttackEnd();
    void Death();
    
};

(C++-Monster.cpp)

// Fill out your copyright notice in the Description page of Project Settings.


#include "Monster.h"
#include "UE7GameInstance.h"
#include "MonsterAIController.h"
#include "BehaviorTree/BehaviorTreeComponent.h"

...

// Called to bind functionality to input
void AMonster::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}

float AMonster::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
    float fDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

    MonsterState.iHP -= (int32)fDamage;

    if (MonsterState.iHP <= 0)  // 죽었을 경우. 
    {
        MonsterState.iHP = 0;

        GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
        
        ChangeAnim(EMonsterAnim::Death);

        AMonsterAIController* MonsterController = GetController<AMonsterAIController>();

        if (IsValid(MonsterController))
        {
            UBehaviorTreeComponent* BT = Cast<UBehaviorTreeComponent>(MonsterController->GetBrainComponent());

            if (IsValid(BT))
                BT->StopTree();
        }
    }

    return fDamage;
}

void AMonster::ChangeAnim(EMonsterAnim eAnim)
{
}

void AMonster::AttackEnd()
{
    bAttack = false;
    OnAttackEnd.Broadcast();

    for (FDelegateHandle& handle : AttackEndHandle)
    {
        OnAttackEnd.Remove(handle);
    }
}

void AMonster::Death()
{
    if (IsValid(SpawnPoint))
        SpawnPoint->Respawn();

    Destroy();
}

(C++-MinionAnim.h)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameInfo.h"
#include "Animation/AnimInstance.h"
#include "MinionAnim.generated.h"

...

public:
    UFUNCTION()
    void AnimNotify_AttackEnd();

    UFUNCTION()
    void AnimNotify_DeathEnd();
    
};

(C++-MinionAnim.cpp)

...
void UMinionAnim::ChangeAnim(EMonsterAnim eAnim)
{
    AnimType = (uint8)eAnim;
}

void UMinionAnim::AnimNotify_AttackEnd()
{
    AMinion* pOwner = Cast<AMinion>(TryGetPawnOwner());

    if (IsValid(pOwner))
        pOwner->AttackEnd();
}

void UMinionAnim::AnimNotify_DeathEnd()
{
    AMinion* pOwner = Cast<AMinion>(TryGetPawnOwner());
    
    if (IsValid(pOwner))
        pOwner->Death();
}

(C++-MinionRangerAnim.h)

...

public:
    UFUNCTION()
    void AnimNotify_AttackEnd();

    UFUNCTION()
    void AnimNotify_DeathEnd();
    
};

(C++-MinionRangerAnim.cpp)

...
void UMinionRangerAnim::ChangeAnim(EMonsterAnim eAnim)
{
    AnimType = (uint8)eAnim;
}

void UMinionRangerAnim::AnimNotify_AttackEnd()
{
    AMinionRanger* pOwner = Cast<AMinionRanger>(TryGetPawnOwner());

    if (IsValid(pOwner))
        pOwner->AttackEnd();
}

void UMinionRangerAnim::AnimNotify_DeathEnd()
{
    AMinionRanger* pOwner = Cast<AMinionRanger>(TryGetPawnOwner());

    if (IsValid(pOwner))
        pOwner->Death();
}

(C++-SpawnPoint.cpp)

...
void ASpawnPoint::Respawn()
{
    if (!IsValid(SpawnType))
        return;

    SpawnMonster = nullptr;
    if (bInfinity)
        return;

    bSpawnEnable = true;
    SpawnDuration = 0.f;  // 시간 0으로.
    
}
...

 

 

'Study > Unreal' 카테고리의 다른 글

[Unreal/정리] 시작 화면  (0) 2020.11.05
[Unreal/정리] HPBar  (0) 2020.11.04
[Unreal/정리] Patrol  (0) 2020.11.02
[Unreal/정리] 행동트리  (0) 2020.10.26
[Unreal/정리] 랜드 스케이프 레이어  (0) 2020.10.23

댓글