목차

     

     


     

     

     
    Source
        Characters
        CAnimInstance.h .cpp
    CPlayer.h .cpp (DoAction 입력만 추가)
    ICharacter.h .cpp
        Components
        CMontagesComponent.h .cpp 
    CMovementComponent.h .cpp 
    CStateComponent.h .cpp 
    CWeaponComponent.h .cpp 
        Notifies
        CAnimNotify_EndState.h .cpp
    CAnimNotifyState_Equip.h .cpp 생성
        Utilities
        CHelper.h
    CLog.h .cpp
        Weapons
        CAttachment.h .cpp
    CDoAction.h .cpp 생성
    CEquipment.h .cpp
    CWeaponAsset.h .cpp
    CWeaponStructures.h .cpp
        Global.h
    CGameMode.h .cpp
    U2212_06.Build.cs
        U2212_06.uproject
     

     

     

     

     

    무기 장착 및 기본 공격하기

     

     


     

     

    프로그래밍 설계의 진화

     

    Top-Down 방식 

    • 초기(과거) 프로그래밍 설계
    • 기능함수, 데이터 변수 수정에 대한 비용이 너무 크다는걸 인지. 

     

    그 후에 이 문제를 해결하기 위해...

    객체(함수 + 변수)가 등장!!

     

    요즘은 객체를 사용하여 프로그래밍한다.

     

     

     

     


     

     

    Weapon 구조

     

           
       WeponComponent  WeaponAsset  Attachment
     Equipment
     - OnEquip Event
     - OnUnequp Event 
     DoAction

     

    같은 레벨의 클래스를 부르는 방법

    • Delegation 사용하는 방법
      • 부분적으로 사용할 때 가장 좋은 방법 
      • ex. WeaponComponent  →  WeaponAsset  →  Equipment(에서 Event 발생 시 Delegation과 엮여있는 Attachment 내의 함수를 호출)
    • Friend를 사용하는 방법
      • 클래스에서만 사용되는 부분은 private으로 막아준다.
      • 다른곳에서 사용되는 부분을 public으로 열어준다.

     


     

     

    CEquipment

     

    CEquipment.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Weapons/CWeaponStructures.h"
    #include "CEquipment.generated.h"
    //직렬화하여 BP에서 사용가능하도록 DYNAMIC, 여러함수를 받을 수 있도록 MULTICAST 사용.
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentBeginEquip);
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentUnequip);
    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();
    public:
    FEquipmentBeginEquip OnEquipmentBeginEquip;//DECLARE_DYNAMIC_MULTICAST_DELEGATE 하였다.
    FEquipmentUnequip OnEquipmentUnequip;//DECLARE_DYNAMIC_MULTICAST_DELEGATE 하였다.
    private:
    class ACharacter* OwnerCharacter;
    FEquipmentData Data;
    private:
    class UCMovementComponent* Movement;
    class UCStateComponent* State;
    private:
    bool bBeginEquip;//Equip이 시작되었는가 확인
    bool bEquipped; //Equip이 완료되었는지 확인
    };

    델리게이트 생성

    • 직렬화하여 BP에서 사용가능하도록 DYNAMIC, 여러함수를 받을 수 있도록 MULTICAST 사용.
    • DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentBeginEquip);
    • DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentUnequip);

    델리게이트를 사용한 함수 선언

    • FEquipmentBeginEquip OnEquipmentBeginEquip;
    • FEquipmentUnequip OnEquipmentUnequip;

     

     

    CEquipment.cpp

    더보기
    #include "Weapons/CEquipment.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;
    if (OnEquipmentBeginEquip.IsBound())
    OnEquipmentBeginEquip.Broadcast();
    }
    void UCEquipment::End_Equip_Implementation()
    {
    bBeginEquip = false;
    bEquipped = true;
    Movement->Move();
    State->SetIdleMode();
    }
    void UCEquipment::Unequip_Implementation()
    {
    bEquipped = false;
    Movement->DisableControlRotation();
    if (OnEquipmentUnequip.IsBound())
    OnEquipmentUnequip.Broadcast();
    }

    Begin_Equip_Implementation()

    • OnEquipmentBeginEquip.Broadcast(); 해준다.

    Unequip_Implementation()

    • OnEquipmentUnequip.Broadcast(); 해준다.

     


     

    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;
    public:
    UFUNCTION(BlueprintImplementableEvent)
    void OnBeginEquip();
    UFUNCTION(BlueprintImplementableEvent)
    void OnUnequip();
    protected:
    UFUNCTION(BlueprintCallable, Category = "Attach")
    void AttachTo(FName InSocketName);
    protected:
    UPROPERTY(BlueprintReadOnly, Category = "Game")
    class ACharacter* OwnerCharacter;
    };

    UFUNCTION(BlueprintImplementableEvent)
    void OnBeginEquip();

    UFUNCTION(BlueprintImplementableEvent)
    void OnUnequip();

     

     

    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);
    }

    변경사항 없음.

     

     

     

    BP_CAttachment_Sword


     

     

    CAnimNotifyState_Equip 생성  +  Draw_Sword_Montage에 할당

     

    새 C++ 클래스 - AnimNotifyState - CAnimNotifyState_Equip 생성

     

     

    CAnimNotifyState_Equip.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Animation/AnimNotifies/AnimNotifyState.h"
    #include "CAnimNotifyState_Equip.generated.h"
    UCLASS()
    class U2212_06_API UCAnimNotifyState_Equip : public UAnimNotifyState
    {
    GENERATED_BODY()
    public:
    FString GetNotifyName_Implementation() const override;
    virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
    virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
    };

    NotifyBegin, NotifyEnd 오버라이드

    • virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
    • virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;

     

     

     

    CAnimNotifyState_Equip.cpp

    더보기
    #include "Notifies/CAnimNotifyState_Equip.h"
    #include "Global.h"
    #include "Components/CWeaponComponent.h"
    #include "Weapons/CEquipment.h"
    FString UCAnimNotifyState_Equip::GetNotifyName_Implementation() const
    {
    return "Equip";
    }
    void UCAnimNotifyState_Equip::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
    {
    Super::NotifyBegin(MeshComp, Animation, TotalDuration);
    CheckNull(MeshComp);
    CheckNull(MeshComp->GetOwner());
    UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(MeshComp->GetOwner());
    CheckNull(weapon);
    CheckNull(weapon->GetEquipment());
    weapon->GetEquipment()->Begin_Equip();
    }
    void UCAnimNotifyState_Equip::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
    {
    Super::NotifyEnd(MeshComp, Animation);
    CheckNull(MeshComp);
    CheckNull(MeshComp->GetOwner());
    UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(MeshComp->GetOwner());
    CheckNull(weapon);
    CheckNull(weapon->GetEquipment());
    weapon->GetEquipment()->End_Equip();
    }

     

     

     

    Draw_Sword_Montage

    AnimNotifyState_Equp 할당

     


     

     

     

    실행화면

     

     


     

     

     

     

     

     

    Sword Mode 애니메이션 

     


     

     

    CAnimInstance

     

    CAnimInstance.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Animation/AnimInstance.h"
    #include "Components/CWeaponComponent.h"
    #include "CAnimInstance.generated.h"
    UCLASS()
    class U2212_06_API UCAnimInstance : public UAnimInstance
    {
    GENERATED_BODY()
    protected:
    UPROPERTY(BlueprintReadOnly, Category = "Animation")
    float Speed;
    UPROPERTY(BlueprintReadOnly, Category = "Animation")
    float Pitch;
    UPROPERTY(BlueprintReadOnly, Category = "Animation")
    float Direction;
    protected:
    UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
    EWeaponType WeaponType = EWeaponType::Max;
    public:
    void NativeBeginPlay() override;
    void NativeUpdateAnimation(float DeltaSeconds) override;
    private:
    UFUNCTION()
    void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);
    private:
    class ACharacter* OwnerCharacter;
    class UCWeaponComponent* Weapon;
    private:
    FRotator PrevRotation;
    };

    헤더 추가

    • #include "Components/CWeaponComponent.h"

    변수 추가

    • UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
      EWeaponType WeaponType = EWeaponType::Max;
    • class UCWeaponComponent* Weapon;

    함수 추가

    •   UFUNCTION()
        void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);

     

     

     

    CAnimInstance.cpp

    더보기
    #include "Characters/CAnimInstance.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    void UCAnimInstance::NativeBeginPlay()
    {
    Super::NativeBeginPlay();
    OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());
    CheckNull(OwnerCharacter);
    Weapon = CHelpers::GetComponent<UCWeaponComponent>(OwnerCharacter);
    if (!!Weapon)
    Weapon->OnWeaponTypeChange.AddDynamic(this, &UCAnimInstance::OnWeaponTypeChanged);
    }
    void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
    {
    Super::NativeUpdateAnimation(DeltaSeconds);
    CheckNull(OwnerCharacter);
    Speed = OwnerCharacter->GetVelocity().Size2D();
    FRotator rotator = OwnerCharacter->GetVelocity().ToOrientationRotator();
    FRotator rotator2 = OwnerCharacter->GetControlRotation();
    FRotator delta = UKismetMathLibrary::NormalizedDeltaRotator(rotator, rotator2);
    PrevRotation = UKismetMathLibrary::RInterpTo(PrevRotation, delta, DeltaSeconds, 25);
    Direction = PrevRotation.Yaw;
    Pitch = UKismetMathLibrary::FInterpTo(Pitch, OwnerCharacter->GetBaseAimRotation().Pitch, DeltaSeconds, 25);
    }
    void UCAnimInstance::OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType)
    {
    WeaponType = InNewType;
    }

     


     

    ABP_Character

     

     

    AnimGraph

     


     

    실행화면

     

     

     


     

     

     

     

    기본공격 구현

     


     

     

    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;
    };
    USTRUCT()
    struct FDoActionData
    {
    GENERATED_BODY()
    public:
    UPROPERTY(EditAnywhere)
    class UAnimMontage* Montage;
    UPROPERTY(EditAnywhere)
    float PlayRate = 1;
    UPROPERTY(EditAnywhere)
    bool bCanMove = true;
    UPROPERTY(EditAnywhere)
    bool bFixedCamera;
    UPROPERTY(EditAnywhere)
    class UFXSystemAsset* Effect; //사용할 Effect 변수
    UPROPERTY(EditAnywhere)
    FVector EffectLocation = FVector::ZeroVector;//(Effect)지정 방향의 보정치.
    UPROPERTY(EditAnywhere)
    FVector EffectScale = FVector::OneVector;//Effect 크기 기본값 1 설정.
    public:
    void DoAction(class ACharacter* InOwner);
    };
    UCLASS()
    class U2212_06_API UCWeaponStructures : public UObject
    {
    GENERATED_BODY()
    };

    이펙트에 사용될 변수 추가

    • UPROPERTY(EditAnywhere)
      class UFXSystemAsset* Effect;                           //사용할 Effect 변수
      FVector EffectLocation = FVector::ZeroVector;   //(Effect)지정 방향의 보정치.
      FVector EffectScale = FVector::OneVector;        //Effect 크기 기본값 1 설정.

    기본 공격 구현을 함수 추가

    • void DoAction(class ACharacter* InOwner);

     

     

    CWeaponStructures.cpp

    더보기
    #include "Weapons/CWeaponStructures.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "Components/CStateComponent.h"
    #include "Components/CMovementComponent.h"
    #include "Animation/AnimMontage.h"
    void FDoActionData::DoAction(ACharacter* InOwner)
    {
    UCMovementComponent* movement = CHelpers::GetComponent<UCMovementComponent>(InOwner);
    if (!!movement)
    {
    if (bFixedCamera)
    movement->EnableFixedCamera();
    if (bCanMove == false)
    movement->Stop();
    }
    if (!!Montage)
    InOwner->PlayAnimMontage(Montage, PlayRate);
    }

     


     

     

    CDoAction 생성

     

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

     

     

    CDoAction.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Weapons/CWeaponStructures.h"
    #include "CDoAction.generated.h"
    UCLASS(Abstract)//DoAction 그 자체로는 객체화되면 안 되기 때문에 Abstract을 붙여준다.
    class U2212_06_API UCDoAction : public UObject
    {
    GENERATED_BODY()
    public:
    UCDoAction();
    virtual void BeginPlay //재정의 할 수 있도록 virtual로 만든다.
    (
    class ACAttachment* InAttachment,
    class UCEquipment* InEquipment,
    class ACharacter* InOwner,
    const TArray<FDoActionData>& InDoActionDatas
    );
    public:
    //재정의 할 수 있도록 virtual로 만든다.
    virtual void DoAction();
    virtual void Begin_DoAction();
    virtual void End_DoAction();
    protected:
    bool bBeginAction;
    class ACharacter* OwnerCharacter;
    class UWorld* World;
    class UCMovementComponent* Movement;
    class UCStateComponent* State;
    TArray<FDoActionData> DoActionDatas;
    };

    DoAction에 대한 수행역할을 전부 구조체에 넣어준다.

    • USTRUCT()
      struct FDoActionData

     

     

    CDoAction.cpp

    더보기
    #include "Weapons/CDoAction.h"
    #include "Global.h"
    #include "CAttachment.h"
    #include "CEquipment.h"
    #include "GameFramework/Character.h"
    #include "Components/CStateComponent.h"
    #include "Components/CMovementComponent.h"
    UCDoAction::UCDoAction()
    {
    }
    void UCDoAction::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionDatas)
    {
    OwnerCharacter = InOwner;
    World = OwnerCharacter->GetWorld();
    State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter);
    Movement = CHelpers::GetComponent<UCMovementComponent>(OwnerCharacter);
    DoActionDatas = InDoActionDatas;
    }
    void UCDoAction::DoAction()
    {
    State->SetActionMode();
    }
    void UCDoAction::Begin_DoAction()
    {
    bBeginAction = true;
    }
    void UCDoAction::End_DoAction()
    {
    bBeginAction = false;
    State->SetIdleMode();
    Movement->Move();
    Movement->DisableFixedCamera();
    }

     


     

     

     

    CDoAction_Combo 생성

     

    새 C++ 클래스 - CDoAction 기반 C++ - CDoAction_Combo 생성

     

    CDoAction_Combo.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Weapons/CDoAction.h"
    #include "CDoAction_Combo.generated.h"
    UCLASS()
    class U2212_06_API UCDoAction_Combo : public UCDoAction
    {
    GENERATED_BODY()
    public:
    FORCEINLINE void EnableCombo() { bEnable = true; }
    FORCEINLINE void DisableCombo() { bEnable = false; }
    public:
    void DoAction() override;
    void Begin_DoAction() override;
    void End_DoAction() override;
    private:
    int32 Index;
    bool bEnable;
    bool bExist;
    };

     

     

     

    CDoAction_Combo.cpp

    더보기
    #include "Weapons/DoActions/CDoAction_Combo.h"
    #include "Global.h"
    #include "GameFramework/Character.h"
    #include "Components/CStateComponent.h"
    void UCDoAction_Combo::DoAction()
    {
    CheckTrue(DoActionDatas.Num() < 1);
    if (bEnable) //bEnable이라면 Combo 구간
    {
    bEnable = false;
    bExist = true;
    return;
    }
    CheckFalse(State->IsIdleMode());
    Super::DoAction(); //첫 타격이 들어간 후에 Combo의 bEnable이 호출되어야 한다. 그래서 맨 위가 아닌 여기에 위치한다.//첫 타격 시 IsIdleMode()를 체크 통과한 후 부모의 DoAction으로 들어가 State->ActionMode()로 변경한다.
    DoActionDatas[Index].DoAction(OwnerCharacter);
    }
    void UCDoAction_Combo::Begin_DoAction()
    {
    Super::Begin_DoAction();
    CheckFalse(bExist);//다음 Combo가 없으면 바로 End_DoAction으로 이동.
    bExist = false;
    DoActionDatas[++Index].DoAction(OwnerCharacter);
    }
    void UCDoAction_Combo::End_DoAction()
    {
    Super::End_DoAction();
    Index = 0;
    }

     

     


     

     

     

    DA_Sword에 Do Action Class 할당

     

     


     

     

    실행화면