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

     


     

     

     

    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를 리턴해준다.


     

     

     


     

    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

     


     

     

    실행화면