지난 시간에는 활 시위를 구부러뜨리는것을 구현하였다. 오늘은 활 시위를 플레이어 손에 붙였다가 화살 발사 시 손에 떨어뜨리는 것을 구현할 것이다. CDo_Action_Bow를 만들어 발사 시점과 끝난 시점을 구분지어 줄 것이다. CAnimNotifyState_BowString 만들어 손에서 떨어진 활 시위를 다시 붙여주는 시점에 몽타주에 삽입할 것이다.

 

 

목차

     

     


     

     

     

     
    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
      U2212_06
        Characters
        CAnimInstance.h .cpp
    CEnemy.h .cpp 
    CPlayer.h .cpp
    ICharacter.h .cpp
        Components
        CMontagesComponent.h .cpp 
    CMovementComponent.h .cpp 
    CStateComponent.h .cpp
    CStatusComponent.h .cpp  
    CWeaponComponent.h .cpp 
        Notifies
        CAnimNotifyState_BeginAction.h .cpp
    CAnimNotifyState_BowString.h .cpp 생성 
    CAnimNotify_CameraShake.h .cpp 
    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
        Utilities
        CHelper.h
    CLog.h .cpp
        Weapons
        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
     

     

     

     

     

    활 시위에 손 맞추기

     


     

     

    CAttachment_Bow - 캡처를 삭제하고 Do_Action_Bow쪽으로 옮긴다.

     

     

    CAttachment_Bow.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Weapons/CAttachment.h"
    #include "CAttachment_Bow.generated.h"
    
    UCLASS()
    class U2212_06_API ACAttachment_Bow : public ACAttachment
    {
        GENERATED_BODY()
    
    private:
        UPROPERTY(EditDefaultsOnly, Category = "View")
            FVector2D ViewPitchRange = FVector2D(-40, +30);//Pitch 제한각 설정
    
    private:
        UPROPERTY(VisibleAnywhere)
            class USkeletalMeshComponent* SkeletalMesh;
    
        UPROPERTY(VisibleAnywhere)
            class UPoseableMeshComponent* PoseableMesh;
    
    public:
        float* GetBend();
    
    public:
        ACAttachment_Bow();
    
    protected:
        virtual void BeginPlay() override;
    
    public:
        virtual void Tick(float DeltaTime) override;
    
    public:
        void OnBeginEquip_Implementation() override;
        void OnUnequip_Implementation() override;
    
    private:
        FVector2D OriginViewPitchRange;
    };

    변경사항 없음

     

     

     

    CAttachment_Bow.h

    더보기
    #include "Weapons/Attachments/CAttachment_Bow.h"
    #include "Global.h"
    #include "Weapons/AnimInstances/CAnimInstance_Bow.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Components/PoseableMeshComponent.h"
    
    ACAttachment_Bow::ACAttachment_Bow()
    {
    	PrimaryActorTick.bCanEverTick = true;//실시간 적용이 되도록 넣어준다. 안 넣어주면 활 시위가 구부러지는것이 업데이트 되지 않을 수도 있다.
    
    	CHelpers::CreateComponent<USkeletalMeshComponent>(this, &SkeletalMesh, "SkeletalMesh", Root);
    	CHelpers::CreateComponent<UPoseableMeshComponent>(this, &PoseableMesh, "PoseableMesh", Root);
    
    
    	USkeletalMesh* mesh;
    	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Weapons/ElvenBow/SK_ElvenBow.SK_ElvenBow'");
    	SkeletalMesh->SetSkeletalMesh(mesh);
    	SkeletalMesh->SetCollisionProfileName("NoCollision");
    
    	TSubclassOf<UCAnimInstance_Bow> animInstance;
    	CHelpers::GetClass<UCAnimInstance_Bow>(&animInstance, "AnimBlueprint'/Game/Weapons/Bow/ABP_ElvenBow.ABP_ElvenBow_C'");//ABP_ElvenBow 레퍼런스 복사하여 생성.
    	SkeletalMesh->SetAnimInstanceClass(animInstance);
    }
    
    void ACAttachment_Bow::BeginPlay()
    {
    	Super::BeginPlay();
    
    	AttachTo("Holster_Bow");
    
    	SkeletalMesh->SetVisibility(false);
    
    	PoseableMesh->SetSkeletalMesh(SkeletalMesh->SkeletalMesh);//SkeletalMesh 내의 SkeletalMesh 사용.
    	PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);//포즈를 캡처해둔다.
    }
    
    void ACAttachment_Bow::Tick(float DeltaTime)
    {
    	Super::Tick(DeltaTime);
    
    	//캡처를 Do_Action_Bow쪽으로 옮긴다.
    	//PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
    }
    
    void ACAttachment_Bow::OnBeginEquip_Implementation()
    {
    	Super::OnBeginEquip_Implementation();
    
    	AttachTo("Hand_Bow_Left");//Hand_Bow_Left 소켓에 장착.
    
    
    	APlayerController* controller = OwnerCharacter->GetController<APlayerController>();
    	if (!!controller)
    	{
    		OriginViewPitchRange.X = controller->PlayerCameraManager->ViewPitchMin;
    		OriginViewPitchRange.Y = controller->PlayerCameraManager->ViewPitchMax;
    
    		controller->PlayerCameraManager->ViewPitchMin = ViewPitchRange.X;
    		controller->PlayerCameraManager->ViewPitchMax = ViewPitchRange.Y;
    	}
    }
    
    void ACAttachment_Bow::OnUnequip_Implementation()
    {
    	Super::OnUnequip_Implementation();
    
    	AttachTo("Holster_Bow");//Holster_Bow 소켓에 장착.
    
    
    	APlayerController* controller = OwnerCharacter->GetController<APlayerController>();
    	if (!!controller)
    	{
    		controller->PlayerCameraManager->ViewPitchMin = OriginViewPitchRange.X;
    		controller->PlayerCameraManager->ViewPitchMax = OriginViewPitchRange.Y;
    	}
    }
    
    float* ACAttachment_Bow::GetBend()
    {
    	return Cast<UCAnimInstance_Bow>(SkeletalMesh->GetAnimInstance())->GetBend();//UCAnimInstance_Bow 안의 GetBend() 함수로 Bend값 리턴. UCAnimInstance_Bow 안의 Bend값을 사용한다.
    }

     

    void ACAttachment_Bow::Tick(float DeltaTime)

    • PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
      • 캡처를 삭제(위의 코드 삭제)하고 Do_Action_Bow쪽으로 옮긴다.

     


     

     

    CDoAction  -  OnBegineEquip(), OnUnequip() 함수 추가

     

     

    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,//CWeaponStucture내의 FDoActionData 
    		const TArray<FHitData>& InHitDatas //CWeaponStucture내의 FHitData
    	);
    	virtual void Tick(float InDeltaTime) { }
    
    public:
    	//재정의 할 수 있도록 virtual로 만든다.
    	virtual void DoAction();
    	virtual void Begin_DoAction();
    	virtual void End_DoAction();
    
    public:
    	//BeginEquip될 때와 Unequip될 때 충돌체를 제거해줘야 한다.
    	//CDoAction_Bow에서 재정의 시키기위해 virtual로 만든다.
    	UFUNCTION()
    		virtual void OnBeginEquip() { }
    	
    	UFUNCTION()
    		virtual void OnUnequip() { }
    
    public:
    	UFUNCTION()
    		virtual void OnAttachmentBeginCollision() {}
    
    	UFUNCTION()
    		virtual void OnAttachmentEndCollision() {}
    
    	UFUNCTION()
    		virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther) { }
    
    	UFUNCTION()
    		virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class ACharacter* InOther) { }
    
    protected:
    	bool bInAction;
    	bool bBeginAction;
    
    	class ACharacter* OwnerCharacter;
    	class UWorld* World;
    
    	class UCMovementComponent* Movement;
    	class UCStateComponent* State;
    
    	TArray<FDoActionData> DoActionDatas;
    	TArray<FHitData> HitDatas;
    };

    자식 클래스인 CDo_Action_Bow에서 재정의할 함수 추가

    • UFUNCTION()
      virtual void OnBeginEquip() { }
    • UFUNCTION()
      virtual void OnUnequip() { }

    CDoAction_Bow에서 재정의 시키기위해 virtual로 만든다. BeginEquip될 때와 Unequip될 때 충돌체를 제거해줘야 한다. 따라서 CDoAction_Bow에서 재정의할 때 OnBeginEquip()에 충돌을 꺼주고 OnUnequip() 시 충돌을 켜준다.

     

     

    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, const TArray<FHitData>& InHitDatas)
    {
    	OwnerCharacter = InOwner;
    	World = OwnerCharacter->GetWorld();
    
    	State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter);
    	Movement = CHelpers::GetComponent<UCMovementComponent>(OwnerCharacter);
    
    	DoActionDatas = InDoActionDatas;
    	HitDatas = InHitDatas;
    }
    
    void UCDoAction::DoAction()
    {
    	bInAction = true;
    
    	State->SetActionMode();
    }
    
    void UCDoAction::Begin_DoAction()
    {
    	bBeginAction = true;
    }
    
    void UCDoAction::End_DoAction()
    {
    	bInAction = false;
    	bBeginAction = false;
    
    	State->SetIdleMode();
    
    	Movement->Move();
    	Movement->DisableFixedCamera();
    }

    변경사항 없음.

     


     

     

     

    CDoAction_Bow 생성

     

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

     

     

    CDoAction_Bow.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Weapons/CDoAction.h"
    #include "CDoAction_Bow.generated.h"
    
    UCLASS(Blueprintable)
    class U2212_06_API UCDoAction_Bow : public UCDoAction
    {
    	GENERATED_BODY()
    
    public:
        UCDoAction_Bow();
    
        void BeginPlay
        (
            class ACAttachment* InAttachment,
            class UCEquipment* InEquipment,
            class ACharacter* InOwner,
            const TArray<FDoActionData>& InDoActionData,
            const TArray<FHitData>& InHitData
        ) override;
    
        void DoAction() override;
        void Begin_DoAction() override;
        void End_DoAction() override;
    
        void OnBeginEquip() override;
        void OnUnequip() override;
    
        void Tick(float InDeltaTime) override;
    
    public:
        void End_BowString();//AnimNotify_BowString의 Notify에서 콜하여 사용한다.
    
    private:
        class USkeletalMeshComponent* SkeletalMesh;
        class UPoseableMeshComponent* PoseableMesh;
    
    private:
        float* Bending;
    
    private:
        FVector OriginLocation;//처음 위치
        bool bAttachedString = true;//true면 활 시위가 손에 붙는다.
    
    private:
        const bool* bEquipped;
    };

     

     

     

    CDoAction_Bow.cpp

    더보기
    #include "Weapons/DoActions/CDoAction_Bow.h"
    #include "Global.h"
    #include "Weapons/CEquipment.h"
    #include "Weapons/Attachments/CAttachment_Bow.h"
    #include "GameFramework/Character.h"
    #include "Components/PoseableMeshComponent.h"
    #include "Components/CStateComponent.h"
    #include "Components/CMovementComponent.h"
    
    UCDoAction_Bow::UCDoAction_Bow()
    {
    	
    }
    
    void UCDoAction_Bow::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionData, const TArray<FHitData>& InHitData)
    {
    	Super::BeginPlay(InAttachment, InEquipment, InOwner, InDoActionData, InHitData);
    
    	SkeletalMesh = CHelpers::GetComponent<USkeletalMeshComponent>(InAttachment);
    	PoseableMesh = CHelpers::GetComponent<UPoseableMeshComponent>(InAttachment);
    
    	//CAnimInstance.h의 Bend변수를 CAttachment_Bow의 GetBend()함수를 거쳐 여기로 가져와서 사용한다.
    	ACAttachment_Bow* bow = Cast<ACAttachment_Bow>(InAttachment);
    	Bending = bow->GetBend();
    
    	OriginLocation = PoseableMesh->GetBoneLocationByName("bow_string_mid", EBoneSpaces::ComponentSpace);
    
    	bEquipped = InEquipment->GetEquipped();//헤더에 만든 bEquipped변수에 장착 상태를 받아와서 넣어준다.
    }
    
    void UCDoAction_Bow::DoAction()
    {
    	CheckFalse(State->IsIdleMode());
    	CheckFalse(State->IsSubActionMode());
    
    	Super::DoAction();
    
    	DoActionDatas[0].DoAction(OwnerCharacter);
    }
    
    void UCDoAction_Bow::Begin_DoAction()//활 발사
    {
    	Super::Begin_DoAction();
    
    	bAttachedString = false;//발사하면 활 시위가 손에서 떨어지도록 false로 만들어준다.
    
    
    	*Bending = 0;//활 구부림 0으로 만들어준다.
    	PoseableMesh->SetBoneLocationByName("bow_string_mid", OriginLocation, EBoneSpaces::ComponentSpace);//활 시위를 처음 위치로 돌아가게 만들어준다.
    }
    
    void UCDoAction_Bow::End_DoAction()
    {
    	Super::End_DoAction();
    
    }
    
    void UCDoAction_Bow::OnBeginEquip()
    {
    	Super::OnBeginEquip();
    
    	OwnerCharacter->GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);//충돌을 꺼준다.
    }
    
    void UCDoAction_Bow::OnUnequip()
    {
    	Super::OnUnequip();
    
    	*Bending = 0;//활 시위 휘어지는 Bending을 0으로 만든다.
    	OwnerCharacter->GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//충돌을 켜준다.
    
    	PoseableMesh->SetBoneLocationByName("bow_string_mid", OriginLocation, EBoneSpaces::ComponentSpace);//활 시위를 처음 상태로 돌려준다.
    }
    
    void UCDoAction_Bow::Tick(float InDeltaTime)
    {
    	Super::Tick(InDeltaTime);
    
    	PoseableMesh->CopyPoseFromSkeletalComponent(SkeletalMesh);
    
    	bool bCheck = true;
    	bCheck &= (*bEquipped == true);//장착 상태여야 한다.
    	bCheck &= (bBeginAction == false);//BeginAction이면(활 발사) 활 시위가 붙으면 안되므로 false여야 한다.
    	bCheck &= (bAttachedString == true);//활 시위 붙는게 true여야 한다.
    
    	CheckFalse(bCheck);
    
    	FVector handLocation = OwnerCharacter->GetMesh()->GetSocketLocation("Hand_Bow_Right");
    	PoseableMesh->SetBoneLocationByName("bow_string_mid", handLocation, EBoneSpaces::WorldSpace);
    }
    
    void UCDoAction_Bow::End_BowString()
    {
    	*Bending = 100;//활 시위를 땡겨 다시 휘어지게 만들어진다.
    	bAttachedString = true;//활 시위가 손에 붙는다.
    }

     

     

     


     

     

     

    AR_ShootAndDraw_Montage 생성

     

    AR_ShootAndDraw

     

     

    AR_ShootAndDraw_Montage

    Begin DoAction: 활 발사

    End_DoAction: 활 시위가 안 휘어지게 Bend를 0으로 만든다. 활 시위와 손이 떨어지게 만든다.

     

    End_BowString: 활 시위가 휘어지게 Bend를 100으로 만든다. 활 시위를 손에 붙인다.

     


     

     

     

    ABP_Character 

     

    기존에 만든부분을 Bose Pose로 저장한다.

     

    BowBody 슬롯을 만든다.

    • '본마다 레이어로 블랜딩합니다'으로 구분한다.

     

     


     

     

     

    DA_Bow에 DoAction Class와 DoAction Data 할당

     

     


     

     

    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(FEquipmentEquip);
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentBeginEquip);
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentUnequip);
    
    UCLASS()
    class U2212_06_API UCEquipment : public UObject
    {
    	GENERATED_BODY()
    
    public:
    	//실시간으로 bEquipped로 가져온다. 주소로 가져온다.
    	FORCEINLINE const bool* GetEquipped() { return &bEquipped; }
    
    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:
    	FEquipmentEquip OnEquipmentEquip;//DECLARE_DYNAMIC_MULTICAST_DELEGATE 하였다.
    	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이 완료되었는지 확인
    };

    델리게이트 추가

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

     

    변수 추가

    • FEquipmentEquip OnEquipmentEquip;
      • 위에 추가한 DECLARE_DYNAMIC_MULTICAST_DELEGATE에 변수로 사용하였다.

     

    인라인 함수 추가

    • FORCEINLINE const bool* GetEquipped() { return &bEquipped; }
      • 실시간으로 bEquipped로 가져온다. 주소로 가져온다.

     

     

     

    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 (OnEquipmentEquip.IsBound())//활 장착 확인?
    		OnEquipmentEquip.Broadcast();
    
    	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();
    }

    void UCEquipment::Equip_Implementation()

    • if (OnEquipmentEquip.IsBound())
      OnEquipmentEquip.Broadcast();
      • isBound() 함수는 delegate가 현재 bind되어 있는지 확인하는 함수이고, broadcast() 함수는 delegate를 호출하는 함수다.

     


     

     

    CWeaponAsset

     

     

    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;
    
    	UPROPERTY(EditAnywhere)
    		TSubclassOf<class UCDoAction> DoActionClass;
    
    	UPROPERTY(EditAnywhere)
    		TArray<FDoActionData> DoActionDatas; //CWeaopnStructure내의 FDoActionData
    
    	UPROPERTY(EditAnywhere)
    		TArray<FHitData> HitDatas; //CWeaopnStructure내의 FHitData
    
    	UPROPERTY(EditAnywhere)
    		TSubclassOf<class UCSubAction> SubActionClass;
    
    public:
    	FORCEINLINE class ACAttachment* GetAttachment() { return Attachment; }//외부에 생성된 것을 리턴해줌. 
    	FORCEINLINE class UCEquipment* GetEquipment() { return Equipment; }//외부에 생성된 것을 리턴해줌.
    	FORCEINLINE class UCDoAction* GetDoAction() { return DoAction; }//외부에 생성된 것을 리턴해줌.
    	FORCEINLINE class UCSubAction* GetSubAction() { return SubAction; }//외부에 생성된 것을 리턴해줌.
    
    public:
    	UCWeaponAsset();
    
    	void BeginPlay(class ACharacter* InOwner);
    
    private:
    	//UPROPERTY를 붙여 가비지 콜렉터가 제거하기 전까지 물고 있게 만든다.
    	//UWeaponAsset은 UObject로부터 상속받아 Actor의 생성주기에 영향을 받지 않아 가비지 콜렉터에 영향을 받는다.
    	UPROPERTY() 
    		class ACAttachment* Attachment;
    
    	UPROPERTY()
    		class UCEquipment* Equipment;
    
    	UPROPERTY()
    		class UCDoAction* DoAction;
    
    	UPROPERTY()
    		class UCSubAction* SubAction;
    
    #if WITH_EDITOR //Editor 내에서만 수행
    	void PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent) override;
    #endif
    };

    변경사항 없음

     

     

     

    CWeaponAsset.cpp

    더보기
    #include "Weapons/CWeaponAsset.h"
    #include "Global.h"
    #include "CAttachment.h"
    #include "CEquipment.h"
    #include "CDoAction.h"
    #include "CSubAction.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)//EquipmentClass가 선택되어 있다면
    	{
    		Equipment = NewObject<UCEquipment>(this, EquipmentClass);
    		Equipment->BeginPlay(InOwner, EquipmentData);
    
    		if (!!Attachment)//Attachment가 있다면
    		{
    			Equipment->OnEquipmentBeginEquip.AddDynamic(Attachment, &ACAttachment::OnBeginEquip);
    			Equipment->OnEquipmentUnequip.AddDynamic(Attachment, &ACAttachment::OnUnequip);
    		}
    	}
    	 
    	if(!!DoActionClass)
    	{
    		DoAction = NewObject<UCDoAction>(this, DoActionClass);
    		DoAction->BeginPlay(Attachment, Equipment, InOwner, DoActionDatas, HitDatas);
    
    		if (!!Attachment)
    		{
    			Attachment->OnAttachmentBeginCollision.AddDynamic(DoAction, &UCDoAction::OnAttachmentBeginCollision);
    			Attachment->OnAttachmentEndCollision.AddDynamic(DoAction, &UCDoAction::OnAttachmentEndCollision);
    
    			Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &UCDoAction::OnAttachmentBeginOverlap);
    			Attachment->OnAttachmentEndOverlap.AddDynamic(DoAction, &UCDoAction::OnAttachmentEndOverlap);
    		}
    
    		if (!!Equipment)//Bow_String 작업
    		{
    			Equipment->OnEquipmentBeginEquip.AddDynamic(DoAction, &UCDoAction::OnBeginEquip);
    			Equipment->OnEquipmentUnequip.AddDynamic(DoAction, &UCDoAction::OnUnequip);
    		}
    	}
    
    	if(!!SubActionClass)
    	{
    		SubAction = NewObject<UCSubAction>(this, SubActionClass);
    		SubAction->BeginPlay(InOwner, Attachment, DoAction);
    	}
    }
    
    #if WITH_EDITOR //Editor 내에서만 수행
    void UCWeaponAsset::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
    {
    	Super::PostEditChangeChainProperty(PropertyChangedEvent);
    	CheckTrue(FApp::IsGame());//게임이 실행중이면 실행하면 안 되기 때문에 체크
    
    	bool bRefresh = false;
    	bRefresh |= PropertyChangedEvent.GetPropertyName().Compare("DoActionDatas") == 0;
    	bRefresh |= PropertyChangedEvent.GetPropertyName().Compare("HitDatas") == 0;//수정하려는 변수명 == 0 이면 이름이 동일하다는 의미.
    
    	if (bRefresh)
    	{
    		bool bCheck = false;
    		bCheck |= PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd;
    		bCheck |= PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayRemove;
    		bCheck |= PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayClear;
    		bCheck |= PropertyChangedEvent.ChangeType == EPropertyChangeType::Duplicate;
    
    		if (bCheck)
    		{
    			FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");//WITH_EDITOR로 Editor 내에서 수행하기 때문에 사용 가능.
    			TSharedPtr<IDetailsView> detailsView = prop.FindDetailView("WeaponAssetEditorDetailsView");//WeaponAssetEditor.cpp에서 설정한 arg.ViewIdentifier이름 WeaponAssetEditorDetailsView 사용. WeaponAssetEditorDetailsView를 찾는다.
    
    			if (detailsView.IsValid())//detailsView 창이 그려졌다면
    				detailsView->ForceRefresh();//새로고침
    		}
    	}
    }
    #endif

    함수 내용 추가

    void UCWeaponAsset::BeginPlay(ACharacter* InOwner)

    • if(!!DoActionClass) //DoAction 클래스가 있다면
      • if (!!Equipment) //Equipment가 true라면
        • Equipment->OnEquipmentBeginEquip.AddDynamic(DoAction, &UCDoAction::OnBeginEquip);
        • Equipment->OnEquipmentUnequip.AddDynamic(DoAction, &UCDoAction::OnUnequip);
        • OnBegineEquip과 OnUnequip함수를 델리게이트 연결처리해준다.

     


     

     

     

    CAnimNotify_BowString 생성

     

    새 C++ 클래스- AnimNotify - CAnimNotify_BowString 생성 

     

     

    CAnimNotify_BowString.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Animation/AnimNotifies/AnimNotify.h"
    #include "CAnimNotify_BowString.generated.h"
    
    UCLASS()
    class U2212_06_API UCAnimNotify_BowString : public UAnimNotify
    {
    	GENERATED_BODY()
    
    public:
    	FString GetNotifyName_Implementation() const override;
    
    	void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
    };

     

     

     

     

    CAnimNotify_BowString.cpp

    더보기
    #include "Notifies/CAnimNotify_BowString.h"
    #include "Global.h"
    #include "Components/CWeaponComponent.h"
    #include "Weapons/DoActions/CDoAction_Bow.h"
    
    FString UCAnimNotify_BowString::GetNotifyName_Implementation() const
    {
    	return "End_BowString";
    }
    
    void UCAnimNotify_BowString::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
    {
    	Super::Notify(MeshComp, Animation);
    	CheckNull(MeshComp);
    	CheckNull(MeshComp->GetOwner());
    
    	UCWeaponComponent* weapon = CHelpers::GetComponent<UCWeaponComponent>(MeshComp->GetOwner());
    	CheckNull(weapon);
    	CheckNull(weapon->GetDoAction());
    
    	UCDoAction_Bow* bow = Cast<UCDoAction_Bow>(weapon->GetDoAction());
    	CheckNull(bow);
    
    	bow->End_BowString();//CDoAction_Bow의 End_BowString을 콜하여 활 시위를 휘게 만들고 손에 붙인다.
    }

     

     

     

     


     

     

    실행화면

     

     

     


     

     

    '⭐ Unreal Engine > UE RPG Skill' 카테고리의 다른 글

    [UE] 파쿠르 구현하기  (0) 2023.07.18
    [UE] 화살  (0) 2023.07.13
    [UE] 활 시위 당기기  (0) 2023.07.07
    [UE] 활 조준  (0) 2023.07.06
    [UE] 활 구현하기  (0) 2023.07.05