권총을 추가했다. Hand IK를 적용하여 손이 권총에 자연스럽게 부착되게 하였다. 탄창을 빼고 장착하는 모션까지 추가하여 게임의 재미를 더했다.

 

목차

     

     


     

     

     

     
    Characters
      CAnimInstance.h .cpp
    CAnimInstance_Arms.h .cpp
    CPlayer.h .cpp
    Utilities
      CHelpers.h
    CLog.h .cpp
    Weapons
      CBullet.h .cpp
    CMagazine.h .cpp 
    CWeapon.h .cpp

    CWeapon_AK47.h .cpp
    CWeapon_AR4.h .cpp
    CWeapon_Pistol.h .cpp 
    CWeaponComponent.h .cpp
    Widget
      CUserWidget_CrossHair.h .cpp
    CUserWidget_HUD.h .cpp

    CGameMode.h .cpp
    Global.h
    CAnimNotifyState_Equip.h .cpp 
    CAnimNotifyState_Reload.h .cpp

     

     

     

     

    Animation & Montage 추가 및 수정

     

     


     

    SK_Mannequin_Arms_Skeleton

     

    소켓 추가 - Pistol_RightHand

     


     

     

    Animation 추가

     

     


     

    Pistol_Equip_Montage 생성

     

    몽타주

    • UpperBody

    노티파이

    • Equp 추가

    커브

    • 커브 생성 - LeftHand

     


     

     

    Pistol_Reload_Montage 생성

     

     

     


     

     

    SK_Mannequin_Arms_Skeleton 릭 셋업 + Pistol_Idle1 생성

     

    SK_Mannequin_Arms_Skeleton 릭 셋업

     

    Pistol_Idle를 복사하여 Pistol_Idle1 생성

    애님 에셋 리타깃 - 애님애셋 복제후 리타깃 - Pistol_Idle_Retarget 생성

     


     

     

    Pistol_Idle_Retarget 생성 및 트랜스레이션 리타기팅 + Pelvis 키 생성

     

    옵션 - 리타기팅 옵션 표시

    spine_01 우클릭 - 트랜스레이션 리타기팅 재귀적으로 설정

    Thigh_L 우클릭 - 트랜스레이션 리타기팅 재귀적으로 설정

    Thigh_R 우클릭 - 트랜스레이션 리타기팅 재귀적으로 설정

     

     

    Pelvis 키 생성

    • pelvis 회전. 롤 값 주기: (0, 90)

     

     

     

    Bone 구조 및 리타기팅 설명

     

    Animation 이동값 = 월드 공간 이동값

     

    Skeleton 이동값 = 부모의 이동값

     

     

     

     

     

     

           
    Root 가상 (Identity) Skeleton Local * Animation 가상(Identity)
    -Pelvis Skeleton Local * Animation Pelvis의 경우 부모가 의미가 없다.
       -spine_01 Animation Local * Animation * Parent  
          - clavicle_l Animation Local * Animation * Parent  
              ... Animation Local * Animation * Parent  

     

     


     

     

     

     

     

     

    Anim Blueprint 애니메이션 블루프린트

     


     

     

    ABP_Player

     

    PistolLayer

    Zoom In

     

     

    AnimGraph

     

     


     

     

    ABP_Player_Arms

     

    AnimGraph

    CAnimInstance_Arms에서 만든 변수

    • EWeaponType WeaponType
    • FTransform LeftHandTransform
    • 해당 2개를 가져다가 사용한다.

     


     

     

    실행화면

     

     

     


     

     

     

    UI 수정

     


     

     

    WB_HUD

     

    그래프

    CUserWidget_HUD에서 추가한 Update Weapon Type 이벤트 사용

    • 해당 이벤트가 적용되면 무기가 바뀔 때  HUD도 무기타입이 업데이트 되어 보인다. 

     


     

     

     

     

     

    코드 적용

     


     

     

    CAnimInstance_Arms

     

    CAnimInstance_Arms.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Animation/AnimInstance.h"
    #include "Weapons/CWeaponComponent.h"
    #include "CAnimInstance_Arms.generated.h"
    
    UCLASS()
    class U2212_04_API UCAnimInstance_Arms : public UAnimInstance
    {
    	GENERATED_BODY()
    
    protected:
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Weapons")
    		EWeaponType WeaponType = EWeaponType::Max;
    
    	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Weapons")
    		FTransform LeftHandTransform; //왼손 이동
    
    public:
    	void NativeBeginPlay() override;
    	void NativeUpdateAnimation(float DeltaSeconds) override;
    
    private:
    	UFUNCTION()
    		void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);
    
    private:
    	class ACPlayer* OwnerCharacter;
    	class UCWeaponComponent* Weapon;
    };

    변수 생성

    • FTransform LeftHandTransform

     

     

    CAnimInstance_Arms.cpp

    더보기
    #include "Character/CAnimInstance_Arms.h"
    #include "Global.h"
    #include "CPlayer.h"
    
    void UCAnimInstance_Arms::NativeBeginPlay()
    {
    	Super::NativeBeginPlay();
    
    	OwnerCharacter = Cast<ACPlayer>(TryGetPawnOwner());
    	CheckNull(OwnerCharacter);
    
    	Weapon = CHelpers::GetComponent<UCWeaponComponent>(OwnerCharacter);
    	CheckNull(Weapon);
    
    	Weapon->OnWeaponTypeChanged.AddDynamic(this, &UCAnimInstance_Arms::OnWeaponTypeChanged);
    }
    
    void UCAnimInstance_Arms::NativeUpdateAnimation(float DeltaSeconds)
    {
    	Super::NativeUpdateAnimation(DeltaSeconds);
    	CheckNull(OwnerCharacter);
    
    	LeftHandTransform = Weapon->GetLeftHandTransform(); //왼손 이동
    }
    
    void UCAnimInstance_Arms::OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType)
    {
    	WeaponType = InNewType;
    }

    NativeUpdateaAnimaton에 왼손 이동 추가

    • LeftHandTransform = Weapon->GetLeftHandTransform();

     


     

     

    CWeaponComponent

     

    CWeaponComponent.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "Components/ActorComponent.h"
    #include "CWeaponComponent.generated.h"
    
    //BlueprintType을 써서 BP에서 공개되게 한다. 추후에 Animation BP에서 다루어야 한다. 추후에 switch로 Weapon Blending을 할 것이다.
    UENUM(BlueprintType) 
    enum class EWeaponType : uint8
    {
    	AR4, AK47, Pistol, Max,
    };
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType);
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWeaponAim_Arms_Begin, class ACWeapon*, InThisWeapon);
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FWeaponAim_Arms_End);
    
    UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class U2212_04_API UCWeaponComponent : public UActorComponent
    {
    	GENERATED_BODY()
    
    private:
    	UPROPERTY(EditAnywhere, Category = "Settings")
    		TArray<TSubclassOf<class ACWeapon>> WeaponClasses; //Spawn시킬 때 class 타입을 TSubclassOf로 명시해준다. TArray는 그것에 대한 배열이다.
    
    	UPROPERTY(EditAnywhere, Category = "Settings")
    		TSubclassOf<class UCUserWidget_HUD> HUDClass;
    
    public:
    	FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; }//Max는 아무것도 선택안한 상황.
    	FORCEINLINE bool IsAR4Mode() { return Type == EWeaponType::AR4; }
    	FORCEINLINE bool IsAK47Mode() { return Type == EWeaponType::AK47; }
    	FORCEINLINE bool IsPistolMode() { return Type == EWeaponType::Pistol; }
    
    public:	
    	// Sets default values for this component's properties
    	UCWeaponComponent();
    
    protected:
    	// Called when the game starts
    	virtual void BeginPlay() override;
    
    public:	
    	// Called every frame
    	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
    
    private:
    	class ACWeapon* GetCurrWeapon();//현재 무기를 리턴
    
    public:
    	void SetUnarmedMode();
    	void SetAR4Mode();
    	void SetAK47Mode();
    	void SetPistolMode();
    
    private:
    	void SetMode(EWeaponType InType);
    	void ChangeType(EWeaponType InType);
    
    public:
    	void Begin_Equip();
    	void End_Equip();
    
    public:
    	void Begin_Aim();
    	void End_Aim();
    
    public:
    	void Begin_Fire();
    	void End_Fire();
    
    public:
    	bool IsInAim();
    	FVector GetLeftHandLocation();
    	FTransform GetLeftHandTransform();
    
    public:
    	void ToggleAutoFire(); //연사 토글
    
    public:
    	void Reload(); //재장전
    	void Eject_Magazine();
    	void Spawn_Magazine();
    	void Load_Magazine();
    	void End_Reload();
    
    
    private:
    	UFUNCTION()
    		void On_Begin_Aim(class ACWeapon* InThisWeapon);
    
    	UFUNCTION()
    		void On_Begin_End();
    
    public:
    	FWeaponTypeChanged OnWeaponTypeChanged;
    	FWeaponAim_Arms_Begin OnWeaponAim_Arms_Begin;
    	FWeaponAim_Arms_End OnWeaponAim_Arms_End;
    
    private:
    	EWeaponType Type = EWeaponType::Max; //Max는 아무것도 선택안한 상황.
    
    private:
    	class ACPlayer* Owner; //플레이어 변수
    	TArray<class ACWeapon*> Weapons; //무기 배열 변수
    	class UCUserWidget_HUD* HUD;
    };

    함수 추가

    • FTransform GetLeftHandTransform(); //왼손 이동

     

     

     

    CWeaponComponent.cpp

    더보기
    #include "Weapons/CWeaponComponent.h"
    #include "Global.h"
    #include "CWeapon.h"
    #include "Character/CPlayer.h"
    #include "CWeaponComponent.h" //여기
    #include "Widgets/CUserWidget_HUD.h"
    
    UCWeaponComponent::UCWeaponComponent()
    {
    	PrimaryComponentTick.bCanEverTick = true;
    
    	CHelpers::GetClass<UCUserWidget_HUD>(&HUDClass, "WidgetBlueprint'/Game/Widgets/WB_HUB.WB_HUB_C'");
    }
    
    void UCWeaponComponent::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Owner = Cast<ACPlayer>(GetOwner());
    	CheckNull(Owner);
    
    	FActorSpawnParameters params;
    	params.Owner = Owner;
    	params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    	//받은 타입을 Spawn 시킨다.
    	for(TSubclassOf<ACWeapon> weaponClass : WeaponClasses)
    	{
    		if(!!weaponClass)
    		{
    			ACWeapon* weapon = Owner->GetWorld()->SpawnActor<ACWeapon>(weaponClass, params);//weaponClass 타입으로 Spawn시킨다.
    			Weapons.Add(weapon);
    		}
    	}
    
    	if(!!HUDClass)
    	{
    		HUD = CreateWidget<UCUserWidget_HUD, APlayerController>(Owner->GetController<APlayerController>(), HUDClass);
    		HUD->AddToViewport(); //Viewport에 추가
    		HUD->SetVisibility(ESlateVisibility::Hidden);//숨김
    	}
    
    	OnWeaponAim_Arms_Begin.AddDynamic(this, &UCWeaponComponent::On_Begin_Aim);
    	OnWeaponAim_Arms_End.AddDynamic(this, &UCWeaponComponent::On_Begin_End);
    }
    
    // Called every frame
    void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
    	if (!!HUD)
    	{
    		if (!!GetCurrWeapon()) //현재 무기가 있다면
    			GetCurrWeapon()->IsAutoFire() ? HUD->OnAutoFire() : HUD->OffAutoFire(); //연사 3항 연산자 OnOff 스위치
    		else //현재 무기가 없다면
    			HUD->OffAutoFire(); //연사 해제
    
    		if(!!GetCurrWeapon()) //현재 무기가 있다면
    		{
    			uint8 currCount = GetCurrWeapon()->GetCurrMagazineCount(); //현재 무기의 현재 탄 수
    			uint8 maxCount = GetCurrWeapon()->GetMaxMagazineCount(); //현재 무기의 최대 탄 수
    
    			HUD->UpdateMagazine(currCount, maxCount); //HUD에 탄 수 업데이트
    
    			HUD->UpdateWeaponType(Type); // HUD에 무기타입 업데이트
    		}
    	}
    
    }
    
    ACWeapon* UCWeaponComponent::GetCurrWeapon()
    {
    	CheckTrueResult(IsUnarmedMode(), nullptr);//IsUnarmedMode가 true면 무기 리턴해줄수 없으므로 nullptr로 리턴 
    
    	return Weapons[(int32)Type]; //BP의 to int //현재 선택된 Weapon을 리턴해준다.
    }
    
    void UCWeaponComponent::SetUnarmedMode()
    {
    	CheckFalse(GetCurrWeapon()->CanUnequip());//현재 무기가 해제될 수 있는지 체크
    
    	GetCurrWeapon()->Unequip(); //무기 해제
    	ChangeType(EWeaponType::Max);//현재 무기를 Max로 바꾸어줌(Max는 아무것도 선택안한 상태)
    
    	HUD->SetVisibility(ESlateVisibility::Hidden);//무기가 없을때 화면에서 숨겨준다.
    }
    
    void UCWeaponComponent::SetAR4Mode()
    {
    	SetMode(EWeaponType::AR4);
    }
    
    void UCWeaponComponent::SetAK47Mode()
    {
    	SetMode(EWeaponType::AK47);
    }
    
    void UCWeaponComponent::SetPistolMode()
    {
    	SetMode(EWeaponType::Pistol);
    }
    
    void UCWeaponComponent::SetMode(EWeaponType InType)
    {
    	if(Type == InType) //현재 무기와 장착하려는 무기가 같은 경우
    	{
    		SetUnarmedMode(); //무기 장착 해제
    
    		return; //무기 장착 해제 후 리턴
    	}
    	else if (IsUnarmedMode() == false)
    	{
    		CheckFalse(GetCurrWeapon()->CanUnequip());//현재 무기가 해제될 수 있는지 체크
    
    		GetCurrWeapon()->Unequip(); //무기 해제
    	}
    
    	CheckNull(Weapons[(int32)InType]); //장착될 무기가 있는지 체크
    	CheckFalse(Weapons[(int32)InType]->CanEquip());//장착될 무기가 장착될 수 있는 상황인지 체크
    
    	Weapons[(int32)InType]->Equip(); // 무기 장착
    
    	ChangeType(InType); //무기 변경 알려줌
    
    	if (!!HUD)
    		HUD->SetVisibility(ESlateVisibility::Visible);//화면에 보이도록 켜줌.
    
    }
    
    void UCWeaponComponent::ChangeType(EWeaponType InType)
    {
    	EWeaponType type = Type;
    	Type = InType; //기존의 무기 타입에서 바뀐 무기 타입을 넣어줌.
    
    	if (OnWeaponTypeChanged.IsBound())
    		OnWeaponTypeChanged.Broadcast(type, InType);//기존 무기 타입, 바뀐 무기 타입
    	
    }
    
    void UCWeaponComponent::Begin_Equip()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Begin_Equip();
    }
    
    void UCWeaponComponent::End_Equip()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->End_Equip();
    }
    
    void UCWeaponComponent::Begin_Aim()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Begin_Aim();
    }
    
    void UCWeaponComponent::End_Aim()  
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->End_Aim();
    }
    
    void UCWeaponComponent::Begin_Fire()
    {
    	CheckNull(GetCurrWeapon());
    	CheckFalse(GetCurrWeapon()->CanFire());
    
    	GetCurrWeapon()->Begin_Fire();
    }
    
    void UCWeaponComponent::End_Fire()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->End_Fire();
    }
    
    bool UCWeaponComponent::IsInAim()
    {
    	CheckNullResult(GetCurrWeapon(), false);
    
    	return GetCurrWeapon()->IsInAim();
    }
    
    FVector UCWeaponComponent::GetLeftHandLocation()
    {
    	CheckNullResult(GetCurrWeapon(), FVector::ZeroVector);
    
    	return GetCurrWeapon()->GetLeftHandLocation();
    }
    
    FTransform UCWeaponComponent::GetLeftHandTransform()
    {
    	CheckNullResult(GetCurrWeapon(), FTransform());
    	//왼손 이동정보 리턴
    	return GetCurrWeapon()->GetArmsLeftHandTransform();
    }
    
    void UCWeaponComponent::ToggleAutoFire()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->ToggleAutoFire();
    }
    
    void UCWeaponComponent::Reload()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Reload();
    }
    
    void UCWeaponComponent::Eject_Magazine()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Eject_Magazine();
    }
    
    void UCWeaponComponent::Spawn_Magazine()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Spawn_Magazine();
    }
    
    void UCWeaponComponent::Load_Magazine()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->Load_Magazine();
    }
    
    void UCWeaponComponent::End_Reload()
    {
    	CheckNull(GetCurrWeapon());
    
    	GetCurrWeapon()->End_Reload();
    }
    
    void UCWeaponComponent::On_Begin_Aim(ACWeapon * InThisWeapon)
    {
    	for (ACWeapon* weapon : Weapons)
    	{
    		if (weapon == InThisWeapon)
    			continue;
    
    		weapon->SetHidden(true);
    	}
    }
    
    void UCWeaponComponent::On_Begin_End()
    {
    	for (ACWeapon* weapon : Weapons)
    		weapon->SetHidden(false);
    }

    함수 정의

    • FTransform UCWeaponComponent::GetLeftHandTransform() {
      CheckNullResult(GetCurrWeapon(), FTransform());
      return GetCurrWeapon()->GetArmsLeftHandTransform(); }

     


     

     

    CWeapon

     

    CWeapon.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "CWeapon.generated.h"
    
    //직렬화 해야하는 struct는 클래스 사이에 선언해야 한다.
    USTRUCT()
    struct FWeaponAimData
    {
    	GENERATED_BODY()
    
    public:
    	UPROPERTY(EditAnywhere)
    		float TargetArmLength;
    
    	UPROPERTY(EditAnywhere)
    		FVector SocketOffset;
    
    	UPROPERTY(EditAnywhere)
    		float FieldOfView;
    
    	UPROPERTY(EditAnywhere)
    		bool bEnableCameraLag;
    
    public:
    	void SetData(class ACharacter* InOwner);
    	void SetDataByNoneCurve(class ACharacter* InOwner);
    
    };
    
    //abstract를 쓰면 Unreal Editor에서 추상 베이스 클래스로 취급하겠다는 의미. 추상이되면 배치시킬 수 없다.
    UCLASS(abstract) 
    class U2212_04_API ACWeapon : public AActor
    {
    	GENERATED_BODY()
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		FName HolsterSocketName;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		class UAnimMontage* EquipMontage;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		float EquipMontage_PlayRate;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		FName RightHandSocketName;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Equip")
    		FVector LeftHandLocation;
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		FWeaponAimData BaseData;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		FWeaponAimData AimData;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		class UCurveFloat* AimCurve;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Aim")
    		float AimingSpeed = 200;
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Hit")
    		float HitDistance = 3000;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Hit")
    		class UMaterialInstanceConstant* HitDecal;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Hit")
    		class UParticleSystem* HitParticle;
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		class UParticleSystem* FlashParticle;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		class UParticleSystem* EjectParticle;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		class USoundWave* FireSound;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		float RecoilAngle; //Cone의 각도
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		TSubclassOf<class UMatineeCameraShake> CameraShakeClass;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		float AutoFireInterval; //연사 시간간격
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		float RecoilRate; //반동률
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		float SpreadSpeed; //얼마나 빨리 벌려질거냐
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		float MaxSpreadAlignment; //얼마나 넓게 벌려질거냐
    
    	UPROPERTY(EditDefaultsOnly, Category = "Fire")
    		TSubclassOf<class ACBullet> BulletClass;
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "UI")
    		TSubclassOf<class UCUserWidget_CrossHair> CrossHairClass; //CrossHair
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Magazine")
    		uint8 MaxMagazineCount;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Magazine")
    		class UAnimMontage* ReloadMontage;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Magazine")
    		float ReloadMontage_PlayRate;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Magazine")
    		FName MagazineBoneName; //탄창이 숨겨졌다 보이게 바뀌는 BoneName
    
    	UPROPERTY(EditDefaultsOnly, Category = "Magazine")
    		TSubclassOf<class ACMagazine> MagazineClass;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Magazine")
    		FName MagazineSocketName;
    
    protected:
    	UPROPERTY(EditDefaultsOnly, Category = "Arms")
    		FTransform ArmsMeshTransform;
    
    	UPROPERTY(EditDefaultsOnly, Category = "Arms")
    		FTransform ArmsLeftHandTransform;
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class USceneComponent* Root;
    
    protected:
    	UPROPERTY(VisibleAnywhere)
    		class USkeletalMeshComponent* Mesh;
    
    private:
    	UPROPERTY(VisibleAnywhere)
    		class UTimelineComponent* Timeline;
    
    public:
    	FORCEINLINE bool IsInAim() { return bInAim; }
    	FORCEINLINE bool IsAutoFire() { return bAutoFire; }
    	FORCEINLINE FVector GetLeftHandLocation() { return LeftHandLocation; }
    	FORCEINLINE FTransform GetArmsLeftHandTransform() { return ArmsLeftHandTransform; }
    
    	FORCEINLINE uint8 GetCurrMagazineCount() { return CurrMagazineCount; } //현재 탄 수
    	FORCEINLINE uint8 GetMaxMagazineCount() { return MaxMagazineCount; } //최대 탄 수
    
    public:	
    	ACWeapon();
    
    protected:
    	virtual void BeginPlay() override;
    
    public:	
    	virtual void Tick(float DeltaTime) override;
    
    public:
    	bool CanEquip();
    	void Equip();
    	virtual void Begin_Equip();
    	virtual void End_Equip();
    
    	bool CanUnequip();
    	void Unequip();
    
    public:
    	bool CanAim();
    	virtual void Begin_Aim();
    	virtual void End_Aim();
    
    private:
    	UFUNCTION()
    		void OnAiming(float Output); //조준
    
    public:
    	bool CanFire();
    	void Begin_Fire();
    	void End_Fire();
    
    private:
    	UFUNCTION()
    		void OnFiring(); //연사
    
    public:
    	void ToggleAutoFire(); //연사 On Off 토글
    
    public:
    	bool CanReload();
    	void Reload();
    	
    	void Eject_Magazine();
    	void Spawn_Magazine();
    	void Load_Magazine();
    	void End_Reload();
    
    private:
    	bool bEquipping;
    	bool bInAim;
    	bool bFiring;
    	bool bReload;
    	bool bAutoFire = true;
    
    protected:
    	class ACPlayer* Owner;
    
    private:
    	FTimerHandle AutoFireHandle; //타이머 핸들 추가
    
    protected: //자식에서 CrossHair 접근하게 하기위해 protected로 설정 
    	class UCUserWidget_CrossHair* CrossHair;
    
    private:
    	float CurrSpreadRadius;
    	float LastAddSpreadTime; 
    
    private:
    	uint8 CurrMagazineCount; //현재 탄 수
    
    private:
    	class ACMagazine* Magazine;
    };

    인라인 함수 추가

    • FORCEINLINE FTransform GetArmsLeftHandTransform() { return ArmsLeftHandTransform; }

     

     

     

    CWeapon.cpp

    더보기
    #include "Weapons/CWeapon.h"
    #include "Global.h"
    #include "CBullet.h"
    #include "CMagazine.h"
    #include "Character/CPlayer.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Components/TimelineComponent.h"
    #include "Components/DecalComponent.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "Camera/CameraComponent.h"
    #include "Camera/CameraShake.h"
    #include "Materials/MaterialInstanceConstant.h"
    #include "Particles/ParticleSystem.h"
    #include "Widgets/CUserWidget_CrossHair.h"
    
    void FWeaponAimData::SetData(ACharacter* InOwner)
    {
    	USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
    	springArm->TargetArmLength = TargetArmLength;
    	springArm->SocketOffset = SocketOffset;
    	springArm->bEnableCameraLag = bEnableCameraLag;
    }
    
    void FWeaponAimData::SetDataByNoneCurve(ACharacter* InOwner)
    {
    	USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
    	springArm->TargetArmLength = TargetArmLength;
    	springArm->SocketOffset = SocketOffset;
    	springArm->bEnableCameraLag = bEnableCameraLag;
    
    	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(InOwner);
    	camera->FieldOfView = FieldOfView; //curve를 이용하여 FieldOfView를 만들어준다. 
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    ACWeapon::ACWeapon()
    {
    	PrimaryActorTick.bCanEverTick = true;
    
    	CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
    	CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Mesh, "Mesh", Root);
    
    	CHelpers::CreateActorComponent<UTimelineComponent>(this, &Timeline, "Timeline");
    	//CurveFloat 
    	CHelpers::GetAsset<UCurveFloat>(&AimCurve, "CurveFloat'/Game/Character/Weapons/Curve_Aim.Curve_Aim'");
    
    	CHelpers::GetAsset<UMaterialInstanceConstant>(&HitDecal, "MaterialInstanceConstant'/Game/Materials/M_Decal_Inst.M_Decal_Inst'");
    	CHelpers::GetAsset<UParticleSystem>(&HitParticle, "ParticleSystem'/Game/Effects/P_Impact_Default.P_Impact_Default'");
    
    	CHelpers::GetAsset<UParticleSystem>(&FlashParticle, "ParticleSystem'/Game/Effects/P_Muzzleflash.P_Muzzleflash'");
    	CHelpers::GetAsset<UParticleSystem>(&EjectParticle, "ParticleSystem'/Game/Effects/P_Eject_bullet.P_Eject_bullet'");
    	CHelpers::GetAsset<USoundWave>(&FireSound, "SoundWave'/Game/Sounds/S_RifleShoot.S_RifleShoot'");
    	CHelpers::GetClass<ACBullet>(&BulletClass, "Blueprint'/Game/Weapons/BP_CBullet.BP_CBullet_C'");
    }
    
    void ACWeapon::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Owner = Cast<ACPlayer>(GetOwner());
    
    	if (HolsterSocketName.IsValid())
    		CHelpers::AttachTo(this, Owner->GetMesh(), HolsterSocketName);
    
    	BaseData.SetDataByNoneCurve(Owner);
    
    	if(!!AimCurve)
    	{
    		FOnTimelineFloat timeline;
    		timeline.BindUFunction(this, "OnAiming");
    
    		Timeline->AddInterpFloat(AimCurve, timeline); //이벤트 객체를 다룸.
    		Timeline->SetLooping(false);
    		Timeline->SetPlayRate(AimingSpeed);
    	}
    
    	if(!!CrossHairClass)
    	{
    		CrossHair = CreateWidget<UCUserWidget_CrossHair, APlayerController>(Owner->GetController<APlayerController>(), CrossHairClass);
    		CrossHair->AddToViewport();
    		CrossHair->SetVisibility(ESlateVisibility::Hidden);
    		CrossHair->UpdateSpreadRange(CurrSpreadRadius, MaxSpreadAlignment);
    	}
    
    	CurrMagazineCount = MaxMagazineCount; //현재 탄 수 = 최대 탄 수로 설정하고 시작.
    }
    
    void ACWeapon::Tick(float DeltaTime)
    {
    	Super::Tick(DeltaTime);
    
    	if(LastAddSpreadTime >= 0.0f) //쏘고 있었다면
    	{
    		if(GetWorld()->GetTimeSeconds() - LastAddSpreadTime >= AutoFireInterval + 0.25f)
    		{
    			CurrSpreadRadius = 0.0f;  //CrossHair 현재값 
    			LastAddSpreadTime = 0.0f; //마지막으로 추가한 시간
    
    			if (!!CrossHair)
    				CrossHair->UpdateSpreadRange(CurrSpreadRadius, MaxSpreadAlignment);
    		} //if
    	}
    
    }
    
    bool ACWeapon::CanEquip()
    {
    	bool b = false;
    	//3개 다 false일 때 b = false
    	//3개 중 하나라도 true면 b = true
    	b |= bEquipping;
    	b |= bReload;
    	b |= bFiring;
    
    	return !b; //b = false일 때 True를 리턴
    }
    
    void ACWeapon::Equip()
    {
    	bEquipping = true;
    
    	if (!!EquipMontage)
    		Owner->PlayAnimMontage(EquipMontage, EquipMontage_PlayRate);
    }
    
    void ACWeapon::Begin_Equip()
    {
    	if (RightHandSocketName.IsValid())
    		CHelpers::AttachTo(this, Owner->GetMesh(), RightHandSocketName);
    }
    
    void ACWeapon::End_Equip()
    {
    	bEquipping = false;
    
    	if (!!CrossHair)
    		CrossHair->SetVisibility(ESlateVisibility::Visible);
    }
    
    bool ACWeapon::CanUnequip()
    {
    	bool b = false;
    	b |= bEquipping;
    	b |= bReload;
    	b |= bFiring;
    
    	return !b;
    }
    
    void ACWeapon::Unequip()
    {
    	End_Aim(); //Unequp 시 Aim 해제
    	
    	if (HolsterSocketName.IsValid())
    		CHelpers::AttachTo(this, Owner->GetMesh(), HolsterSocketName);
    
    	if (!!CrossHair) //무기해제 시 CrossHair가 있으면
    		CrossHair->SetVisibility(ESlateVisibility::Hidden);//CrossHair 꺼줌
    }
    
    bool ACWeapon::CanAim()
    {
    	bool b = false;
    	b |= bEquipping;
    	b |= bReload;
    	b |= bInAim;
    
    	return !b;
    }
    
    void ACWeapon::Begin_Aim()
    {
    	bInAim = true;
    
    	//우리는 생성자 ACWeapon()에서 AimCurve를 가져왔다.
    	if (!!AimCurve) //AimCurve가 있으면 true, 없으면 nullptr이기 때문에 false
    	{
    		Timeline->PlayFromStart();
    		AimData.SetData(Owner);
    
    		return;
    	}
    
    	AimData.SetDataByNoneCurve(Owner);
    }
    
    void ACWeapon::End_Aim()
    {
    	CheckFalse(bInAim);
    	bInAim = false;
    
    	if (!!AimCurve)
    	{
    		Timeline->ReverseFromEnd();
    		BaseData.SetData(Owner);
    
    		return;
    	}
    
    	BaseData.SetDataByNoneCurve(Owner);
    
    }
    
    void ACWeapon::OnAiming(float Output)
    {
    	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(Owner);
    	camera->FieldOfView = FMath::Lerp(AimData.FieldOfView, BaseData.FieldOfView, Output);
    }
    
    bool ACWeapon::CanFire()
    {
    	bool b = false;
    	b |= bEquipping;
    	b |= bReload;
    	b |= bFiring; //발사하고 있는지
    
    	return !b;
    }
    
    void ACWeapon::Begin_Fire()
    {
    	bFiring = true;
    
    	if(bAutoFire)
    	{
    		GetWorld()->GetTimerManager().SetTimer(AutoFireHandle, this, &ACWeapon::OnFiring, AutoFireInterval, true, 0);
    
    		return;
    	}
    
    	OnFiring();
    }
    
    void ACWeapon::End_Fire()
    {
    	CheckFalse(bFiring);
    
    	if(GetWorld()->GetTimerManager().IsTimerActive(AutoFireHandle))//AutoFireHandle이 활성화 있다면
    	{
    		GetWorld()->GetTimerManager().ClearTimer(AutoFireHandle);//AutoFireHandle 해제
    	}
    
    	bFiring = false;
    }
    
    void ACWeapon::OnFiring()
    {
    	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(Owner);
    	FVector direction = camera->GetForwardVector();
    	FTransform transform = camera->GetComponentToWorld();
    
    	FVector start = transform.GetLocation() + direction; //눈알 안보다 앞쪽으로 설정하기 때문에 쏘는 방향쪽으로 조금 더해준다.
    
    	// Cone 모양의 탄착군 형성. 나갈방향(direction). 몇 도로 만들것인가(RecoilAngle). CWeapon_AR4에서 RecoilAngle의 Default값을 설정하였다.
    	direction = UKismetMathLibrary::RandomUnitVectorInConeInDegrees(direction, RecoilAngle);
    	FVector end = transform.GetLocation() + direction * HitDistance;
    
    	//DrawDebugLine(GetWorld(), start, end, FColor::Red, true, 5);
    
    	TArray<AActor*> ignores;
    
    	FHitResult hitResult;
    	UKismetSystemLibrary::LineTraceSingle(GetWorld(), start, end, ETraceTypeQuery::TraceTypeQuery1, false, ignores, EDrawDebugTrace::None, hitResult,true);
    
    	if(hitResult.bBlockingHit) //발사한 LineTrace가 Object에 맞아 Block 충돌이 되었다면
    	{
    		if (!!HitDecal)
    		{
    			FRotator rotator = hitResult.ImpactNormal.Rotation(); //HitDecal이 붙을 방향(rotator) = 충돌된 곳의 Normal 방향
    
    			UDecalComponent* decal = UGameplayStatics::SpawnDecalAtLocation(GetWorld(), HitDecal, FVector(5), hitResult.Location, rotator, 10);
    			decal->SetFadeScreenSize(0);
    		}
    
    		if(!!HitParticle)
    		{
    			//hit된 지점으로부터 최초로 쏜 지점(=나)을 향하도록 설정.
    			FRotator rotator = UKismetMathLibrary::FindLookAtRotation(hitResult.Location, hitResult.TraceStart);
    
    			//Particle이 충돌위치에서 쏜 위치를 향햐도록 나옴.
    			UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), HitParticle, hitResult.Location, rotator);
    		}
    	}
    
    	if (!!FlashParticle) //Muzzle에서 나오는 FlashParticle
    		UGameplayStatics::SpawnEmitterAttached(FlashParticle, Mesh, "Muzzle", FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::KeepRelativeOffset);
    
    
    	if (!!EjectParticle) //Eject에서 나오는 EjectParticle. 탄피.
    		UGameplayStatics::SpawnEmitterAttached(EjectParticle, Mesh, "Eject", FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::KeepRelativeOffset);
    
    	//muzzleLocation 변수를 소켓으로 만든 muzzle 위치로 잡아준다.
    	FVector muzzleLocation = Mesh->GetSocketLocation("Muzzle");
    
    	if (!!FireSound)
    		UGameplayStatics::SpawnSoundAtLocation(GetWorld(), FireSound, muzzleLocation);
    
    	if(!!CameraShakeClass)
    	{
    		//PlayerCameraManager는 PlayerController에 있으므로 PlayerController를 가져온다.
    		APlayerController* controller = Owner->GetController<APlayerController>();
    
    		if (!!controller)
    			controller->PlayerCameraManager->StartCameraShake(CameraShakeClass);
    	}
    
    	Owner->AddControllerPitchInput(-RecoilRate * UKismetMathLibrary::RandomFloatInRange(0.8f, 1.2f));
    
    	if (CurrSpreadRadius <= 1.0f)
    	{
    		//쏘는 동안 계속 CrossHair의 크기를 가산한다.
    		CurrSpreadRadius += SpreadSpeed * GetWorld()->GetDeltaSeconds();
    
    		if (!!CrossHair)
    			CrossHair->UpdateSpreadRange(CurrSpreadRadius, MaxSpreadAlignment);
    	}
    	LastAddSpreadTime = GetWorld()->GetTimeSeconds(); //마지막 시간을 저장. Tick에서 이 값을 사용한다.
    
    	if(!!BulletClass)
    	{
    		FVector location = Mesh->GetSocketLocation("Muzzle_Bullet");
    
    		FActorSpawnParameters params;
    		params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    		//Bullet을 Spawn시킨다.
    		ACBullet* bullet = GetWorld()->SpawnActor<ACBullet>(BulletClass, location, direction.Rotation(), params);
    
    		if (!!bullet)
    			bullet->Shoot(direction);
    	}
    
    	if(CurrMagazineCount >= 1) //쏠 때마다 현재 탄 수를 줄여준다.
    	{
    		CurrMagazineCount--;
    	}
    	else
    	{
    		if (CanReload())
    			Reload();
    	}
    
    }
    
    void ACWeapon::ToggleAutoFire()
    {
    	bAutoFire = !bAutoFire;
    }
    
    bool ACWeapon::CanReload()
    {
    	bool b = false;
    	b |= bEquipping;
    	b |= bReload;
    
    	return !b;
    }
    
    void ACWeapon::Reload()
    {
    	bReload = true;
    
    	End_Aim();
    	End_Fire();
    
    	if (!!ReloadMontage) //재장전 몽타주가 있다면
    		Owner->PlayAnimMontage(ReloadMontage, ReloadMontage_PlayRate); //몽타주 애니메이션 플레이
    }
    
    void ACWeapon::Eject_Magazine()
    {
    	if (MagazineBoneName.IsValid()) //MagazineBoneName이 있다면. CWeapon_AR4에 MagazineBoneName을 설정했다.
    		Mesh->HideBoneByName(MagazineBoneName, EPhysBodyOp::PBO_None); //MagazineBoneName이하를 숨겨준다. 이 때, Physics는 꺼준다.
    
    	CheckNull(MagazineClass);
    
    	FTransform transform = Mesh->GetSocketTransform(MagazineBoneName);
    	ACMagazine* magazine = GetWorld()->SpawnActorDeferred<ACMagazine>(MagazineClass, transform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
    	magazine->SetEject();
    	magazine->SetLifeSpan(5);
    	magazine->FinishSpawning(transform);
    }
    
    void ACWeapon::Spawn_Magazine()
    {
    	CheckNull(MagazineClass);
    
    	FActorSpawnParameters params;
    	params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    	Magazine = GetWorld()->SpawnActor<ACMagazine>(MagazineClass, params);
    	CHelpers::AttachTo(Magazine, Owner->GetMesh(), MagazineSocketName);
    }
    
    void ACWeapon::Load_Magazine()
    {
    	CurrMagazineCount = MaxMagazineCount;
    
    	if (MagazineBoneName.IsValid())
    		Mesh->UnHideBoneByName(MagazineBoneName);//재장전 후에 MagazineBoneName이하를 보이게 켜준다.
    
    	if (!!Magazine)
    		Magazine->Destroy();
    }
    
    void ACWeapon::End_Reload()
    {
    	bReload = false;  
    }

    void ACWeapon::Unequip()에 End_Aim()추가

    • void ACWeapon::Unequip() { End_Aim(); }

     

     




     

    CWeapon_Pistol

     

    CWeapon_Pistol.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "Weapons/CWeapon.h"
    #include "CWeapon_Pistol.generated.h"
    
    UCLASS()
    class U2212_04_API ACWeapon_Pistol : public ACWeapon
    {
    	GENERATED_BODY()
    
    public:
    	ACWeapon_Pistol();
    
    protected:
    	void BeginPlay() override;
    
    public:
    	void Begin_Equip() override;
    
    	void Begin_Aim() override;
    	void End_Aim() override;
    };

    변동사항 없음.

     

     

     

    CWeapon_Pistol.cpp

    더보기
    #include "Weapons/CWeapon_Pistol.h"
    #include "Global.h"
    #include "CMagazine.h"
    #include "CWeaponComponent.h"
    #include "Character/CPlayer.h"
    #include "Components/StaticMeshComponent.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Animation/AnimMontage.h"
    #include "Camera/CameraShake.h"
    #include "Widgets/CUserWidget_CrossHair.h"
    
    ACWeapon_Pistol::ACWeapon_Pistol()
    {
    	USkeletalMesh* mesh;
    	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/MilitaryWeapSilver/Weapons/Pistols_A.Pistols_A'");
    	Mesh->SetSkeletalMesh(mesh);
    
    
    	//Equip
    	{
    		HolsterSocketName = NAME_None;
    		CHelpers::GetAsset<UAnimMontage>(&EquipMontage, "AnimMontage'/Game/Character/Animations/Pistol/Pistol_Equip_Montage.Pistol_Equip_Montage'");
    		EquipMontage_PlayRate = 2;
    		RightHandSocketName = "Pistol_RightHand";
    		LeftHandLocation = FVector(0, 12.5f, 0);
    	}
    
    	//Aim
    	{
    		BaseData.bEnableCameraLag = true;
    		BaseData.TargetArmLength = 200;
    		BaseData.SocketOffset = FVector(0, 50, 15);
    		BaseData.FieldOfView = 90;
    
    		AimData.bEnableCameraLag = false;
    		AimData.TargetArmLength = 30;
    		AimData.SocketOffset = FVector(-55, 0, 10);
    		AimData.FieldOfView = 55;
    	}
    
    	//Fire
    	{
    		RecoilAngle = 1.5f;
    		CHelpers::GetClass<UMatineeCameraShake>(&CameraShakeClass, "Blueprint'/Game/Weapons/BP_CameraShake_Pistol.BP_CameraShake_Pistol_C'");
    		AutoFireInterval = 0.3f;
    		RecoilRate = 0.05f;
    		SpreadSpeed = 6;
    		MaxSpreadAlignment = 4;
    	}
    
    	//UI
    	{
    		CHelpers::GetClass<UCUserWidget_CrossHair>(&CrossHairClass, "WidgetBlueprint'/Game/Widgets/WB_CrossHair.WB_CrossHair_C'");
    	}
    
    	//Magazine
    	{
    		MaxMagazineCount = 5;
    		CHelpers::GetAsset<UAnimMontage>(&ReloadMontage, "AnimMontage'/Game/Character/Animations/Pistol_Reload_Montage.Pistol_Reload_Montage'");
    		ReloadMontage_PlayRate = 1.5f;
    		MagazineBoneName = NAME_None;
    		CHelpers::GetClass<ACMagazine>(&MagazineClass, "Blueprint'/Game/Weapons/BP_CMagazine_Pistol.BP_CMagazine_Pistol_C'");
    		MagazineSocketName = "Pistol_Magazine";
    	}
    
    	//Arms
    	{
    		ArmsMeshTransform.SetLocation(FVector(0, 5.1f, -156.6));
    		ArmsMeshTransform.SetRotation(FQuat(FRotator(0, -4.8f, 0)));
    
    		ArmsLeftHandTransform.SetLocation(FVector(0, 10, 0));
    		ArmsLeftHandTransform.SetRotation(FQuat(FRotator(0, 180, 180)));
    	}
    }
    
    void ACWeapon_Pistol::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Mesh->SetVisibility(false);
    }
    
    void ACWeapon_Pistol::Begin_Equip()
    {
    	Super::Begin_Equip();
    
    	Mesh->SetVisibility(true);
    	Owner->GetArms()->SetRelativeTransform(ArmsMeshTransform);
    }
    
    void ACWeapon_Pistol::Begin_Aim()
    {
    	Super::Begin_Aim();
    
    	if (!!CrossHair)
    		CrossHair->SetVisibility(ESlateVisibility::Hidden);
    
    	Owner->GetMesh()->SetVisibility(false);
    	Owner->GetBackpack()->SetVisibility(false);
    	Owner->GetArms()->SetVisibility(true);
    
    	CHelpers::AttachTo(this, Owner->GetArms(), RightHandSocketName);
    
    	CHelpers::GetComponent<UCWeaponComponent>(Owner)->OnWeaponAim_Arms_Begin.Broadcast(this);
    }
    
    void ACWeapon_Pistol::End_Aim()
    {
    	Super::End_Aim();
    
    	if (!!CrossHair)
    		CrossHair->SetVisibility(ESlateVisibility::Visible);
    
    	Owner->GetMesh()->SetVisibility(true);
    	Owner->GetBackpack()->SetVisibility(true);
    	Owner->GetArms()->SetVisibility(false);
    
    	CHelpers::AttachTo(this, Owner->GetMesh(), RightHandSocketName);
    
    	CHelpers::GetComponent<UCWeaponComponent>(Owner)->OnWeaponAim_Arms_End.Broadcast();
    }

    몽타주, CameraShake 경로 변경.


     

     

    CUserWidget_HUD

     

    CUserWidget_HUD.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Blueprint/UserWidget.h"
    #include "Weapons/CWeaponComponent.h"
    #include "CUserWidget_HUD.generated.h"
    
    UCLASS()
    class U2212_04_API UCUserWidget_HUD : public UUserWidget
    {
    	GENERATED_BODY()
    
    public:
    	UFUNCTION(BlueprintImplementableEvent)
    		void OnAutoFire();
    
    	UFUNCTION(BlueprintImplementableEvent)
    		void OffAutoFire();
    
    public:
    	UFUNCTION(BlueprintImplementableEvent)
    		void UpdateMagazine(uint8 InCurr, uint8 InMax); //현재 탄 수 업데이트
    
    public:
    	UFUNCTION(BlueprintImplementableEvent)
    		void UpdateWeaponType(EWeaponType InType);
    };

    함수 추가 

    • void UpdateWeaponType(EWeaponType InType);

     

     

     

    CUserWidget_HUD.cpp

    더보기
    #include "Widgets/CUserWidget_HUD.h"

    변동사항 없음 

     


     

     

     

     

     

    실행화면