[UE] TPS Weapon - Weapon Framework 짜기

목차
Weapon - Framework 짜기
Characters | |
CAnimInstance.h .cpp CPlayer.h .cpp |
|
Utilities | |
CHelpers.h CLog.h .cpp |
|
Weapons | |
CWeapon.h .cpp 생성 CWeapon_AR4.h .cpp 생성 CWeaponComponent.h .cpp 생성 |
|
CGameMode.h .cpp Global.h |


https://designerd.tistory.com/entry/Unreal-20230111-10%EA%B0%95-%EB%AC%B4%EA%B8%B0%EA%B4%80%EB%A6%AC
[Unreal] (2023.01.11) 10강 - 무기관리
목차 무기 관리 BP: Blueprint Class BS: Blend Space ABP: Animation Blueprint AN: Blueprint Class AnimNotify ANS: Blueprint Class - AnimNotifyState E: Enum 열거형 DT: Data Table UE2212_01 01_Blueprints Level - LEVEL BP_01_Variable 02_Player 02_Playe
designerd.tistory.com
Service Locator
Weapon Component (이 패턴을 Service Locator. 응집도를 높이는 패턴)
Weapon
SOLID
- 단일책임 원칙
- 개방페쇄 원칙:
- 추가에는 열려있고 수정에는 닫혀있어야 한다는 원칙. 기능을 추가하는 것만 열려있다.
- 리스콥프 치환 원칙:
- 부모에 있는 것을 자식으로 내렸을때 정상적으로 작동해야한다.
- 부모는 공통적인 것만 들어가야 한다.
- df
- 의존역전 원칙:
응집도는 높이고 결합도는 낮추는 방향으로 프로그래밍해야 한다.
조건 체크 방식 - bool CanEquip()
class A { virtual bool CanEquip() { } //조건 체크를 여기서 한다. virtual void Equip() { if(CanEquip()) //조건체크 { } } } class A : B { bool CanEquip() { } //부모꺼 체크 void Equip() //자기꺼 체크 { Super::Equip(); if(CanEquip()) { } } }
부모 클래스에서 virtual bool CanEquip()으로 조건 체크를 한다.
- 자식 클래스에서 부모의 CanEquip()을 체크한다.
- 자식 클래스에서 필요한 내용을 재정의한다.
- 자식은 '부모 조건 체크 + 자기꺼 조건 체크'
CWeapon
새 C++ 클래스 생성 - Actor - CWeapon 생성
CWeapon.h
#pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "CWeapon.generated.h" //abstract를 쓰면 Unreal Editor에서 추상 베이스 클래스로 취급하겠다는 의미. 추상이되면 배치시킬 수 없다. UCLASS(abstract) class U2212_04_API ACWeapon : public AActor { GENERATED_BODY() protected: UPROPERTY(EditDefaultsOnly, Category = "Equip") FName HolsterSocketName; private: UPROPERTY(VisibleAnywhere) class USceneComponent* Root; protected: UPROPERTY(VisibleAnywhere) class USkeletalMeshComponent* Mesh; public: ACWeapon(); protected: virtual void BeginPlay() override; public: virtual void Tick(float DeltaTime) override; public: bool CanEquip(); void Equip(); void Begin_Equip(); void End_Equip(); bool CanUnequip(); void Unequip(); private: class ACPlayer* Owner; };
모든 Weapon의 부모 클래스가 될 CWeapon에
- bool CanEquip();
- void Equip();
- void Begin_Equip();
- void End_Equip();
- bool CanUnequip();
- void Unequip();
을 정의한다.
CWeapon.cpp
#include "Weapons/CWeapon.h" #include "Global.h" #include "Character/CPlayer.h" #include "Components/SkeletalMeshComponent.h" ACWeapon::ACWeapon() { PrimaryActorTick.bCanEverTick = true; CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root"); CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Mesh, "Mesh", Root); } void ACWeapon::BeginPlay() { Super::BeginPlay(); Owner = Cast<ACPlayer>(GetOwner()); if (HolsterSocketName.IsValid()) AttachToComponent(Owner->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HolsterSocketName); } void ACWeapon::Tick(float DeltaTime) { Super::Tick(DeltaTime); } bool ACWeapon::CanEquip() { return true; } void ACWeapon::Equip() { } void ACWeapon::Begin_Equip() { } void ACWeapon::End_Equip() { } bool ACWeapon::CanUnequip() { return true; } void ACWeapon::Unequip() { }
추후에 코드 추가 예정.

- 위의 BP 노드를 Unreal C++ 코드로 작성하면 다음과 같다.
- AttachToComponent(Owner->GetMesh(), FAttachmentTransformRules EAttachmentRule::KeepRelative, true), HolsterSocketName);
CWeapon_AR4
Weapon의 자식 클래스 생성 - Actor - CWeapon_AR4 생성
CWeapon_AR4.h
#pragma once #include "CoreMinimal.h" #include "Weapons/CWeapon.h" #include "CWeapon_AR4.generated.h" UCLASS() class U2212_04_API ACWeapon_AR4 : public ACWeapon { GENERATED_BODY() public: ACWeapon_AR4(); };
CWeapon_AR4.cpp
#include "Weapons/CWeapon_AR4.h" #include "Global.h" #include "Components/SkeletalMeshComponent.h" ACWeapon_AR4::ACWeapon_AR4() { USkeletalMesh* mesh; CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/FPS_Weapon_Bundle/Weapons/Meshes/AR4/SK_AR4.SK_AR4'"); Mesh->SetSkeletalMesh(mesh); //Equip { HolsterSocketName = "Rifle_AR4_Holster"; } }
CWeaponComponent
새 C++ 클래스 생성 - ActorComponent - 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); 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는 그것에 대한 배열이다. 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: FWeaponTypeChanged OnWeaponTypeChanged; private: EWeaponType Type = EWeaponType::Max; //Max는 아무것도 선택안한 상황. private: class ACPlayer* Owner; //플레이어 변수 TArray<class ACWeapon*> Weapons; //무기 배열 변수 };
CWeaponComponent.cpp
#include "Weapons/CWeaponComponent.h" #include "Global.h" #include "CWeapon.h" #include "Character/CPlayer.h" // Sets default values for this component's properties UCWeaponComponent::UCWeaponComponent() { // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features // off to improve performance if you don't need them. PrimaryComponentTick.bCanEverTick = true; } 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); } } } // Called every frame void UCWeaponComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); } 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는 아무것도 선택안한 상태) } void UCWeaponComponent::SetAR4Mode() { SetMode(EWeaponType::AR4); } 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); //무기 변경 알려줌 } void UCWeaponComponent::ChangeType(EWeaponType InType) { EWeaponType type = Type; Type = InType; //기존의 무기 타입에서 바뀐 무기 타입을 넣어줌. if (OnWeaponTypeChanged.IsBound()) OnWeaponTypeChanged.Broadcast(type, InType);//기존 무기 타입, 바뀐 무기 타입 }
Player 부분 추가
Skel_Mannequin

- 소켓 추가
CHelper
CHelper.h
#pragma once #include "CoreMinimal.h" #define CheckTrue(x) { if(x == true) return; } #define CheckTrueResult(x, y) { if(x == true) return y; } #define CheckFalse(x) { if(x == false) return;} #define CheckFalseResult(x, y) { if(x == false) return y;} #define CheckNull(x) { if(x == nullptr) return;} #define CheckNullResult(x, y) { if(x == nullptr) return y;} #define CreateTextRender()\ {\ CHelpers::CreateComponent<UTextRenderComponent>(this, &Text, "Tex", Root);\ Text->SetRelativeLocation(FVector(0, 0, 100));\ Text->SetRelativeRotation(FRotator(0, 180, 0));\ Text->SetRelativeScale3D(FVector(2));\ Text->TextRenderColor = FColor::Red;\ Text->HorizontalAlignment = EHorizTextAligment::EHTA_Center;\ Text->Text = FText::FromString(GetName().Replace(L"Default__", L""));\ } class U2212_04_API CHelpers { public: template<typename T> static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr) { *OutComponent = InActor->CreateDefaultSubobject<T>(InName); if (!!InParent) { (*OutComponent)->SetupAttachment(InParent); return; } InActor->SetRootComponent(*OutComponent); } //CreateActorComponent 추가 template<typename T> static void CreateActorComponent(AActor* InActor, T** OutComponent, FName InName) { *OutComponent = InActor->CreateDefaultSubobject<T>(InName); } template<typename T> static void GetAsset(T** OutObject, FString InPath) { ConstructorHelpers::FObjectFinder<T> asset(*InPath); *OutObject = asset.Object; } template<typename T> static void GetAssetDynamic(T** OutObject, FString InPath) { *OutObject = Cast<T>(StaticLoadObject(T::StaticClass(), nullptr, *InPath)); } template<typename T> static void GetClass(TSubclassOf<T>* OutClass, FString InPath) { ConstructorHelpers::FClassFinder<T> asset(*InPath); *OutClass = asset.Class; } template<typename T> static T* FindActor(UWorld* InWorld) { for (AActor* actor : InWorld->GetCurrentLevel()->Actors) { if (!!actor && actor->IsA<T>()) return Cast<T>(actor); } return nullptr; } template<typename T> static void FindActors(UWorld* InWorld, TArray<T*>& OutActors) { for (AActor* actor : InWorld->GetCurrentLevel()->Actors) { if (!!actor && actor->IsA<T>()) OutActors.Add(Cast<T>(actor)); } } template<typename T> static T* GetComponent(AActor* InActor) { return Cast<T>(InActor->GetComponentByClass(T::StaticClass())); } template<typename T> static T* GetComponent(AActor* InActor, const FString& InName) { TArray<T*> components; InActor->GetComponents<T>(components); for (T* component : components) { if (component->GetName() == InName) return component; } return nullptr; } };
추가된 부분
template<typename T> static void CreateActorComponent(AActor* InActor, T** OutComponent, FName InName) { *OutComponent = InActor->CreateDefaultSubobject<T>(InName); } template<typename T> static T* GetComponent(AActor* InActor, const FString& InName) { TArray<T*> components; InActor->GetComponents<T>(components); for (T* component : components) { if (component->GetName() == InName) return component; } return nullptr; }
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(VisibleAnywhere) class USpringArmComponent* SpringArm; UPROPERTY(VisibleAnywhere) class UCameraComponent* Camera; private: UPROPERTY(VisibleAnywhere) class UCWeaponComponent* Weapon; 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(); };
CPlayer에 WeaponComponent를 넣어준다.
CPlayer.cpp
#include "CPlayer.h" #include "Global.h" #include "CAnimInstance.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 "Materials/MaterialInstanceDynamic.h" ACPlayer::ACPlayer() { PrimaryActorTick.bCanEverTick = true; CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetCapsuleComponent()); CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm); 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); bUseControllerRotationYaw = false; GetCharacterMovement()->bOrientRotationToMovement = true; GetCharacterMovement()->MaxWalkSpeed = 400; SpringArm->SetRelativeLocation(FVector(0, 0, 60)); SpringArm->TargetArmLength = 200; SpringArm->bUsePawnControlRotation = true; SpringArm->bEnableCameraLag = true; } void ACPlayer::BeginPlay() { Super::BeginPlay(); //Super가 BP의 BeginPlay를 콜한다. } 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으로 받는다. } 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; }
BP_CPlayer

- Weapon - 세팅 - Weapon Classes - 추가
- 0번에 BP_CWeapon_AR4 할당
ABP_Player - 동작 할당
AnimGraph

- WeaponType에 따른 블랜드 포즈 할당.
실행화면

'⭐ Unreal Engine > UE FPS TPS' 카테고리의 다른 글
[UE] Hand IK, AnimInstance, Fire (0) | 2023.03.21 |
---|---|
[UE] TPS Weapon, AnimNotify, Aim (0) | 2023.03.17 |
[UE] Line Trace, Multi Trace, TPS 기본 세팅 (0) | 2023.03.15 |
[UE] Collision(Override), BP와 C++ 실행순서 (0) | 2023.03.14 |
[UE] Collsion(trigger, MultiTrigger, Explosion) (0) | 2023.03.13 |
댓글
이 글 공유하기
다른 글
-
[UE] Hand IK, AnimInstance, Fire
[UE] Hand IK, AnimInstance, Fire
2023.03.21Unreal Engine 4의 Hand IK(Inverse Kinematics)는 캐릭터의 가상 손이 사실적인 방식으로 환경 및 개체와 상호 작용할 수 있도록 하는 기능이다. 대상 개체의 위치와 방향을 기반으로 캐릭터 손의 위치와 방향을 계산하여 작동한다. 언리얼 엔진 4에서 Hand IK를 사용하려면 스켈레탈 메시가 있는 캐릭터와 손 애니메이션이 포함된 애니메이션 세트가 있어야 한다. Hand IK 시스템은 애니메이션 데이터를 사용하여 캐릭터 손의 위치와 방향을 실시간으로 계산한다. 목차 Weapon Unreal Engine 4의 Hand IK(Inverse Kinematics)는 캐릭터의 가상 손이 사실적인 방식으로 환경 및 개체와 상호 작용할 수 있도록 하는 기능이다. 대상 개체의 위치와 방향… -
[UE] TPS Weapon, AnimNotify, Aim
[UE] TPS Weapon, AnimNotify, Aim
2023.03.17글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자입니다 목차 Weapon - Equip&Unequip, Aim 구현하기 Characters CAnimInstance.h .cppCPlayer.h .cppUtilities CHelpers.hCLog.h .cppWeapons CWeapon.h .cppCWeapon_AR4.h .cppCWeaponComponent.h .cppCGameMode.h .cppGlobal.hCAnimNotifyState_Equip.h .cpp 생성 CWeapon CW… -
[UE] Line Trace, Multi Trace, TPS 기본 세팅
[UE] Line Trace, Multi Trace, TPS 기본 세팅
2023.03.15목차 Line Trace 01_Spawn C01_Properties.h .cppC02_MeshC02_Mesh_SphereC02_Mesh_ConeC03_Spawner.h .cpp02_Profiler C01_Log.h .cppC02_DrawDebug.h .cpp03_Collision C01_ActorOverlap.h .cppC02_ComponentOverlap.h .cppC03_OverlapAndHit.h .cpp C04_Trigger.h .cppC04_Light.h .cpp C05_MultiTrigger.h .cpp C05_FallingBox.h .cpp C05_SpotLight.h .cpp C06_EventTrigger.h .cpp C06_Explosion.h .cpp C07_Override.h …. -
[UE] Collision(Override), BP와 C++ 실행순서
[UE] Collision(Override), BP와 C++ 실행순서
2023.03.14충돌 검사를 수행하고 나면, 충돌한 물체의 위치와 방향, 속도 등을 계산할 수 있다. 이를 바탕으로 충돌한 물체에 대한 반응을 구현할 수 있다. 이를 위해서는 충돌 응답(Collision Response) 코드를 작성해야 한다. 충돌 응답 코드는 충돌한 물체가 서로 어떻게 반응해야 하는지를 결정하는 것이다. 예를 들어, 물체가 서로 충돌하면 반대 방향으로 튕겨져 나가거나, 속도가 감소하는 등의 반응을 구현할 수 있다. 목차 Collision - Override 01_Spawn C01_Properties.h .cppC02_MeshC02_Mesh_SphereC02_Mesh_ConeC03_Spawner.h .cpp02_Profiler C01_Log.h .cppC02_DrawDebug.h .cpp03_C…
댓글을 사용할 수 없습니다.