[UE] 무기 시스템 설계하기

목차
Source | ||
Characters | ||
CAnimInstance.h .cpp CPlayer.h .cpp ICharacter.h .cpp |
||
Components | ||
CMontagesComponent.h .cpp CMovementComponent.h .cpp CStateComponent.h .cpp CWeaponComponent.h .cpp |
||
Notifies | ||
CAnimNotify_EndState.h .cpp | ||
Utilities | ||
CHelper.h CLog.h .cpp |
||
Weapons | ||
CAttachment.h .cpp CEquipment.h .cpp 생성 CWeaponAsset.h .cpp CWeaponStructures.h .cpp 생성 |
||
Global.h CGameMode.h .cpp U2212_06.Build.cs |
||
U2212_06.uproject | ||
무기 시스템 만들기
Weapon System 구조 개요
WeponComponent | WeaponAsset | Attachment Equipment DoAction |
CPlayer + BP_CPlayer
CPlayer.h
#pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "Components/CStateComponent.h" #include "Characters/ICharacter.h" #include "CPlayer.generated.h" UCLASS() class U2212_06_API ACPlayer : public ACharacter, public IICharacter //다중상속 { GENERATED_BODY() private: UPROPERTY(VisibleAnywhere) class USpringArmComponent* SpringArm; UPROPERTY(VisibleAnywhere) class UCameraComponent* Camera; private: UPROPERTY(VisibleAnywhere) class UCWeaponComponent* Weapon; UPROPERTY(VisibleAnywhere) class UCMontagesComponent* Montages; UPROPERTY(VisibleAnywhere) class UCMovementComponent* Movement; UPROPERTY(VisibleAnywhere) class UCStateComponent* State; public: ACPlayer(); protected: virtual void BeginPlay() override; public: virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; private: UFUNCTION() void OnStateTypeChanged(EStateType InPrevType, EStateType InNewType); private: void OnAvoid(); private: void BackStep(); public: void End_BackStep() override;//ICharacter의 함수 오버라이드 };
Player 내에 WeaponComponent 추가
- UPROPERTY(VisibleAnywhere)
class UCWeaponComponent* Weapon;
CPlayer.cpp
#include "Characters/CPlayer.h" #include "Global.h" #include "CAnimInstance.h" #include "GameFramework/SpringArmComponent.h" #include "GameFramework/CharacterMovementComponent.h" #include "Camera/CameraComponent.h" #include "Components/SkeletalMeshComponent.h" #include "Components/InputComponent.h" #include "Components/CMontagesComponent.h" #include "Components/CMovementComponent.h" #include "Components/CWeaponComponent.h" ACPlayer::ACPlayer() { CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetMesh()); CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm); CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon"); CHelpers::CreateActorComponent<UCMontagesComponent>(this, &Montages, "Montage"); CHelpers::CreateActorComponent<UCMovementComponent>(this, &Movement, "Movement"); CHelpers::CreateActorComponent<UCStateComponent>(this, &State, "State"); GetMesh()->SetRelativeLocation(FVector(0, 0, -90)); GetMesh()->SetRelativeRotation(FRotator(0, -90, 0)); USkeletalMesh* mesh; CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'"); GetMesh()->SetSkeletalMesh(mesh); TSubclassOf<UCAnimInstance> animInstance; CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_Character.ABP_Character_C'"); GetMesh()->SetAnimClass(animInstance); SpringArm->SetRelativeLocation(FVector(0, 0, 140)); SpringArm->SetRelativeRotation(FRotator(0, 90, 0)); SpringArm->TargetArmLength = 200; SpringArm->bDoCollisionTest = false; SpringArm->bUsePawnControlRotation = true; SpringArm->bEnableCameraLag = true; GetCharacterMovement()->RotationRate = FRotator(0, 720, 0); } void ACPlayer::BeginPlay() { Super::BeginPlay(); Movement->OnRun(); //Movement의 기본을 Run으로 설정 Movement->DisableControlRotation();//Movement의 기본을 DisableControlRotation으로 설정 State->OnStateTypeChanged.AddDynamic(this, &ACPlayer::OnStateTypeChanged); } void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); PlayerInputComponent->BindAxis("MoveForward", Movement, &UCMovementComponent::OnMoveForward); PlayerInputComponent->BindAxis("MoveRight", Movement, &UCMovementComponent::OnMoveRight); PlayerInputComponent->BindAxis("VerticalLook", Movement, &UCMovementComponent::OnVerticalLook); PlayerInputComponent->BindAxis("HorizontalLook", Movement, &UCMovementComponent::OnHorizontalLook); PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Pressed, Movement, &UCMovementComponent::OnSprint); PlayerInputComponent->BindAction("Sprint", EInputEvent::IE_Released, Movement, &UCMovementComponent::OnRun); PlayerInputComponent->BindAction("Avoid", EInputEvent::IE_Pressed, this, &ACPlayer::OnAvoid); PlayerInputComponent->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode); } void ACPlayer::OnStateTypeChanged(EStateType InPrevType, EStateType InNewType) { switch (InNewType) { case EStateType::BackStep: BackStep(); break; } } void ACPlayer::OnAvoid() { CheckFalse(State->IsIdleMode()); CheckFalse(Movement->CanMove()); CheckTrue(InputComponent->GetAxisValue("MoveForward") >= 0.0f);//뒷방향을 입력했다면 State->SetBackStepMode();//State을 BackStepMode로 변경한다. } void ACPlayer::BackStep() { Movement->EnableControlRotation();//정면을 바라본 상태로 뒤로 뛰어야하기 때문에 EnableControlRotation으로 만들어준다. Montages->PlayBackStepMode();//PlayBackStepMode()를 통해 몽타주 재생. } void ACPlayer::End_BackStep() { Movement->DisableControlRotation();//Backstep이 끝나면 원래대로 돌려준다. State->SetIdleMode();//Idle상태로 돌려줌. }
Player 내에 WeaponComponent 추가
- CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");
Sword 장착/해제 키 입력 추가
- PlayerInputComponent->BindAction("Sword", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetSwordMode);
BP_CPlayer

Player내에 추가한 WeaponComponet에 Data Asset 할당
- Weapon - Data Asset - Data Assets - Sword - DA_Sword 할당
CWeaponComponent.h .cpp
CWeaponComponent.h
#pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "CWeaponComponent.generated.h" UENUM(BlueprintType) enum class EWeaponType : uint8 { Fist, Sword, Hammer, Warp, Around, Bow, Max, }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType); UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent)) class U2212_06_API UCWeaponComponent : public UActorComponent { GENERATED_BODY() private://DataAsset을 받아온다. UPROPERTY(EditAnywhere, Category = "DataAsset") class UCWeaponAsset* DataAssets[(int32)EWeaponType::Max]; public: //무기 Type이 맞는지 확인해주는 함수들 FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; } FORCEINLINE bool IsFistMode() { return Type == EWeaponType::Fist; } FORCEINLINE bool IsSwordMode() { return Type == EWeaponType::Sword; } FORCEINLINE bool IsHammerMode() { return Type == EWeaponType::Hammer; } FORCEINLINE bool IsWarpMode() { return Type == EWeaponType::Warp; } FORCEINLINE bool IsAroundMode() { return Type == EWeaponType::Around; } FORCEINLINE bool IsBowMode() { return Type == EWeaponType::Bow; } public: UCWeaponComponent(); protected: virtual void BeginPlay() override; private: bool IsIdleMode();//StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다. public: class ACAttachment* GetAttachment(); class UCEquipment* GetEquipment(); public: //무기 세팅 void SetUnarmedMode(); void SetFistMode(); void SetSwordMode(); void SetHammerMode(); void SetWarpMode(); void SetAroundMode(); void SetBowMode(); private: void SetMode(EWeaponType InType); void ChangeType(EWeaponType InType); public: //무기가 바뀌었을때 통보해줄 delegate FWeaponTypeChanged OnWeaponTypeChange; private: class ACharacter* OwnerCharacter; EWeaponType Type = EWeaponType::Max; };
WeaponAsset의 데이터를 가져온다.
- UPROPERTY(EditAnywhere, Category = "DataAsset")
class UCWeaponAsset* DataAssets[(int32)EWeaponType::Max];
무기 Type이 맞는지 확인해주는 함수들 추가
- FORCEINLINE bool IsUnarmedMode() { return Type == EWeaponType::Max; }
- FORCEINLINE bool IsFistMode() { return Type == EWeaponType::Fist; }
- FORCEINLINE bool IsSwordMode() { return Type == EWeaponType::Sword; }
- FORCEINLINE bool IsHammerMode() { return Type == EWeaponType::Hammer; }
- FORCEINLINE bool IsWarpMode() { return Type == EWeaponType::Warp; }
- FORCEINLINE bool IsAroundMode() { return Type == EWeaponType::Around; }
- FORCEINLINE bool IsBowMode() { return Type == EWeaponType::Bow; }
IsIdleMode() 상태
- bool IsIdleMode();
- StateComponent, WeaponComponent 둘 다 같은 레벨이다. 서로 소유할 수 없는 관계이기 때문에 참조만해서 리턴받기 위해 IsIdleMode를 사용한다.
CWeaponComponent.cpp
#include "Components/CWeaponComponent.h" #include "Global.h" #include "CStateComponent.h" #include "GameFramework/Character.h" #include "Weapons/CWeaponAsset.h" #include "Weapons/CAttachment.h" #include "Weapons/CEquipment.h" UCWeaponComponent::UCWeaponComponent() { } void UCWeaponComponent::BeginPlay() { Super::BeginPlay(); OwnerCharacter = Cast<ACharacter>(GetOwner()); for (int32 i=0; i < (int32)EWeaponType::Max; i++) { if (!!DataAssets[i]) //DataAssets[i]이 있다면(=무기가 할당되어 있다면) DataAssets[i]->BeginPlay(OwnerCharacter);//BeginPla y 시 OwnerCharacter에 Spawn시켜준다. } } bool UCWeaponComponent::IsIdleMode() { return CHelpers::GetComponent<UCStateComponent>(OwnerCharacter)->IsIdleMode(); } ACAttachment* UCWeaponComponent::GetAttachment() { CheckTrueResult(IsUnarmedMode(), nullptr); CheckFalseResult(!!DataAssets[(int32)Type], nullptr); return DataAssets[(int32)Type]->GetAttachment(); } UCEquipment* UCWeaponComponent::GetEquipment() { CheckTrueResult(IsUnarmedMode(), nullptr); CheckFalseResult(!!DataAssets[(int32)Type], nullptr); return DataAssets[(int32)Type]->GetEquipment(); } void UCWeaponComponent::SetUnarmedMode() { GetEquipment()->Unequip(); ChangeType(EWeaponType::Max); } void UCWeaponComponent::SetFistMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Fist); } void UCWeaponComponent::SetSwordMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Sword); } void UCWeaponComponent::SetHammerMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Hammer); } void UCWeaponComponent::SetWarpMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Warp); } void UCWeaponComponent::SetAroundMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Around); } void UCWeaponComponent::SetBowMode() { CheckFalse(IsIdleMode()); SetMode(EWeaponType::Bow); } void UCWeaponComponent::SetMode(EWeaponType InType) { if (Type == InType) { SetUnarmedMode(); return; } else if (IsUnarmedMode() == false) { GetEquipment()->Unequip(); } if (!!DataAssets[(int32)InType]) { DataAssets[(int32)InType]->GetEquipment()->Equip(); ChangeType(InType); } } void UCWeaponComponent::ChangeType(EWeaponType InType) { EWeaponType prevType = Type; Type = InType; if (OnWeaponTypeChange.IsBound()) OnWeaponTypeChange.Broadcast(prevType, InType); }
CAttachment + BP_CAttachment_Sword
CAttachment.h
#pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "CAttachment.generated.h" UCLASS() class U2212_06_API ACAttachment : public AActor { GENERATED_BODY() protected: UPROPERTY(BlueprintReadOnly, VisibleAnywhere) class USceneComponent* Root; public: ACAttachment(); protected: virtual void BeginPlay() override; protected: UFUNCTION(BlueprintCallable, Category = "Attach") void AttachTo(FName InSocketName); protected: UPROPERTY(BlueprintReadOnly, Category = "Game") class ACharacter* OwnerCharacter; };
CAttachment.cpp
#include "Weapons/CAttachment.h" #include "Global.h" #include "GameFramework/Character.h" #include "Components/SceneComponent.h" ACAttachment::ACAttachment() { CHelpers::CreateComponent(this, &Root, "Root"); } void ACAttachment::BeginPlay() { OwnerCharacter = Cast<ACharacter>(GetOwner()); //ACharacter를 먼저 Cast 한 후에 Super::BeginPlay() 호출. Super::BeginPlay(); } void ACAttachment::AttachTo(FName InSocketName) { AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSocketName); }

왼쪽은 Blueprint에서 사용하는 버전인 Attach Actor To Component.
Blueprint와는 달리 C++에서는 AttachToComponent라는 함수를 사용한다.
BP_CAttachment_Sword

실행화면

게임을 플레이하면 Holster_Sword 위치에 Sword가 생성된다.
무기 장착 및 해제 시스템 만들기
CEquipment 생성
새 C++ 클래스 - Object - CEquipment 생성

CEquipment.h
#pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "Weapons/CWeaponStructures.h" #include "CEquipment.generated.h" UCLASS() class U2212_06_API UCEquipment : public UObject { GENERATED_BODY() public: void BeginPlay(class ACharacter* InOwner, const FEquipmentData& InData); public: UFUNCTION(BlueprintNativeEvent)//필요시 BP에서 재정의하기 위해 BlueprintNativeEvent사용. void Equip(); void Equip_Implementation(); UFUNCTION(BlueprintNativeEvent) void Begin_Equip(); void Begin_Equip_Implementation(); UFUNCTION(BlueprintNativeEvent) void End_Equip(); void End_Equip_Implementation(); UFUNCTION(BlueprintNativeEvent) void Unequip(); void Unequip_Implementation(); private: class ACharacter* OwnerCharacter; FEquipmentData Data; private: class UCMovementComponent* Movement; class UCStateComponent* State; private: bool bBeginEquip;//Equip이 시작되었는가 확인 bool bEquipped; //Equip이 완료되었는지 확인 };
CEquipment.cpp
#include "Weapons/CEquipment.h"
CWeaponStructures 생성
새 C++ 클래스 - Object - CWeaponStructures 생성

CWeaponStructures.h
#pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "CWeaponStructures.generated.h" USTRUCT() struct FEquipmentData { GENERATED_BODY() public: UPROPERTY(EditAnywhere) class UAnimMontage* Montage; UPROPERTY(EditAnywhere) float PlayRate = 1; UPROPERTY(EditAnywhere) bool bCanMove = true; UPROPERTY(EditAnywhere) bool bUseControlRotation = true; }; UCLASS() class U2212_06_API UCWeaponStructures : public UObject { GENERATED_BODY() };
CWeaponStructures.cpp
#include "Weapons/CWeaponStructures.h" #include "Global.h" #include "GameFramework/Character.h" #include "Components/CMovementComponent.h" #include "Components/CStateComponent.h" #include "CEquipment.h" void UCEquipment::BeginPlay(ACharacter* InOwner, const FEquipmentData& InData) { OwnerCharacter = InOwner; Data = InData; Movement = CHelpers::GetComponent<UCMovementComponent>(InOwner); State = CHelpers::GetComponent<UCStateComponent>(InOwner); } void UCEquipment::Equip_Implementation() { State->SetEquipMode(); if (Data.bCanMove == false) Movement->Stop(); if (Data.bUseControlRotation) Movement->EnableControlRotation(); if (!!Data.Montage) { OwnerCharacter->PlayAnimMontage(Data.Montage, Data.PlayRate); } else { Begin_Equip(); End_Equip(); } } void UCEquipment::Begin_Equip_Implementation() { bBeginEquip = true; } void UCEquipment::End_Equip_Implementation() { bBeginEquip = false; bEquipped = true; Movement->Move(); State->SetIdleMode(); } void UCEquipment::Unequip_Implementation() { bEquipped = false; Movement->DisableControlRotation(); }
CWeaponAsset + DA_Sword
CWeaponAsset.h
#pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "Weapons/CWeaponStructures.h" #include "CWeaponAsset.generated.h" UCLASS() class U2212_06_API UCWeaponAsset : public UDataAsset { GENERATED_BODY() private: UPROPERTY(EditAnywhere) TSubclassOf<class ACAttachment> AttachmentClass; UPROPERTY(EditAnywhere) FEquipmentData EquipmentData; UPROPERTY(EditAnywhere) TSubclassOf<class UCEquipment> EquipmentClass; public: FORCEINLINE class ACAttachment* GetAttachment() { return Attachment; }//외부에 생성된 것을 리턴해줌. FORCEINLINE class UCEquipment* GetEquipment() { return Equipment; }//외부에 생성된 것을 리턴해줌. public: UCWeaponAsset(); void BeginPlay(class ACharacter* InOwner); private: //UPROPERTY를 붙여 가비지 콜렉터가 제거하기 전까지 물고 있게 만든다. //UWeaponAsset은 UObject로부터 상속받아 Actor의 생성주기에 영향을 받지 않아 가비지 콜렉터에 영향을 받는다. UPROPERTY() class ACAttachment* Attachment; UPROPERTY() class UCEquipment* Equipment; };
CWeaponAsset.cpp
#include "Weapons/CWeaponAsset.h" #include "Global.h" #include "CAttachment.h" #include "CEquipment.h" #include "GameFramework/Character.h" UCWeaponAsset::UCWeaponAsset() { AttachmentClass = ACAttachment::StaticClass();//기본값 EquipmentClass = UCEquipment::StaticClass();//기본값 } void UCWeaponAsset::BeginPlay(ACharacter* InOwner) { if (!!AttachmentClass)//AttachmentClass가 선택되어 있다면 { FActorSpawnParameters params; params.Owner = InOwner; Attachment = InOwner->GetWorld()->SpawnActor<ACAttachment>(AttachmentClass, params); } if (!!EquipmentClass) { Equipment = NewObject<UCEquipment>(this, EquipmentClass); Equipment->BeginPlay(InOwner, EquipmentData); } }
DA_Sword

실행화면

Sword 키 입력을 하면 Equip_Sword 몽타주가 실행된다.
'⭐ Unreal Engine > UE RPG Weapon System' 카테고리의 다른 글
[UE] AnimNotify, DoAction (0) | 2023.05.08 |
---|---|
[UE] 무기 장착 및 기본 공격하기 (0) | 2023.05.02 |
[UE] Interface, AnimNotify, Backstep 구현하기 (0) | 2023.04.28 |
[UE] Component 컴포넌트, Player 이동 (0) | 2023.04.27 |
[UE] Player 초기 세팅하기 (0) | 2023.04.26 |
댓글
이 글 공유하기
다른 글
-
[UE] AnimNotify, DoAction
[UE] AnimNotify, DoAction
2023.05.08목차 Source Characters CAnimInstance.h .cppCEnemy.h .cpp 생성CPlayer.h .cppICharacter.h .cpp Components CMontagesComponent.h .cpp CMovementComponent.h .cpp CStateComponent.h .cpp CWeaponComponent.h .cpp Notifies CAnimNotifyState_BeginAction.h .cpp 생성CAnimNotifyState_EndAction.h .cpp 생성CAnimNotify_EndState.h .cppCAnimNotifyState_Collision.h .cpp 생성CAnimNotifyState_Combo.h .cpp 생성CAnimNoti… -
[UE] 무기 장착 및 기본 공격하기
[UE] 무기 장착 및 기본 공격하기
2023.05.02목차 Source Characters CAnimInstance.h .cppCPlayer.h .cpp (DoAction 입력만 추가)ICharacter.h .cpp Components CMontagesComponent.h .cpp CMovementComponent.h .cpp CStateComponent.h .cpp CWeaponComponent.h .cpp Notifies CAnimNotify_EndState.h .cppCAnimNotifyState_Equip.h .cpp 생성 Utilities CHelper.hCLog.h .cpp Weapons CAttachment.h .cppCDoAction.h .cpp 생성CEquipment.h .cppCWeaponAsset.h .cppC… -
[UE] Interface, AnimNotify, Backstep 구현하기
[UE] Interface, AnimNotify, Backstep 구현하기
2023.04.28목차 Source Characters CAnimInstance.h .cppCPlayer.h .cppICharacter.h .cpp 생성 Components CMontagesComponent.h .cpp CMovementComponent.h .cpp CStateComponent.h .cpp CWeaponComponent.h .cpp Notifies CAnimNotify_EndState.h .cpp 생성 Utilities CHelper.hCLog.h .cpp Weapons CAttachment.h .cpp 생성CWeaponAsset.h .cpp 생성 Global.hCGameMode.h .cppU2212_06.Build.cs U2212_06.uproject 인터페이스 인터… -
[UE] Component 컴포넌트, Player 이동
[UE] Component 컴포넌트, Player 이동
2023.04.27목차 Source Characters CAnimInstance.h .cppCPlayer.h .cpp Components CMontagesComponent.h .cpp .생성CMovementComponent.h .cpp 생성CStateComponent.h .cpp 생성CWeaponComponent.h .cpp 생성 Utilities CHelper.hCLog.h .cpp Global.hCGameMode.h .cppU2212_06.Build.cs U2212_06.uproject Component 만들기 (Movement, State, Montage) CMovementComponent 생성 새 C++ 클래스 - ActorComponent - CMovementComponent 생성 C…
댓글을 사용할 수 없습니다.