[UE] Behavior Tree 시작

Behavior Tree를 사용하여 AI System을 만들고 적AI를 구현할 것이다. AIController를 만들어 Enemy의 하위 클래스인 Enemy_AI에 할당할 것이다. Enemy_AI는 Behavior Tree를 가지고 AIBehaviorComponent를 활용하여 AI의 상태를 변경시킨다.
목차
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 생성 | ||
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 | ||
Behavior Tree
CAIController 생성
새 C++ 클래스- AIController - CAIController 생성


CAIController.h
#pragma once #include "CoreMinimal.h" #include "AIController.h" #include "CAIController.generated.h" UCLASS() class U2212_06_API ACAIController : public AAIController { GENERATED_BODY() private: UPROPERTY(VisibleAnywhere) class UAIPerceptionComponent* Perception; public: ACAIController(); protected: virtual void BeginPlay() override; protected: void OnPossess(APawn* InPawn) override; void OnUnPossess() override; private: UFUNCTION() void OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors); private: class ACEnemy_AI* Enemy; class UCAIBehaviorComponent* Behavior; class UAISenseConfig_Sight* Sight;//시야 감지 };
CAIController.cpp
#include "Characters/CAIController.h" #include "Global.h" #include "GameFramework/Character.h" #include "BehaviorTree/BlackboardComponent.h" #include "BehaviorTree/BehaviorTree.h" #include "Characters/CEnemy_AI.h" #include "Components/CAIBehaviorComponent.h" #include "Perception/AIPerceptionComponent.h" #include "Perception/AISenseConfig_Sight.h" ACAIController::ACAIController() { CHelpers::CreateActorComponent<UBlackboardComponent>(this, &Blackboard, "Blackboard"); CHelpers::CreateActorComponent<UAIPerceptionComponent>(this, &Perception, "Perception"); Sight = CreateDefaultSubobject<UAISenseConfig_Sight>("Sight");//CreateDefaultSubobject는 생성자 동적할당, UObject는 런타임 동적할당. Sight->SightRadius = 600;//시야 반경 Sight->LoseSightRadius = 800;//시야를 잃는 범위 Sight->PeripheralVisionAngleDegrees = 45;//시야각 Sight->SetMaxAge(2); Sight->DetectionByAffiliation.bDetectEnemies = true;//적들을 감지o Sight->DetectionByAffiliation.bDetectNeutrals = false;//중립 감지x Sight->DetectionByAffiliation.bDetectFriendlies = false;//아군 감지x Perception->ConfigureSense(*Sight);//감지 객체 지정. 시야감지로 지정. Perception->SetDominantSense(*Sight->GetSenseImplementation());//시야감지를 최우선 감지타입으로 설정. } void ACAIController::BeginPlay() { Super::BeginPlay(); Perception->OnPerceptionUpdated.AddDynamic(this, &ACAIController::OnPerceptionUpdated); } void ACAIController::OnPossess(APawn* InPawn) { Super::OnPossess(InPawn); Enemy = Cast<ACEnemy_AI>(InPawn);//Enemy에 possess되는 Pawn을 넣어준다. SetGenericTeamId(Enemy->GetTeamID());//TeamID 지정 CheckNull(Enemy->GetBehaviorTree());//Enemy가 BehaviorTree를 가지고 있는지 체크 UseBlackboard(Enemy->GetBehaviorTree()->BlackboardAsset, Blackboard);//Blackboard 사용을 위해 BlackboardData인 BlackboardAsset과 BlackboardComonent인 Blackboard를 할당한다. Behavior = CHelpers::GetComponent<UCAIBehaviorComponent>(Enemy);//Enemy내의 BehaviorComponent를 가져온다. Behavior->SetBlackboard(Blackboard); RunBehaviorTree(Enemy->GetBehaviorTree());//BehaviorTree 실행 } void ACAIController::OnUnPossess() { Super::OnUnPossess(); } void ACAIController::OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors) { TArray<AActor*> actors;//감지되는 객체들 Perception->GetCurrentlyPerceivedActors(nullptr, actors);//Perception 감지 if (actors.Num() > 0)//감지되는 객체가 0 이상이면(=감지된 객체가 있다면) { Blackboard->SetValueAsObject("Target", actors[0]);//감지된 actor[0]을 객체로 지정한다. return; } Blackboard->SetValueAsObject("Target", nullptr);//감지가 안 되었다면 nullptr }

Inheritance Hierarchy
UObjectBase
- UObjectBaseUtility
- UObject
- UAISenseConfig
- UAISenseConfig_Blueprint
- UAISenseConfig_Damage
- UAISenseConfig_Hearing
- UAISenseConfig_Prediction
- UAISenseConfig_Sight
- UAISenseConfig_Team
- UAISenseConfig_Touch
- UAISenseConfig
- UObject
BP_CAIController_Melee 생성
블루프린트 클래스 - CAIController - BP_CAIController_Melee 생성



CAIBehaviorComponent
CAIBehaviorComponent.h
#pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "CAIBehaviorComponent.generated.h" UENUM(BlueprintType) enum class EAIStateType : uint8 { Wait = 0, Approach, Action, Patrol, Hitted, Avoid, Dead, Max, }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAIStateTypeChanged, EAIStateType, InPrevType, EAIStateType, InNewType); UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class U2212_06_API UCAIBehaviorComponent : public UActorComponent { GENERATED_BODY() private: UPROPERTY(EditAnywhere, Category = "Key") FName AIStateTypeKey = "AIState"; UPROPERTY(EditAnywhere, Category = "Key") FName TargetKey = "Target"; private: EAIStateType GetType(); public: bool IsWaitMode(); bool IsApproachMode(); bool IsActionMode(); bool IsPatrolMode(); bool IsHittedMode(); bool IsAvoidMode(); bool IsDeadMode(); public: UCAIBehaviorComponent(); protected: virtual void BeginPlay() override; public: FORCEINLINE void SetBlackboard(class UBlackboardComponent* InBlackboard) { Blackboard = InBlackboard; } public: class ACharacter* GetTarget(); public: void SetWaitMode(); void SetApproachMode(); void SetActionMode(); void SetPatrolMode(); void SetHittedMode(); void SetAvoidMode(); void SetDeadMode(); private: void ChangeType(EAIStateType InType); public: FAIStateTypeChanged OnAIStateTypeChanged; private: class UBlackboardComponent* Blackboard; };
변경사항 없음
CAIBehaviorComponent.cpp
#include "Components/CAIBehaviorComponent.h" #include "Global.h" #include "GameFramework/Character.h" #include "BehaviorTree/BlackboardComponent.h" UCAIBehaviorComponent::UCAIBehaviorComponent() { } void UCAIBehaviorComponent::BeginPlay() { Super::BeginPlay(); } EAIStateType UCAIBehaviorComponent::GetType() { return (EAIStateType)Blackboard->GetValueAsEnum(AIStateTypeKey); } bool UCAIBehaviorComponent::IsWaitMode() { return GetType() == EAIStateType::Wait; } bool UCAIBehaviorComponent::IsApproachMode() { return GetType() == EAIStateType::Approach; } bool UCAIBehaviorComponent::IsActionMode() { return GetType() == EAIStateType::Action; } bool UCAIBehaviorComponent::IsPatrolMode() { return GetType() == EAIStateType::Patrol; } bool UCAIBehaviorComponent::IsHittedMode() { return GetType() == EAIStateType::Hitted; } bool UCAIBehaviorComponent::IsAvoidMode() { return GetType() == EAIStateType::Avoid; } bool UCAIBehaviorComponent::IsDeadMode() { return GetType() == EAIStateType::Dead; } ACharacter* UCAIBehaviorComponent::GetTarget() { //Blackboard 내의 TargetKey를 리턴해준다. return Cast<ACharacter>(Blackboard->GetValueAsObject(TargetKey)); } void UCAIBehaviorComponent::SetWaitMode() { ChangeType(EAIStateType::Wait); } void UCAIBehaviorComponent::SetApproachMode() { ChangeType(EAIStateType::Approach); } void UCAIBehaviorComponent::SetActionMode() { ChangeType(EAIStateType::Action); } void UCAIBehaviorComponent::SetPatrolMode() { ChangeType(EAIStateType::Patrol); } void UCAIBehaviorComponent::SetHittedMode() { ChangeType(EAIStateType::Hitted); } void UCAIBehaviorComponent::SetAvoidMode() { ChangeType(EAIStateType::Avoid); } void UCAIBehaviorComponent::SetDeadMode() { ChangeType(EAIStateType::Dead); } void UCAIBehaviorComponent::ChangeType(EAIStateType InType) { EAIStateType prevType = GetType(); Blackboard->SetValueAsEnum(AIStateTypeKey, (uint8)InType); if (OnAIStateTypeChanged.IsBound()) OnAIStateTypeChanged.Broadcast(prevType, InType); }
ACharacter* UCAIBehaviorComponent::GetTarget()
- return Cast(Blackboard->GetValueAsObject(TargetKey));
- Blackboard 내의 TargetKey를 리턴해준다.
- Blackboard 내의 TargetKey를 리턴해준다.
CEnemy_AI
CEnemy_AI.h
#pragma once #include "CoreMinimal.h" #include "Characters/CEnemy.h" #include "CEnemy_AI.generated.h" UCLASS() class U2212_06_API ACEnemy_AI : public ACEnemy { GENERATED_BODY() private: UPROPERTY(EditDefaultsOnly, Category = "AI") class UBehaviorTree* BehaviorTree; UPROPERTY(EditDefaultsOnly, Category = "AI") uint8 TeamID = 2;//TeamID를 0~255번까지 지정가능하다. 255번은 중립이다. ID 같으면 아군이고 ID가 다르면 적이다. private: UPROPERTY(EditDefaultsOnly, Category = "Label") float LabelViewAmount = 3000.0f; #if WITH_EDITOR private: UPROPERTY(VisibleDefaultsOnly) class UWidgetComponent* LabelWidget; #endif private: UPROPERTY(VisibleDefaultsOnly) class UCWeaponComponent* Weapon; UPROPERTY(VisibleDefaultsOnly) class UCAIBehaviorComponent* Behavior; public: FORCEINLINE uint8 GetTeamID() { return TeamID; } FORCEINLINE class UBehaviorTree* GetBehaviorTree() { return BehaviorTree; } public: ACEnemy_AI(); protected: virtual void BeginPlay() override; public: virtual void Tick(float DeltaTime) override; private: void UpdateLabelRenderScale(); };
변수 추가
- UPROPERTY(EditDefaultsOnly, Category = "AI")
uint8 TeamID = 2;- TeamID를 0~255번까지 지정가능하다. 255번은 중립이다. ID 같으면 아군이고 ID가 다르면 적이다.
인라인 함수 추가
- FORCEINLINE uint8 GetTeamID() { return TeamID; }
- FORCEINLINE class UBehaviorTree* GetBehaviorTree() { return BehaviorTree; }
CEnemy_AI.cpp
#include "Characters/CEnemy_AI.h" #include "Global.h" #include "Components/CWeaponComponent.h" #include "Components/CAIBehaviorComponent.h" #include "Components/WidgetComponent.h" #include "Components/CStatusComponent.h" #include "Widgets/CUserWidget_Label.h" ACEnemy_AI::ACEnemy_AI() { PrimaryActorTick.bCanEverTick = true; CHelpers::CreateComponent<UWidgetComponent>(this, &LabelWidget, "Label", GetMesh()); CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon"); CHelpers::CreateActorComponent<UCAIBehaviorComponent>(this, &Behavior, "Behavior"); TSubclassOf<UCUserWidget_Label> labelClass; CHelpers::GetClass<UCUserWidget_Label>(&labelClass, "WidgetBlueprint'/Game/Widgets/WB_Label.WB_Label_C'"); LabelWidget->SetWidgetClass(labelClass); LabelWidget->SetRelativeLocation(FVector(0, 0, 220)); LabelWidget->SetDrawSize(FVector2D(120, 0)); LabelWidget->SetWidgetSpace(EWidgetSpace::Screen); } void ACEnemy_AI::BeginPlay() { Super::BeginPlay(); LabelWidget->InitWidget(); UCUserWidget_Label* label = Cast<UCUserWidget_Label>(LabelWidget->GetUserWidgetObject()); label->UpdateHealth(Status->GetHealth(), Status->GetMaxHealth()); label->UpdateName(GetName()); label->UpdateControllerName(GetController()->GetName()); } void ACEnemy_AI::Tick(float DeltaTime) { Super::Tick(DeltaTime); UCUserWidget_Label* label = Cast<UCUserWidget_Label>(LabelWidget->GetUserWidgetObject()); if (!!label) { label->UpdateHealth(Status->GetHealth(), Status->GetMaxHealth()); UpdateLabelRenderScale(); } } void ACEnemy_AI::UpdateLabelRenderScale() { UCUserWidget_Label* label = Cast<UCUserWidget_Label>(LabelWidget->GetUserWidgetObject()); CheckNull(label); APlayerCameraManager* cameraManager = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0); FVector cameraLocation = cameraManager->GetCameraLocation(); FVector targetLocation = GetController()->GetTargetLocation(); float distance = FVector::Distance(cameraLocation, targetLocation); float sizeRate = 1.0f - (distance / LabelViewAmount); if (distance > LabelViewAmount) { label->SetVisibility(ESlateVisibility::Collapsed); return; } label->SetVisibility(ESlateVisibility::Visible); label->SetRenderScale(FVector2D(sizeRate, sizeRate)); }
변경사항 없음.
BP_CEnemy_Melee

AI
- Behavior Tree: BT_Melee 할당

Pawn
- AIController Class: BP_CAIController_Melee 할당

Weapon
- Data Assets
- Sword: DA_Sword 할당

Montages
- Data Table
- Data Table: DT_Enemy 할당
BB_Melee 생성
우클릭 - 인공지능 - 블랙보 - BB_Melee 생성




BT_Melee 생성
우클릭 - 인공지능 - 비헤이비어 트리 - BT_Melee 생성



AI
- BehaviorTree
- Blackboard Asset: BB_Melee 할당

CBTService_Melee 생성
새 C++ 클래스 - BTService - CBTService_Melee 생성


CBTService_Melee.h
#pragma once #include "CoreMinimal.h" #include "BehaviorTree/BTService.h" #include "CBTService_Melee.generated.h" UCLASS() class U2212_06_API UCBTService_Melee : public UBTService { GENERATED_BODY() private: UPROPERTY(EditAnywhere, Category = "Action") float ActionRange = 150; public: UCBTService_Melee(); protected: void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override; };
CBTService_Melee.cpp
#include "BehaviorTree/CBTService_Melee.h" #include "Global.h" #include "GameFramework/Character.h" #include "Characters/CAIController.h" #include "Characters/CEnemy_AI.h" #include "Components/CStateComponent.h" #include "Components/CAIBehaviorComponent.h" UCBTService_Melee::UCBTService_Melee() { NodeName = "Melee"; Interval = 0.1f;//호출 간격. 0.1초 마다 호출 RandomDeviation = 0.0f; } void UCBTService_Melee::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) { Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds); ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner()); ACEnemy_AI* ai = Cast<ACEnemy_AI>(controller->GetPawn()); UCStateComponent* state = CHelpers::GetComponent<UCStateComponent>(ai); UCAIBehaviorComponent* aiState = CHelpers::GetComponent<UCAIBehaviorComponent>(ai); if (state->IsHittedMode()) { aiState->SetHittedMode(); return; } ACharacter* target = aiState->GetTarget(); if(target == nullptr)//target이 없다면(=시야 범위 내에 적이 없다면) { aiState->SetPatrolMode();//Patrol return; } float distance = ai->GetDistanceTo(target); if (distance < ActionRange)//공격범위 내로 들어오면 { aiState->SetActionMode();//공격 return; } aiState->SetApproachMode();//공격범위가 아니라면 Approach }
Build.cs
Build.cs
using UnrealBuildTool; public class U2212_06 : ModuleRules { public U2212_06(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicIncludePaths.Add(ModuleDirectory); PublicDependencyModuleNames.Add("Core"); PrivateDependencyModuleNames.Add("CoreUObject"); PrivateDependencyModuleNames.Add("Engine"); PrivateDependencyModuleNames.Add("InputCore"); PublicDependencyModuleNames.Add("Niagara"); PublicDependencyModuleNames.Add("AIModule"); PublicDependencyModuleNames.Add("GameplayTasks"); } }
모듈 추가
- PublicDependencyModuleNames.Add("GameplayTasks");
CPlayer
CPlayer.h
#pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "Components/CStateComponent.h" #include "Characters/ICharacter.h" #include "Parkour/CParkourComponent.h" #include "GenericTeamAgentInterface.h" #include "CPlayer.generated.h" UCLASS() class U2212_06_API ACPlayer : public ACharacter, public IICharacter, public IGenericTeamAgentInterface //다중상속 { GENERATED_BODY() private: UPROPERTY(EditDefaultsOnly, Category = "Team") uint8 TeamID = 1; private: UPROPERTY(VisibleAnywhere) class USpringArmComponent* SpringArm; UPROPERTY(VisibleAnywhere) class UCameraComponent* Camera; private: UPROPERTY(VisibleAnywhere) class UCWeaponComponent* Weapon; UPROPERTY(VisibleAnywhere) class UCMontagesComponent* Montages; UPROPERTY(VisibleAnywhere) class UCMovementComponent* Movement; UPROPERTY(VisibleAnywhere) class UCStateComponent* State; /** 파쿠르 */ private: UPROPERTY(VisibleDefaultsOnly) class USceneComponent* ArrowGroup;//파쿠르를 위한 ArrowGroup UPROPERTY(VisibleDefaultsOnly) class UArrowComponent* Arrows[(int32)EParkourArrowType::Max]; UPROPERTY(VisibleDefaultsOnly) class UCParkourComponent* Parkour; /** 파쿠르 */ UPROPERTY(VisibleDefaultsOnly) class UCZoomComponent* Zoom; public: ACPlayer(); protected: virtual void BeginPlay() override; public: virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; FGenericTeamId GetGenericTeamId() const override { return FGenericTeamId(TeamID); } private: UFUNCTION() void OnStateTypeChanged(EStateType InPrevType, EStateType InNewType); private: void OnAvoid(); private: void BackStep(); public: void End_BackStep() override;//ICharacter의 함수 오버라이드 public: void Click_RightButton(); public: void Landed(const FHitResult& Hit) override;//BP의 OnLanded C++버젼. private: void SetZooming(float InValue); };
헤더 추가
- #include "GenericTeamAgentInterface.h"
다중상속 추가
- public IGenericTeamAgentInterface
변수 추가
- UPROPERTY(EditDefaultsOnly, Category = "Team")
uint8 TeamID = 1;
함수 추가
- FGenericTeamId GetGenericTeamId() const override { return FGenericTeamId(TeamID); }
CPlayer.cpp
#include "Characters/CPlayer.h" #include "Global.h" #include "CAnimInstance.h" #include "GameFramework/SpringArmComponent.h" #include "GameFramework/CharacterMovementComponent.h" #include "Camera/CameraComponent.h" #include "Components/SkeletalMeshComponent.h" #include "Components/InputComponent.h" #include "Components/CMontagesComponent.h" #include "Components/CMovementComponent.h" #include "Components/CWeaponComponent.h" #include "Components/CZoomComponent.h" #include "Components/ArrowComponent.h" ACPlayer::ACPlayer() { CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetMesh()); CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm); CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon"); CHelpers::CreateActorComponent<UCMontagesComponent>(this, &Montages, "Montages"); CHelpers::CreateActorComponent<UCMovementComponent>(this, &Movement, "Movement"); CHelpers::CreateActorComponent<UCStateComponent>(this, &State, "State"); CHelpers::CreateActorComponent<UCParkourComponent>(this, &Parkour, "Parkour"); CHelpers::CreateActorComponent<UCZoomComponent>(this, &Zoom, "Zoom"); GetMesh()->SetRelativeLocation(FVector(0, 0, -90)); GetMesh()->SetRelativeRotation(FRotator(0, -90, 0)); USkeletalMesh* mesh; CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'"); GetMesh()->SetSkeletalMesh(mesh); TSubclassOf<UCAnimInstance> animInstance; CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'"); GetMesh()->SetAnimClass(animInstance); SpringArm->SetRelativeLocation(FVector(0, 0, 140)); SpringArm->SetRelativeRotation(FRotator(0, 90, 0)); SpringArm->TargetArmLength = 200; SpringArm->bDoCollisionTest = false; SpringArm->bUsePawnControlRotation = true; SpringArm->bEnableCameraLag = true; GetCharacterMovement()->RotationRate = FRotator(0, 720, 0); CHelpers::CreateComponent<USceneComponent>(this, &ArrowGroup, "ArrowGroup", GetCapsuleComponent());//ArrowGroup을 생성하고 GetCapsuleComponent 아래로 붙여준다. //파쿠르에 활용한 Arrows들을 생성한다. for (int32 i = 0; i < (int32)EParkourArrowType::Max; i++) { FString name = StaticEnum<EParkourArrowType>()->GetNameStringByIndex(i); CHelpers::CreateComponent<UArrowComponent>(this, &Arrows[i], FName(name), ArrowGroup); switch ((EParkourArrowType)i) { case EParkourArrowType::Center: Arrows[i]->ArrowColor = FColor::Red; break; case EParkourArrowType::Ceil: Arrows[i]->ArrowColor = FColor::Green; Arrows[i]->SetRelativeLocation(FVector(0, 0, 100)); break; case EParkourArrowType::Floor: Arrows[i]->ArrowColor = FColor::Blue; Arrows[i]->SetRelativeLocation(FVector(0, 0, -80)); break; case EParkourArrowType::Left: Arrows[i]->ArrowColor = FColor::Magenta; Arrows[i]->SetRelativeLocation(FVector(0, -30, 0)); break; case EParkourArrowType::Right: Arrows[i]->ArrowColor = FColor::Magenta; Arrows[i]->SetRelativeLocation(FVector(0, 30, 0)); break; case EParkourArrowType::Land: Arrows[i]->ArrowColor = FColor::Yellow; Arrows[i]->SetRelativeLocation(FVector(200, 0, 100)); Arrows[i]->SetRelativeRotation(FRotator(-90, 0, 0)); break; } } } void ACPlayer::BeginPlay() { Super::BeginPlay(); Movement->OnRun(); //Movement의 기본을 Run으로 설정 Movement->DisableControlRotation();//Movement의 기본을 DisableControlRotation으로 설정 State->OnStateTypeChanged.AddDynamic(this, &ACPlayer::OnStateTypeChanged); } void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); PlayerInputComponent->BindAxis("MoveForward", Movement, &UCMovementComponent::OnMoveForward); PlayerInputComponent->BindAxis("MoveRight", Movement, &UCMovementComponent::OnMoveRight); PlayerInputComponent->BindAxis("VerticalLook", Movement, &UCMovementComponent::OnVerticalLook); PlayerInputComponent->BindAxis("HorizontalLook", Movement, &UCMovementComponent::OnHorizontalLook); PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Pressed, Movement, &UCMovementComponent::OnSprint); PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Released, Movement, &UCMovementComponent::OnRun); PlayerInputComponent->BindAction("Avoid", EInputEvent::IE_Pressed, this, &ACPlayer::OnAvoid); //PlayerInputComponent->BindAxis("Zoom", Zoom, &UCZoomComponent::SetZoomValue);//Value로 연동되게하면 Bow 모드 시 문제가 될 수 있다. PlayerInputComponent->BindAxis("Zoom", this, &ACPlayer::SetZooming);//Bow 모드가 아닐 때만 사용하려고 SetZooming으로 설정하여 만들었다. PlayerInputComponent->BindAction("Fist", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetFistMode); PlayerInputComponent->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode); PlayerInputComponent->BindAction("Hammer", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetHammerMode); PlayerInputComponent->BindAction("Warp", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetWarpMode); PlayerInputComponent->BindAction("Around", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAroundMode); PlayerInputComponent->BindAction("Bow", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetBowMode); PlayerInputComponent->BindAction("Action", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::DoAction); //PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SubAction_Pressed); PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, this, &ACPlayer::Click_RightButton); PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::SubAction_Released); } void ACPlayer::OnStateTypeChanged(EStateType InPrevType, EStateType InNewType) { switch (InNewType) { case EStateType::BackStep: BackStep(); break; } } void ACPlayer::OnAvoid() { CheckFalse(State->IsIdleMode()); CheckFalse(Movement->CanMove()); CheckTrue(InputComponent->GetAxisValue("MoveForward") >= 0.0f);//뒷방향을 입력했다면 State->SetBackStepMode();//State을 BackStepMode로 변경한다. } void ACPlayer::BackStep() { Movement->EnableControlRotation();//정면을 바라본 상태로 뒤로 뛰어야하기 때문에 EnableControlRotation으로 만들어준다. Montages->PlayBackStepMode();//PlayBackStepMode()를 통해 몽타주 재생. } void ACPlayer::End_BackStep() { Movement->DisableControlRotation();//Backstep이 끝나면 원래대로 돌려준다. State->SetIdleMode();//Idle상태로 돌려줌. } void ACPlayer::Click_RightButton() { if (Weapon->IsUnarmedMode()) { Parkour->DoParkour(); return; } Weapon->SubAction_Pressed(); } void ACPlayer::Landed(const FHitResult& Hit) { Parkour->DoParkour(true); } void ACPlayer::SetZooming(float InValue) { CheckTrue(Weapon->IsBowMode());//BowMode이면 바로 리턴 Zoom->SetZoomValue(InValue);//BowMode가 아닌 경우 Zoom In&Out 가능. }
변경사항 없음.
※ 참고사항: GenericTeamAgentInterface.h

실행화면

'⭐ 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: 무기 장착(Equip) (0) | 2023.07.26 |
[UE] Behavior Tree: 순찰(Patrol) (0) | 2023.07.25 |
댓글
이 글 공유하기
다른 글
-
[UE] Behavior Tree: 피격(Hitted)
[UE] Behavior Tree: 피격(Hitted)
2023.07.31적(Enemy)의 피격과 Behavior Tree가 연결되어 있지 않다. 적이 피격 되었을 때 Behavior Tree에 알리고 피격이 끝났을 때 Behavior를 WaitMode로 변경하는 코드를 추가하였다. 목차 Plugins Weapon Resource Icon128.pngweapon_thumbnail_icon.png Source Weapon SWeaponCheckBoxes.h .cppSWeaponDetailsView.h .cppSWeaponDoActionData.h .cppSWeaponEquipmentData.h .cppSWeaponHitData.h .cppSWeaponLeftArea.h .cppWeapon.Build.csWeaponAssetEditor.h …. -
[UE] Behavior Tree: 무기 장착 및 공격
[UE] Behavior Tree: 무기 장착 및 공격
2023.07.27지난 시간에 구현한 Enemy 무기장착 시, Player쪽에서 무기 장착 몽타주가 재생되며 무기가 제대로 장착되지 않는 버그가 있었다. 이는 Weapon이 가지고 있는 정보들이 Player와 연관되어 있기 때문이었다. 이번 시간에 WeaponAsset의 일부 데이터를 WeaponData라는 새로운 클래스에 옮겨 분리시켜 줄 것이다. 이렇게 함으로써 무기 장착 시 무기가 Player쪽으로 불리는 문제를 방지시켜 줄 것이다. 목차 Plugins Weapon Resource Icon128.pngweapon_thumbnail_icon.png Source Weapon SWeaponCheckBoxes.h .cppSWeaponDetailsView.h .cppSWeaponDoAction… -
[UE] Behavior Tree: 무기 장착(Equip)
[UE] Behavior Tree: 무기 장착(Equip)
2023.07.26현재 적 AI 순찰을 진행하다 Player가 시야 범위 내에 들어오면 접근한다. 이번 시간에는 Player에게 접근한 후 무기를 장착하는 것을 Behavior Tree를 통해 구현할 것이다. BTTask_Node를 새로 만들어 무기 장착 Task를 전담하게 할 것이다. 목차 Plugins Weapon Resource Icon128.pngweapon_thumbnail_icon.png Source Weapon SWeaponCheckBoxes.h .cppSWeaponDetailsView.h .cppSWeaponDoActionData.h .cppSWeaponEquipmentData.h .cppSWeaponHitData.h .cppSWeaponLeftArea.h .cppWeapo… -
[UE] Behavior Tree: 순찰(Patrol)
[UE] Behavior Tree: 순찰(Patrol)
2023.07.25적의 상태에 따라 이동속도를 다르게 설정해보자. 적 Behavior Tree에 순찰 기능을 추가하고 Player가 접근하면 달려오도록 수정해보자. 목차 Plugins Weapon Resource Icon128.pngweapon_thumbnail_icon.png Source Weapon SWeaponCheckBoxes.h .cppSWeaponDetailsView.h .cppSWeaponDoActionData.h .cppSWeaponEquipmentData.h .cppSWeaponHitData.h .cppSWeaponLeftArea.h .cppWeapon.Build.csWeaponAssetEditor.h .cppWeaponAssetFactory.h .cppWeaponCo…
댓글을 사용할 수 없습니다.