현재 적 AI 순찰을 진행하다 Player가 시야 범위 내에 들어오면 접근한다. 이번 시간에는 Player에게 접근한 후 무기를 장착하는 것을 Behavior Tree를 통해 구현할 것이다. BTTask_Node를 새로 만들어 무기 장착 Task를 전담하게 할 것이다.

 

 

목차

     

     


     

     

     
    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
    CBTTaskNode_Equip.h .cpp 생성

    CBTTaskNode_Patrol.h .cpp
    CBTTaskNode_Speed.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
    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
    CWeaponStructures.h .cpp
        Global.h
    CGameMode.h .cpp
    U2212_06.Build.cs
        U2212_06.uproject
     

     

     

    무기 장착(Equip)

     


     

    CWeaponComponent

     

    CWeaponComponent.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "CWeaponComponent.generated.h"
    
    UENUM(BlueprintType)
    enum class EWeaponType : uint8
    {
    	Fist, Sword, Hammer, Warp, Around, Bow, Max,
    };
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType);
    
    UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
    class U2212_06_API UCWeaponComponent : public UActorComponent
    {
    	GENERATED_BODY()
    
    private://DataAsset을 받아온다.
    	UPROPERTY(EditAnywhere, Category = "DataAsset")
    		class UCWeaponAsset* DataAssets[(int32)EWeaponType::Max];
    
    public:
    	FORCEINLINE EWeaponType GetWeaponType() { return Type; }
    
    public: //무기 Type이 맞는지 확인해주는 함수들
    	FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; }
    	FORCEINLINE bool IsFistMode() { return Type == EWeaponType::Fist; }
    	FORCEINLINE bool IsSwordMode() { return Type == EWeaponType::Sword; }
    	FORCEINLINE bool IsHammerMode() { return Type == EWeaponType::Hammer; }
    	FORCEINLINE bool IsWarpMode() { return Type == EWeaponType::Warp; }
    	FORCEINLINE bool IsAroundMode() { return Type == EWeaponType::Around; }
    	FORCEINLINE bool IsBowMode() { return Type == EWeaponType::Bow; }
    
    public:
    	UCWeaponComponent();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:
    	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
    private:
    	bool IsIdleMode();//StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다.
    
    public:
    	class ACAttachment* GetAttachment();
    	class UCEquipment* GetEquipment();
    	class UCDoAction* GetDoAction();
    	class UCSubAction* GetSubAction();
    
    public: //무기 세팅
    	void SetUnarmedMode();
    	void SetFistMode();
    	void SetSwordMode();
    	void SetHammerMode();
    	void SetWarpMode();
    	void SetAroundMode();
    	void SetBowMode();
    
    	void DoAction();
    	void SubAction_Pressed();
    	void SubAction_Released();
    
    private:
    	void SetMode(EWeaponType InType);
    	void ChangeType(EWeaponType InType);
    
    public: //무기가 바뀌었을때 통보해줄 delegate
    	FWeaponTypeChanged OnWeaponTypeChange;
    
    private:
    	class ACharacter* OwnerCharacter;
    
    	EWeaponType Type = EWeaponType::Max;
    };

    인라인 함수 추가

    • FORCEINLINE EWeaponType GetWeaponType() { return Type; }

     

     

     

    CWeaponComponent.h

    더보기
    #include "Components/CWeaponComponent.h"
    #include "Global.h"
    #include "CStateComponent.h"
    #include "GameFramework/Character.h"
    #include "Weapons/CWeaponAsset.h"
    #include "Weapons/CAttachment.h"
    #include "Weapons/CEquipment.h"
    #include "Weapons/CDoAction.h"
    #include "Weapons/CSubAction.h"
    
    UCWeaponComponent::UCWeaponComponent()
    {
    	//Tick을 실행시켜주는 코드
    	PrimaryComponentTick.bCanEverTick = true;
    }
    
    void UCWeaponComponent::BeginPlay()
    {
    	Super::BeginPlay();
    
    	OwnerCharacter = Cast<ACharacter>(GetOwner());
    	for (int32 i=0; i < (int32)EWeaponType::Max; i++)
    	{
    		if (!!DataAssets[i]) //DataAssets[i]이 있다면(=무기가 할당되어 있다면)
    			DataAssets[i]->BeginPlay(OwnerCharacter);//BeginPla y 시 OwnerCharacter에 Spawn시켜준다.
    	}
    }
    
    void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
    	if (!!GetDoAction())//DoAction이 있다면
    		GetDoAction()->Tick(DeltaTime);//DoAction의 Tick을 콜 해준다.
    
    	if (!!GetSubAction())//SubAction이 있다면
    		GetSubAction()->Tick(DeltaTime);//SubAction의 Tick을 콜 해준다.
    }
    
    bool UCWeaponComponent::IsIdleMode()
    {
    	return CHelpers::GetComponent<UCStateComponent>(OwnerCharacter)->IsIdleMode();
    }
    
    ACAttachment* UCWeaponComponent::GetAttachment()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetAttachment();
    }
    
    UCEquipment* UCWeaponComponent::GetEquipment()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetEquipment();
    }
    
    UCDoAction* UCWeaponComponent::GetDoAction()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetDoAction();
    }
    
    UCSubAction* UCWeaponComponent::GetSubAction()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);
    	CheckFalseResult(!!DataAssets[(int32)Type], nullptr);
    
    	return DataAssets[(int32)Type]->GetSubAction();
    }
    
    void UCWeaponComponent::SetUnarmedMode()
    {
    	GetEquipment()->Unequip();
    
    	ChangeType(EWeaponType::Max);
    }
    
    void UCWeaponComponent::SetFistMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Fist);
    }
    
    void UCWeaponComponent::SetSwordMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Sword);
    }
    
    void UCWeaponComponent::SetHammerMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Hammer);
    }
    
    void UCWeaponComponent::SetWarpMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Warp);
    }
    
    void UCWeaponComponent::SetAroundMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Around);
    }
    
    void UCWeaponComponent::SetBowMode()
    {
    	CheckFalse(IsIdleMode());
    
    	SetMode(EWeaponType::Bow);
    }
    
    void UCWeaponComponent::SetMode(EWeaponType InType)
    {
    	if (Type == InType)
    	{
    		SetUnarmedMode();
    
    		return;
    	}
    	else if (IsUnarmedMode() == false)
    	{
    		GetEquipment()->Unequip();
    	}
    
    	if (!!DataAssets[(int32)InType])
    	{
    		DataAssets[(int32)InType]->GetEquipment()->Equip();
    
    		ChangeType(InType);
    	}
    }
    
    void UCWeaponComponent::ChangeType(EWeaponType InType)
    {
    	EWeaponType prevType = Type;
    	Type = InType;
    
    	if (OnWeaponTypeChange.IsBound())
    		OnWeaponTypeChange.Broadcast(prevType, InType);
    }
    
    void UCWeaponComponent::DoAction()
    {
    	if (!!GetDoAction())
    		GetDoAction()->DoAction();
    }
    
    void UCWeaponComponent::SubAction_Pressed()
    {
    	if (!!GetSubAction())
    		GetSubAction()->Pressed();
    }
    
    void UCWeaponComponent::SubAction_Released()
    {
    	if (!!GetSubAction())
    		GetSubAction()->Released();
    }

    변경사항 없음.


     

     

     

    CBTTaskNode_Equip 생성

     

    새 C++ 클래스 - BTTaskNode - 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:
    	virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
    	virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) 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 장착
    	}
    
    	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);
    
    		return;
    	}
    }

     

     

     

     

    CEquipment.h에 있는 bEquipped 정보를 가져오기 위해 아래의 코드를 사용하였다.

    UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(ai);//weapon을 가져온다.
    
    const bool* bEquipped = weapon->GetEquipment()->GetEquipped();

    WeaponComponent 내의 GetEquipment() 함수 

    →  CWeaponAsset.h 내의 GetEquipment() 함수로 CEquipment의 Equipment 

    →  CEquipment.h의 GetEquipped() 함수로 &bEquipped 리턴 

     


     

    BT_Melee

     

     


     

    실행화면