목차

     

     


     

     

     

     
    Source
        Characters
        CAnimInstance.h .cpp
    CPlayer.h .cpp
    ICharacter.h .cpp
        Components
        CMontagesComponent.h .cpp 
    CMovementComponent.h .cpp 
    CStateComponent.h .cpp 
    CWeaponComponent.h .cpp 
        Notifies
        CAnimNotify_EndState.h .cpp
        Utilities
        CHelper.h
    CLog.h .cpp
        Weapons
        CAttachment.h .cpp
    CEquipment.h .cpp 생성
    CWeaponAsset.h .cpp
    CWeaponStructures.h .cpp 생성
        Global.h
    CGameMode.h .cpp
    U2212_06.Build.cs
        U2212_06.uproject
     

     

     

     

    무기 시스템 만들기

     

     


     

     

    Weapon System 구조 개요 

     

     

           
       WeponComponent  WeaponAsset  Attachment
     Equipment
     DoAction

     

     


     

     

    CPlayer + BP_CPlayer

     

    CPlayer.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Character.h"
    #include "Components/CStateComponent.h"
    #include "Characters/ICharacter.h"
    #include "CPlayer.generated.h"
    
    UCLASS()
    class U2212_06_API ACPlayer
    	: public ACharacter, public IICharacter //다중상속
    {
    	GENERATED_BODY()
    
    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;
    
    public:
    	ACPlayer();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:	
    	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    
    private:
    	UFUNCTION()
    		void OnStateTypeChanged(EStateType InPrevType, EStateType InNewType);
    
    private:
    	void OnAvoid();
    
    private:
    	void BackStep();
    
    public:
    	void End_BackStep() override;//ICharacter의 함수 오버라이드
    };

    Player 내에 WeaponComponent 추가

    • UPROPERTY(VisibleAnywhere)
      class UCWeaponComponent* Weapon;

     

     

    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"
    
    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, "Montage");
    	CHelpers::CreateActorComponent<UCMovementComponent>(this, &Movement, "Movement");
    	CHelpers::CreateActorComponent<UCStateComponent>(this, &State, "State");
    
    	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);
    
    }
    
    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->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode);
    }
    
    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상태로 돌려줌.
    }

    Player 내에 WeaponComponent 추가

    • CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");

     

    Sword 장착/해제 키 입력 추가

    • PlayerInputComponent->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode);

     

     

     

     

    BP_CPlayer

    Player내에 추가한 WeaponComponet에 Data Asset 할당

    • Weapon - Data Asset - Data Assets - Sword - DA_Sword 할당

     


     

     

     

    CWeaponComponent.h .cpp 

     

    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: //무기 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;
    
    private:
    	bool IsIdleMode();//StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다.
    
    public:
    	class ACAttachment* GetAttachment();
    	class UCEquipment* GetEquipment();
    
    public: //무기 세팅
    	void SetUnarmedMode();
    	void SetFistMode();
    	void SetSwordMode();
    	void SetHammerMode();
    	void SetWarpMode();
    	void SetAroundMode();
    	void SetBowMode();
    
    private:
    	void SetMode(EWeaponType InType);
    	void ChangeType(EWeaponType InType);
    
    public: //무기가 바뀌었을때 통보해줄 delegate
    	FWeaponTypeChanged OnWeaponTypeChange;
    
    private:
    	class ACharacter* OwnerCharacter;
    
    	EWeaponType Type = EWeaponType::Max;
    };

    WeaponAsset의 데이터를 가져온다.

    • UPROPERTY(EditAnywhere, Category = "DataAsset")
      class UCWeaponAsset* DataAssets[(int32)EWeaponType::Max];

     

    무기 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; }

     

    IsIdleMode() 상태

    • bool IsIdleMode();
      • StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다.

     

     

     

    CWeaponComponent.cpp 

    더보기
    #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"
    
    UCWeaponComponent::UCWeaponComponent()
    {
    }
    
    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시켜준다.
    	}
    }
    
    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();
    }
    
    
    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);
    }

     

     


     

     

     

    CAttachment + BP_CAttachment_Sword

     

    CAttachment.h 

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "CAttachment.generated.h"
    
    UCLASS()
    class U2212_06_API ACAttachment : public AActor
    {
    	GENERATED_BODY()
    
    protected:
    	UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
    		class USceneComponent* Root;
    	
    public:	
    	ACAttachment();
    
    protected:
    	virtual void BeginPlay() override;
    
    protected:
    	UFUNCTION(BlueprintCallable, Category = "Attach")
    		void AttachTo(FName InSocketName);
    
    protected:
    	UPROPERTY(BlueprintReadOnly, Category = "Game")
    		class ACharacter* OwnerCharacter;
    };

     

     

    CAttachment.cpp

    더보기
    #include "Weapons/CAttachment.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "Components/SceneComponent.h"
    
    ACAttachment::ACAttachment()
    {
    	CHelpers::CreateComponent(this, &Root, "Root");
    }
    
    void ACAttachment::BeginPlay()
    {
    	OwnerCharacter = Cast<ACharacter>(GetOwner());
    	//ACharacter를 먼저 Cast 한 후에 Super::BeginPlay() 호출.
    	Super::BeginPlay();
    }
    
    void ACAttachment::AttachTo(FName InSocketName)
    {
    	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSocketName);
    }

     

     

     

     

     

    왼쪽은 Blueprint에서 사용하는 버전인 Attach Actor To Component.

    Blueprint와는 달리 C++에서는 AttachToComponent라는 함수를 사용한다.

     

     

     

     

     

     

     

     

    BP_CAttachment_Sword

     

     


     

     

     

    실행화면

     

    게임을 플레이하면 Holster_Sword 위치에 Sword가 생성된다. 

     

     


     

     

     

     

    무기 장착 및 해제 시스템 만들기

     

     


     

     

     

    CEquipment 생성

     

    새 C++ 클래스 - Object - CEquipment 생성

     

     

    CEquipment.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Weapons/CWeaponStructures.h"
    #include "CEquipment.generated.h"
    
    UCLASS()
    class U2212_06_API UCEquipment : public UObject
    {
    	GENERATED_BODY()
    
    public:
    	void BeginPlay(class ACharacter* InOwner, const FEquipmentData& InData);
    
    public:
    	UFUNCTION(BlueprintNativeEvent)//필요시 BP에서 재정의하기 위해 BlueprintNativeEvent사용.
    		void Equip();
    		void Equip_Implementation();
    
    	UFUNCTION(BlueprintNativeEvent)
    		void Begin_Equip();
    		void Begin_Equip_Implementation();
    
    	UFUNCTION(BlueprintNativeEvent)
    		void End_Equip();
    		void End_Equip_Implementation();
    
    	UFUNCTION(BlueprintNativeEvent)
    		void Unequip();
    		void Unequip_Implementation();
    
    private:
    	class ACharacter* OwnerCharacter;
    	FEquipmentData Data;
    
    private:
    	class UCMovementComponent* Movement;
    	class UCStateComponent* State;
    
    private:
    	bool bBeginEquip;//Equip이 시작되었는가 확인
    	bool bEquipped;  //Equip이 완료되었는지 확인
    };

     

     

     

    CEquipment.cpp

    더보기
    #include "Weapons/CEquipment.h"

     

     


     

     

    CWeaponStructures 생성

     

    새 C++ 클래스 - Object - CWeaponStructures 생성

     

     

    CWeaponStructures.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "CWeaponStructures.generated.h"
    
    USTRUCT()
    struct FEquipmentData
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY(EditAnywhere)
    		class UAnimMontage* Montage;
    
    	UPROPERTY(EditAnywhere)
    		float PlayRate = 1;
    
    	UPROPERTY(EditAnywhere)
    		bool bCanMove = true;
    
    	UPROPERTY(EditAnywhere)
    		bool bUseControlRotation = true;
    };
    
    UCLASS()
    class U2212_06_API UCWeaponStructures : public UObject
    {
    	GENERATED_BODY()
    };

     

     

     

    CWeaponStructures.cpp

    더보기
    #include "Weapons/CWeaponStructures.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "Components/CMovementComponent.h"
    #include "Components/CStateComponent.h"
    #include "CEquipment.h"
    
    void UCEquipment::BeginPlay(ACharacter* InOwner, const FEquipmentData& InData)
    {
    	OwnerCharacter = InOwner;
    	Data = InData;
    
    	Movement = CHelpers::GetComponent<UCMovementComponent>(InOwner);
    	State = CHelpers::GetComponent<UCStateComponent>(InOwner);
    }
    
    void UCEquipment::Equip_Implementation()
    {
    	State->SetEquipMode();
    
    	if (Data.bCanMove == false)
    		Movement->Stop();
    
    	if (Data.bUseControlRotation)
    		Movement->EnableControlRotation();
    
    	if (!!Data.Montage)
    	{
    		OwnerCharacter->PlayAnimMontage(Data.Montage, Data.PlayRate);
    	}
    	else
    	{
    		Begin_Equip();
    		End_Equip();
    	}
    }
    
    void UCEquipment::Begin_Equip_Implementation()
    {
    	bBeginEquip = true;
    }
    
    void UCEquipment::End_Equip_Implementation()
    {
    	bBeginEquip = false;
    	bEquipped = true;
    
    	Movement->Move();
    	State->SetIdleMode();
    }
    
    void UCEquipment::Unequip_Implementation()
    {
    	bEquipped = false;
    	Movement->DisableControlRotation();
    }

     

     

     


     

     

     

    CWeaponAsset  +  DA_Sword

     

    CWeaponAsset.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "Engine/DataAsset.h"
    #include "Weapons/CWeaponStructures.h"
    #include "CWeaponAsset.generated.h"
    
    UCLASS()
    class U2212_06_API UCWeaponAsset : public UDataAsset
    {
    	GENERATED_BODY()
    
    private:
    	UPROPERTY(EditAnywhere)
    		TSubclassOf<class ACAttachment> AttachmentClass;
    
    	UPROPERTY(EditAnywhere)
    		FEquipmentData EquipmentData;
    
    	UPROPERTY(EditAnywhere)
    		TSubclassOf<class UCEquipment> EquipmentClass;
    
    public:
    	FORCEINLINE class ACAttachment* GetAttachment() { return Attachment; }//외부에 생성된 것을 리턴해줌. 
    	FORCEINLINE class UCEquipment* GetEquipment() { return Equipment; }//외부에 생성된 것을 리턴해줌.
    
    public:
    	UCWeaponAsset();
    
    	void BeginPlay(class ACharacter* InOwner);
    
    private:
    	//UPROPERTY를 붙여 가비지 콜렉터가 제거하기 전까지 물고 있게 만든다.
    	//UWeaponAsset은 UObject로부터 상속받아 Actor의 생성주기에 영향을 받지 않아 가비지 콜렉터에 영향을 받는다.
    	UPROPERTY() 
    		class ACAttachment* Attachment;
    
    	UPROPERTY()
    		class UCEquipment* Equipment;
    };

     

     

     

    CWeaponAsset.cpp

    더보기
    #include "Weapons/CWeaponAsset.h"
    #include "Global.h"
    #include "CAttachment.h"
    #include "CEquipment.h"
    #include "GameFramework/Character.h"
    
    
    UCWeaponAsset::UCWeaponAsset()
    {
    	AttachmentClass = ACAttachment::StaticClass();//기본값
    	EquipmentClass = UCEquipment::StaticClass();//기본값
    }
    
    void UCWeaponAsset::BeginPlay(ACharacter* InOwner)
    {
    	if (!!AttachmentClass)//AttachmentClass가 선택되어 있다면
    	{
    		FActorSpawnParameters params;
    		params.Owner = InOwner;
    
    		Attachment = InOwner->GetWorld()->SpawnActor<ACAttachment>(AttachmentClass, params);
    	}
    
    	if (!!EquipmentClass)
    	{
    		Equipment = NewObject<UCEquipment>(this, EquipmentClass);
    		Equipment->BeginPlay(InOwner, EquipmentData);
    	}
    }

     

     

     

    DA_Sword

     

     


     

     

     

     

     

     

     

    실행화면

     

    Sword 키 입력을 하면 Equip_Sword 몽타주가 실행된다.