목차

     

     


     

     

     
    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
    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

     

     

     

     

    필요한 에셋 추가하기, 소켓 추가하기

     

     


     

     

     

    SK_Mannequin_Arms 가져오기

     

    Player Arm으로 사용할 메쉬를 가져온다.

     

     


     

    SM_T4_Sight 가져오기

     

    머터리얼 슬롯 - 엘리먼트0 - M_KA47_Pattern 할당

     

     


     

    SK_KA47_Skeleton  - 소켓 추가

     

    DotSight 소켓 추가

     

     


     

     

     

    SK_Mannequin_Arms_Skeleton - 소켓 추가

     

    Rifle_AK47_RightHand 추가

     

     


     

     

     

     

    Arm

     

     


     

     

    CAnimInstance_Arms 생성

     

    새 C++ 클래스 - AnimInstance - 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;
    public:
    void NativeBeginPlay() override;
    void NativeUpdateAnimation(float DeltaSeconds) override;
    private:
    UFUNCTION()
    void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);
    private:
    class ACPlayer* OwnerCharacter;
    class UCWeaponComponent* Weapon;
    };

     

     

    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);
    }
    void UCAnimInstance_Arms::OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType)
    {
    WeaponType = InNewType;
    }

     

     


     

     

     

    ABP_Player_Arms 생성

     

    애니메이션 - 애니메이션 블루프린트 - 부모클래스: CAnimInstance_Arms, 스켈레톤: Skel_Mannequin_Arms - 

    ABP_Player_Arms 생성

     

     

    AnimGraph

     

     


     

     

    CPlayer

     

    CPlayer.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "GameFramework/Character.h"
    #include "CPlayer.generated.h"
    UCLASS()
    class U2212_04_API ACPlayer : public ACharacter
    {
    GENERATED_BODY()
    private:
    UPROPERTY(EditDefaultsOnly, Category = "Camera")
    FVector2D PitchRange = FVector2D(-40, +40);
    private:
    UPROPERTY(VisibleAnywhere)
    class USpringArmComponent* SpringArm;
    UPROPERTY(VisibleAnywhere)
    class UCameraComponent* Camera;
    UPROPERTY(VisibleAnywhere)
    class UStaticMeshComponent* Backpack;
    UPROPERTY(VisibleAnywhere)
    class USkeletalMeshComponent* Arms;
    private:
    UPROPERTY(VisibleAnywhere)
    class UCWeaponComponent* Weapon;
    public:
    FORCEINLINE UStaticMeshComponent* GetBackpack() { return Backpack; }
    FORCEINLINE USkeletalMeshComponent* GetArms() { return Arms; }
    public:
    ACPlayer();
    protected:
    virtual void BeginPlay() override;
    public:
    virtual void Tick(float DeltaTime) override;
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    private:
    void OnMoveForward(float InAxisValue);
    void OnMoveRight(float InAxisValue);
    void OnHorizontalLook(float InAxisValue);
    void OnVerticalLook(float InAxisValue);
    private:
    void OnRun();
    void OffRun();
    };

    변수 추가

    • class USkeletalMeshComponent* Arms;

     

    인라인 함수 추가

    • FORCEINLINE UStaticMeshComponent* GetBackpack() { return Backpack; }
    • FORCEINLINE USkeletalMeshComponent* GetArms() { return Arms; }

     

     

     

    CPlayer.cpp

    더보기
    #include "CPlayer.h"
    #include "Global.h"
    #include "CAnimInstance.h"
    #include "CAnimInstance_Arms.h"
    #include "Weapons/CWeaponComponent.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "GameFramework/CharacterMovementComponent.h"
    #include "Camera/CameraComponent.h"
    #include "Components/CapsuleComponent.h"
    #include "Components/InputComponent.h"
    #include "Components/StaticMeshComponent.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Materials/MaterialInstanceDynamic.h"
    ACPlayer::ACPlayer()
    {
    PrimaryActorTick.bCanEverTick = true;
    CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetCapsuleComponent());
    CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
    CHelpers::CreateComponent<UStaticMeshComponent>(this, &Backpack, "Backpack", GetMesh(), "Backpack");
    CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Arms, "Arms", Camera);
    CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");
    USkeletalMesh* mesh;
    CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
    GetMesh()->SetSkeletalMesh(mesh);
    GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
    GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
    TSubclassOf<UCAnimInstance> animInstance;
    CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/Player/ABP_Player.ABP_Player_C'");
    GetMesh()->SetAnimClass(animInstance);
    GetCharacterMovement()->MaxWalkSpeed = 400;
    SpringArm->SetRelativeLocation(FVector(0, 0, 60));
    SpringArm->TargetArmLength = 200;
    SpringArm->bUsePawnControlRotation = true;
    SpringArm->bEnableCameraLag = true;
    UStaticMesh* staticMesh;
    CHelpers::GetAsset<UStaticMesh>(&staticMesh, "StaticMesh'/Game/FPS_Weapon_Bundle/Backpack/Backpack.Backpack'");
    Backpack->SetStaticMesh(staticMesh);
    Backpack->SetCollisionProfileName("NoCollision");
    CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character_Arms/Mesh/SK_Mannequin_Arms.SK_Mannequin_Arms'");
    Arms->SetSkeletalMesh(mesh);
    Arms->SetRelativeLocation(FVector(-14.25f, -5.85f, -156.935f));
    Arms->SetRelativeRotation(FRotator(-0.5f, -11.85f, -1.2f));
    Arms->SetVisibility(false); //안 보이도록 기본값 세팅
    TSubclassOf<UCAnimInstance_Arms> armsAnimInstance;
    CHelpers::GetClass<UCAnimInstance_Arms>(&armsAnimInstance, "AnimBlueprint'/Game/Player/ABP_Player_Arms.ABP_Player_Arms_C'");
    Arms->SetAnimClass(armsAnimInstance);
    }
    void ACPlayer::BeginPlay()
    {
    Super::BeginPlay(); //Super가 BP의 BeginPlay를 콜한다.
    GetController<APlayerController>()->PlayerCameraManager->ViewPitchMin = PitchRange.X; //각 제한 걸기
    GetController<APlayerController>()->PlayerCameraManager->ViewPitchMax = PitchRange.Y; //각 제한 걸기
    }
    void ACPlayer::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime);
    }
    void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
    PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
    PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
    PlayerInputComponent->BindAction("Run", EInputEvent::IE_Pressed, this, &ACPlayer::OnRun);
    PlayerInputComponent->BindAction("Run", EInputEvent::IE_Released, this, &ACPlayer::OffRun);
    PlayerInputComponent->BindAction("AR4", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAR4Mode);//Object를 Weapon으로 받는다.
    PlayerInputComponent->BindAction("AK47", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAK47Mode);//Object를 Weapon으로 받는다.
    PlayerInputComponent->BindAction("Aim", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Aim);
    PlayerInputComponent->BindAction("Aim", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Aim);
    PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Fire);
    PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Fire);
    PlayerInputComponent->BindAction("AutoFire", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::ToggleAutoFire);
    PlayerInputComponent->BindAction("Reload", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Reload);
    }
    void ACPlayer::OnMoveForward(float InAxisValue)
    {
    FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
    FVector direction = FQuat(rotator).GetForwardVector();
    AddMovementInput(direction, InAxisValue);
    }
    void ACPlayer::OnMoveRight(float InAxisValue)
    {
    FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
    FVector direction = FQuat(rotator).GetRightVector();
    AddMovementInput(direction, InAxisValue);
    }
    void ACPlayer::OnHorizontalLook(float InAxisValue)
    {
    AddControllerYawInput(InAxisValue);
    }
    void ACPlayer::OnVerticalLook(float InAxisValue)
    {
    AddControllerPitchInput(InAxisValue);
    }
    void ACPlayer::OnRun()
    {
    GetCharacterMovement()->MaxWalkSpeed = 600;
    }
    void ACPlayer::OffRun()
    {
    GetCharacterMovement()->MaxWalkSpeed = 400;
    }

    Arms를 카메라에 붙여 생성한다.

    • CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Arms, "Arms", Camera);

     

    Arms 기본값 세팅

    • CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character_Arms의 레퍼런스 경로'");
    • Arms->SetSkeletalMesh(mesh);
    • Arms->SetRelativeLocation(FVector(-14.25f, -5.85f, -156.935f));
    • Arms->SetRelativeRotation(FRotator(-0.5f, -11.85f, -1.2f));
    • Arms->SetVisibility(false);

     

    Arms의 AnimInstance 설정

    • TSubclassOf<UCAnimInstance_Arms> armsAnimInstance;
    • CHelpers::GetClass<UCAnimInstance_Arms>(&armsAnimInstance, "ABP_Player 레퍼런스 경로'");
    • Arms->SetAnimClass(armsAnimInstance);

     

     

    위와 같이 CPlayer에 Arms를 생성하고 정보를 기입하고 컴파일을 실행한다.

     

     

    BP_CPlayer

    Arms - 렌더링 - 테스트를 하기 위해 Visible을 체크하여 Arms가 화면에 보이도록 만들어준다. 테스트가 끝난 후 체크를 해제해준다.

    Animation - Anim Class - ABP_Player_Arms_C 할당

    메시 - Skeletal Mesh - SK_Mannequin_Arms 할당

    머터리얼 - 엘리먼트0 - < M_UE4Man_Body_Inst 할당

     

     


     

     

     

     

    Weapon

     

     


     

     

     

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

    자식에서 재정의할 수 있도록 가상함수로 변경한다.

    • void Begin_Aim();   →변경    virtual void Begin_Aim(); 
    • void End_Aim();     →변경    virtual void End_Aim();
    • CWeapon_AK47에서 Begin_Aim()와 End_Aim()를 재정의한다.

     

    자식에서 접근이 가능하도록 private을 protected로 변경

    • private  →변경  protected: class UCUserWidget_CrossHair* CrossHair;

     

     

    CWeapon.cpp

    더보기
    #include "CPlayer.h"
    #include "Global.h"
    #include "CAnimInstance.h"
    #include "CAnimInstance_Arms.h"
    #include "Weapons/CWeaponComponent.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "GameFramework/CharacterMovementComponent.h"
    #include "Camera/CameraComponent.h"
    #include "Components/CapsuleComponent.h"
    #include "Components/InputComponent.h"
    #include "Components/StaticMeshComponent.h"
    #include "Components/SkeletalMeshComponent.h"
    #include "Materials/MaterialInstanceDynamic.h"
    ACPlayer::ACPlayer()
    {
    PrimaryActorTick.bCanEverTick = true;
    CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetCapsuleComponent());
    CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
    CHelpers::CreateComponent<UStaticMeshComponent>(this, &Backpack, "Backpack", GetMesh(), "Backpack");
    CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Arms, "Arms", Camera);
    CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");
    USkeletalMesh* mesh;
    CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
    GetMesh()->SetSkeletalMesh(mesh);
    GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
    GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
    TSubclassOf<UCAnimInstance> animInstance;
    CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/Player/ABP_Player.ABP_Player_C'");
    GetMesh()->SetAnimClass(animInstance);
    GetCharacterMovement()->MaxWalkSpeed = 400;
    SpringArm->SetRelativeLocation(FVector(0, 0, 60));
    SpringArm->TargetArmLength = 200;
    SpringArm->bUsePawnControlRotation = true;
    SpringArm->bEnableCameraLag = true;
    UStaticMesh* staticMesh;
    CHelpers::GetAsset<UStaticMesh>(&staticMesh, "StaticMesh'/Game/FPS_Weapon_Bundle/Backpack/Backpack.Backpack'");
    Backpack->SetStaticMesh(staticMesh);
    Backpack->SetCollisionProfileName("NoCollision");
    CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character_Arms/Mesh/SK_Mannequin_Arms.SK_Mannequin_Arms'");
    Arms->SetSkeletalMesh(mesh);
    Arms->SetRelativeLocation(FVector(-14.25f, -5.85f, -156.935f));
    Arms->SetRelativeRotation(FRotator(-0.5f, -11.85f, -1.2f));
    Arms->SetVisibility(false); //안 보이도록 기본값 세팅
    TSubclassOf<UCAnimInstance_Arms> armsAnimInstance;
    CHelpers::GetClass<UCAnimInstance_Arms>(&armsAnimInstance, "AnimBlueprint'/Game/Player/ABP_Player_Arms.ABP_Player_Arms_C'");
    Arms->SetAnimClass(armsAnimInstance);
    }
    void ACPlayer::BeginPlay()
    {
    Super::BeginPlay(); //Super가 BP의 BeginPlay를 콜한다.
    GetController<APlayerController>()->PlayerCameraManager->ViewPitchMin = PitchRange.X; //각 제한 걸기
    GetController<APlayerController>()->PlayerCameraManager->ViewPitchMax = PitchRange.Y; //각 제한 걸기
    }
    void ACPlayer::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime);
    }
    void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
    PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
    PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
    PlayerInputComponent->BindAction("Run", EInputEvent::IE_Pressed, this, &ACPlayer::OnRun);
    PlayerInputComponent->BindAction("Run", EInputEvent::IE_Released, this, &ACPlayer::OffRun);
    PlayerInputComponent->BindAction("AR4", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAR4Mode);//Object를 Weapon으로 받는다.
    PlayerInputComponent->BindAction("AK47", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAK47Mode);//Object를 Weapon으로 받는다.
    PlayerInputComponent->BindAction("Aim", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Aim);
    PlayerInputComponent->BindAction("Aim", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Aim);
    PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Fire);
    PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Fire);
    PlayerInputComponent->BindAction("AutoFire", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::ToggleAutoFire);
    PlayerInputComponent->BindAction("Reload", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Reload);
    }
    void ACPlayer::OnMoveForward(float InAxisValue)
    {
    FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
    FVector direction = FQuat(rotator).GetForwardVector();
    AddMovementInput(direction, InAxisValue);
    }
    void ACPlayer::OnMoveRight(float InAxisValue)
    {
    FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
    FVector direction = FQuat(rotator).GetRightVector();
    AddMovementInput(direction, InAxisValue);
    }
    void ACPlayer::OnHorizontalLook(float InAxisValue)
    {
    AddControllerYawInput(InAxisValue);
    }
    void ACPlayer::OnVerticalLook(float InAxisValue)
    {
    AddControllerPitchInput(InAxisValue);
    }
    void ACPlayer::OnRun()
    {
    GetCharacterMovement()->MaxWalkSpeed = 600;
    }
    void ACPlayer::OffRun()
    {
    GetCharacterMovement()->MaxWalkSpeed = 400;
    }

    변경사항 없음.

     

     


     

     

    CWeapon_AK47

     

    CWeapon_AK47.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Weapons/CWeapon.h"
    #include "CWeapon_AK47.generated.h"
    UCLASS()
    class U2212_04_API ACWeapon_AK47 : public ACWeapon
    {
    GENERATED_BODY()
    private:
    UPROPERTY(EditDefaultsOnly, Category = "Equip")
    FName LeftHandSocketName;
    private:
    UPROPERTY(VisibleAnywhere)
    class UStaticMeshComponent* Sight;
    public:
    ACWeapon_AK47();
    public:
    void Begin_Equip() override;
    void End_Equip() override;
    void Begin_Aim() override;
    void End_Aim() override;
    };

    변수 생성

    • class UStaticMeshComponent* Sight;

     

    함수 재정의

    • void Begin_Aim() override;
    • void End_Aim() override;

     

     

     

    CWeapon_AK47.cpp

    더보기
    #include "Weapons/CWeapon_AK47.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_AK47::ACWeapon_AK47()
    {
    USkeletalMesh* mesh;
    CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/FPS_Weapon_Bundle/Weapons/Meshes/AR4/SK_AR4.SK_AR4'");
    Mesh->SetSkeletalMesh(mesh);
    CHelpers::CreateComponent<UStaticMeshComponent>(this, &Sight, "Sight", Mesh, "DotSight");
    UStaticMesh* staticMesh;
    CHelpers::GetAsset<UStaticMesh>(&staticMesh, "StaticMesh'/Game/FPS_Weapon_Bundle/Weapons/Meshes/Accessories/SM_T4_Sight.SM_T4_Sight'");
    Sight->SetStaticMesh(staticMesh);
    Sight->SetRelativeScale3D(FVector(1, 0.95f, 1));
    Sight->SetCollisionProfileName("NoCollision");//AK47와 T4_Sight가 충돌되지 않도록 T4_Sight를 No Collision으로 만들어준다.
    //AK
    {
    LeftHandSocketName = "Rifle_AK47_LeftHand";
    }
    //Equip
    {
    HolsterSocketName = "Rifle_AK47_Holster";
    CHelpers::GetAsset<UAnimMontage>(&EquipMontage, "AnimMontage'/Game/Character/Animations/Rifle_Equip_AK47_Montage.Rifle_Equip_AK47_Montage'");
    EquipMontage_PlayRate = 2.0f;
    RightHandSocketName = "Rifle_AK47_RightHand";
    LeftHandLocation = FVector(-35, 15.5f, 7);
    }
    //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 = 0.75f;
    CHelpers::GetClass<UMatineeCameraShake>(&CameraShakeClass, "Blueprint'/Game/Weapons/BP_CameraShake_AR4.BP_CameraShake_AR4_C'");
    AutoFireInterval = 0.1f; //연사 시간간격(=연사 속도)
    RecoilRate = 0.05f;
    SpreadSpeed = 2.0f;
    MaxSpreadAlignment = 2.0f;
    }
    //UI
    {
    CHelpers::GetClass<UCUserWidget_CrossHair>(&CrossHairClass, "WidgetBlueprint'/Game/Widgets/WB_CrossHair.WB_CrossHair_C'");
    }
    //Magazine
    {
    MaxMagazineCount = 30; //최대 탄 수 = 30으로 설정
    CHelpers::GetAsset<UAnimMontage>(&ReloadMontage, "AnimMontage'/Game/Character/Animations/Rifle_Reload_Montage.Rifle_Reload_Montage'");
    ReloadMontage_PlayRate = 1.5f;
    MagazineBoneName = "b_gun_mag";
    CHelpers::GetClass<ACMagazine>(&MagazineClass, "Blueprint'/Game/Weapons/BP_CMagazine_AK47.BP_CMagazine_AK47_C'");
    MagazineSocketName = "Rifle_Magazine";
    }
    }
    void ACWeapon_AK47::Begin_Equip()
    {
    if (LeftHandSocketName.IsValid())
    CHelpers::AttachTo(this, Owner->GetMesh(), LeftHandSocketName); //AK47(=this)이 Owner->GetMesh()의 LeftHandSockName으로 붙는다.
    }
    void ACWeapon_AK47::End_Equip()
    {
    Super::Begin_Equip(); //부모 Begin_Equip()이 콜 되어 오른손으로 간다.
    Super::End_Equip(); //부모 End_Equip()이 콜 되어 마무리된다.
    }
    void ACWeapon_AK47::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_AK47::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();
    }

    Weapon에서 정의한 Begin_Aim()와 End_Aim() 재정의

    • void ACWeapon_AK47::Begin_Aim()
    • void ACWeapon_AK47::End_Aim()

     

     


     

     

     

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

    Delegate 추가

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

     

    변수 추가

    • FWeaponTypeChanged OnWeaponTypeChanged;
    • FWeaponAim_Arms_Begin OnWeaponAim_Arms_Begin;
    • FWeaponAim_Arms_End OnWeaponAim_Arms_End;

     

     

    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에 탄 수 업데이트
    }
    }
    }
    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::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();
    }
    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);
    }

     

     

     


     

     

    실행화면

     

     

     

     

     


     

     

     

     

     

     

     

    ABP: Animation Blueprint
    AN: Blueprint Class AnimNotify
    ANS: Blueprint Class - AnimNotifyState

    AO: Aim Offset

    BP: Blueprint Class
    BS: Blend Space

    BF: Blueprint Function Library
    CS: Matinee Camera Shake
    E: Enum 열거형
    DT: Data Table
    F: 구조체

    I: Blueprint Interface

    WB: Widget Bluepprint

     

    UE2212_04

      Level - LEVEL
    Character Animation Rifle
    Unarmed
    Rifle_Equip_Montage
    Rifle_Reload_Montage
    BlendSpaces AO_Rifle_Aim
    AO_Rifle_Idle
    BS_Rifle
    BS_Rifle_Aim

    BS_Unarme
    Materials

    MaterialLayers
    M_UE4Man_Body
    M_UE4Man_ChestLogo
    Mesh SK_Mannequin
    SK_MAnnequin_PhysicsAsset
    Skel_Mannequin
       
    Textures UE4_LOGO_CARD
    UE4_Mannequin__normals
    UE4_Mannequin_MAT_MASKA
    UE4Man_Logo_N
    Weapons Elven Bow
    Greate Hammer
    Sword
    Effects
    Materials    
    Meshes    
    Texture    
    P_Eject_bullet
    P_Impact_Default
    P_Muzzleflash
    Environment
    Materials Textures  
    Meshes    
    Obstacle    
    FPS_Weapon_Bundle

    Backpack backpact
    M_Backpack
     
    Weapons
    Material  
    Meshes  
    Textures  
    SM_AR4_Mag
    SM_AR4_Mag_Empty
       
    Materials Texture    
    M_Cursor
    M_Cursor_Inst
    M_Decal
    M_Mesh
    M_White_Inst
    M_Red_Inst
    M_Green_Inst
    M_Blue_Inst
    M_UE4Man_Body_Inst
    M_UE4Man_ChesLogo_Inst
       
    Meshes Cone_2
    Cube_2
    Cylinder_2
    Sphere_2
       
    Player ABP_Player
    BP_Player
       
    Sounds S_RifleShoot    
    Textures Crosshair    
    Weapons
    BP_CameraShake_AR4
    BP_CBullet
    BP_CMagazine_AK47
    BP_CMagazine_AR4
    BP_CWeapon_AK47
    BP_CWeapon_AR4
       
         
    Widgets NotoSansKR-Black
    NotoSansKR-Black_Font
    WB_CrossHair
    WB_HUB
       

     

     

     

    '⭐ Unreal Engine > UE FPS TPS' 카테고리의 다른 글

    [UE] Pistol  (0) 2023.04.01
    [UE] FPS, Pistol  (0) 2023.03.31
    [UE] Mixamo, 무기 추가(AK47)  (0) 2023.03.29
    [UE] 재장전(탄창 생성 및 제거)  (0) 2023.03.28
    [UE] 총알 & 재장전 구현. Bullet, Magazine, Reload  (0) 2023.03.27