목차

     

     


     

     

     

     
    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 몽타주가 실행된다.