[UE] Behavior Tree: 무기 장착(Equip)

현재 적 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

실행화면

'⭐ Unreal Engine > UE RPG AI' 카테고리의 다른 글
[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: 순찰(Patrol) (0) | 2023.07.25 |
[UE] Behavior Tree 시작 (1) | 2023.07.24 |
댓글
이 글 공유하기
다른 글
-
[UE] Behavior Tree: 피격(Hitted)
[UE] Behavior Tree: 피격(Hitted)
2023.07.31 -
[UE] Behavior Tree: 무기 장착 및 공격
[UE] Behavior Tree: 무기 장착 및 공격
2023.07.27 -
[UE] Behavior Tree: 순찰(Patrol)
[UE] Behavior Tree: 순찰(Patrol)
2023.07.25 -
[UE] Behavior Tree 시작
[UE] Behavior Tree 시작
2023.07.24
댓글을 사용할 수 없습니다.