[UE] Hit Data, Effect, Object Pooling(오브젝트 풀링)

목차
Source | ||
Characters | ||
CAnimInstance.h .cpp CEnemy.h .cpp CPlayer.h .cpp ICharacter.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 .cpp CAnimNotifyState_Collision.h .cpp CAnimNotifyState_Combo.h .cpp CAnimNotifyState_Equip.h .cpp |
||
Utilities | ||
CHelper.h CLog.h .cpp |
||
Weapons | ||
CDoAction_Combo.h .cpp CAttachment.h .cpp CDoAction.h .cpp CEquipment.h .cpp CWeaponAsset.h .cpp CWeaponStructures.h .cpp |
||
Global.h CGameMode.h .cpp U2212_06.Build.cs |
||
U2212_06.uproject | ||
Hit Data 활용하기
CWeaponStructure - Hit Data 추가하기
CWeaponStructure.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; }; USTRUCT() struct FDoActionData { GENERATED_BODY() public: UPROPERTY(EditAnywhere) class UAnimMontage* Montage; UPROPERTY(EditAnywhere) float PlayRate = 1; UPROPERTY(EditAnywhere) bool bCanMove = true; UPROPERTY(EditAnywhere) bool bFixedCamera; UPROPERTY(EditAnywhere) class UFXSystemAsset* Effect; //사용할 Effect 변수 UPROPERTY(EditAnywhere) FVector EffectLocation = FVector::ZeroVector;//(Effect)지정 방향의 보정치. UPROPERTY(EditAnywhere) FVector EffectScale = FVector::OneVector;//Effect 크기 기본값 1 설정. public: void DoAction(class ACharacter* InOwner); }; USTRUCT() struct FHitData { GENERATED_BODY() UPROPERTY(EditAnywhere) class UAnimMontage* Montage; UPROPERTY(EditAnywhere) float PlayRate = 1; UPROPERTY(EditAnywhere) float Power; UPROPERTY(EditAnywhere) float Launch = 100; UPROPERTY(EditAnywhere) float StopTime; UPROPERTY(EditAnywhere) class USoundWave* Sound; UPROPERTY(EditAnywhere) class UFXSystemAsset* Effect; UPROPERTY(EditAnywhere) FVector EffectLocation = FVector::ZeroVector; UPROPERTY(EditAnywhere) FVector EffectScale = FVector::OneVector; public: void SendDamage(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther); void PlayMontage(class ACharacter* InOwner); void PlayHitStop(UWorld* InWorld); void PlaySoundWave(class ACharacter* InOwner); }; USTRUCT() struct FActionDamageEvent : public FDamageEvent { GENERATED_BODY() public: FHitData* HitData; }; UCLASS() class U2212_06_API UCWeaponStructures : public UObject { GENERATED_BODY() };
FHitData 구조체 추가
- 구조체 내에 변수 추가
- class UAnimMontage* Montage;
- float PlayRate = 1;
- float Power;
- float Launch = 100;
- float StopTime;
- class USoundWave* Sound;
- class UFXSystemAsset* Effect;
- FVector EffectLocation = FVector::ZeroVector;
- FVector EffectScale = FVector::OneVector;
- 함수 추가
- void SendDamage(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther);
- void PlayMontage(class ACharacter* InOwner);
- void PlayHitStop(UWorld* InWorld);
- void PlaySoundWave(class ACharacter* InOwner);
CWeaponStructure.cpp
더보기
#include "Weapons/CWeaponStructures.h" #include "Global.h" #include "GameFramework/Character.h" #include "Components/CStateComponent.h" #include "Components/CMovementComponent.h" #include "Animation/AnimMontage.h" void FDoActionData::DoAction(ACharacter* InOwner) { UCMovementComponent* movement = CHelpers::GetComponent<UCMovementComponent>(InOwner); if (!!movement) { if (bFixedCamera) movement->EnableFixedCamera(); if (bCanMove == false) movement->Stop(); } if (!!Montage) InOwner->PlayAnimMontage(Montage, PlayRate); } void FHitData::SendDamage(ACharacter* InAttacker, AActor* InAttackCauser, ACharacter* InOther) { FActionDamageEvent e; e.HitData = this; InOther->TakeDamage(Power, e, InAttacker->GetController(), InAttackCauser); } void FHitData::PlayMontage(ACharacter* InOwner) { if (!!Montage) InOwner->PlayAnimMontage(Montage, PlayRate); } void FHitData::PlayHitStop(UWorld* InWorld) { CheckTrue(FMath::IsNearlyZero(StopTime)); TArray<ACharacter*> characters; for(AActor* actor : InWorld->GetCurrentLevel()->Actors) { ACharacter* character = Cast<ACharacter>(actor); if(!!character) { character->CustomTimeDilation = 1e-3f; characters.Add(character); } } //익명 메소드 //람다 클로저 //람다는 외부에서 닫힌 상태로 실행되는 객체 FTimerDelegate timerDelegate; timerDelegate.BindLambda([=]() { for (ACharacter* character : characters) character->CustomTimeDilation = 1; }); FTimerHandle timerHandle; InWorld->GetTimerManager().SetTimer(timerHandle, timerDelegate, StopTime, false); } void FHitData::PlaySoundWave(ACharacter* InOwner) { CheckNull(Sound); UWorld* world = InOwner->GetWorld(); FVector location = InOwner->GetActorLocation(); UGameplayStatics::SpawnSoundAtLocation(world, Sound, location); }
FHitData 구조체
- 함수 정의
- void FHitData::SendDamage(ACharacter* InAttacker, AActor* InAttackCauser, ACharacter* InOther)
- void FHitData::PlayMontage(ACharacter* InOwner)
- void FHitData::PlayHitStop(UWorld* InWorld)
- void FHitData::PlaySoundWave(ACharacter* InOwner)
CWeaponAsset
CWeaponAsset.h
더보기
#pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "Weapons/CWeaponStructures.h" #include "CWeaponAsset.generated.h" UCLASS() class U2212_06_API UCWeaponAsset : public UDataAsset { GENERATED_BODY() private: UPROPERTY(EditAnywhere) TSubclassOf<class ACAttachment> AttachmentClass; UPROPERTY(EditAnywhere) FEquipmentData EquipmentData; UPROPERTY(EditAnywhere) TSubclassOf<class UCEquipment> EquipmentClass; UPROPERTY(EditAnywhere) TSubclassOf<class UCDoAction> DoActionClass; UPROPERTY(EditAnywhere) TArray<FDoActionData> DoActionDatas; //CWeaopnStructure내의 FDoActionData UPROPERTY(EditAnywhere) TArray<FHitData> HitDatas; //CWeaopnStructure내의 FHitData public: FORCEINLINE class ACAttachment* GetAttachment() { return Attachment; }//외부에 생성된 것을 리턴해줌. FORCEINLINE class UCEquipment* GetEquipment() { return Equipment; }//외부에 생성된 것을 리턴해줌. FORCEINLINE class UCDoAction* GetDoAction() { return DoAction; }//외부에 생성된 것을 리턴해줌. public: UCWeaponAsset(); void BeginPlay(class ACharacter* InOwner); private: //UPROPERTY를 붙여 가비지 콜렉터가 제거하기 전까지 물고 있게 만든다. //UWeaponAsset은 UObject로부터 상속받아 Actor의 생성주기에 영향을 받지 않아 가비지 콜렉터에 영향을 받는다. UPROPERTY() class ACAttachment* Attachment; UPROPERTY() class UCEquipment* Equipment; UPROPERTY() class UCDoAction* DoAction; };
변수 추가
- TArray<FHitData> HitDatas
CWeaponAsset.cpp
더보기
#include "Weapons/CWeaponAsset.h" #include "Global.h" #include "CAttachment.h" #include "CEquipment.h" #include "CDoAction.h" #include "GameFramework/Character.h" UCWeaponAsset::UCWeaponAsset() { AttachmentClass = ACAttachment::StaticClass();//기본값 EquipmentClass = UCEquipment::StaticClass();//기본값 DoActionClass = UCDoAction::StaticClass();//기본값 } void UCWeaponAsset::BeginPlay(ACharacter* InOwner) { if (!!AttachmentClass)//AttachmentClass가 선택되어 있다면 { FActorSpawnParameters params; params.Owner = InOwner; Attachment = InOwner->GetWorld()->SpawnActor<ACAttachment>(AttachmentClass, params); } if (!!EquipmentClass)//EquipmentClass가 선택되어 있다면 { Equipment = NewObject<UCEquipment>(this, EquipmentClass); Equipment->BeginPlay(InOwner, EquipmentData); if (!!Attachment)//Attachment가 있다면 { Equipment->OnEquipmentBeginEquip.AddDynamic(Attachment, &ACAttachment::OnBeginEquip); Equipment->OnEquipmentUnequip.AddDynamic(Attachment, &ACAttachment::OnUnequip); } } if(!!DoActionClass) { DoAction = NewObject<UCDoAction>(this, DoActionClass); DoAction->BeginPlay(Attachment, Equipment, InOwner, DoActionDatas, HitDatas); if (!!Attachment) { Attachment->OnAttachmentBeginCollision.AddDynamic(DoAction, &UCDoAction::OnAttachmentBeginCollision); Attachment->OnAttachmentEndCollision.AddDynamic(DoAction, &UCDoAction::OnAttachmentEndCollision); Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &UCDoAction::OnAttachmentBeginOverlap); Attachment->OnAttachmentEndOverlap.AddDynamic(DoAction, &UCDoAction::OnAttachmentEndOverlap); } } }
DoAction->BeginPlay 호출 시 HitDatas도 같이 넘겨준다.
- if ( !!DoActionClass ) {
DoAction = NewObject<UCDoAction>(this, DoActionClass);
DoAction->BeginPlay(Attachment, Equipment, InOwner, DoActionDatas, HitDatas); }
CDoAction
CDoAction.h
더보기
#pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "Weapons/CWeaponStructures.h" #include "CDoAction.generated.h" UCLASS(Abstract)//DoAction 그 자체로는 객체화되면 안 되기 때문에 Abstract을 붙여준다. class U2212_06_API UCDoAction : public UObject { GENERATED_BODY() public: UCDoAction(); virtual void BeginPlay //재정의 할 수 있도록 virtual로 만든다. ( class ACAttachment* InAttachment, class UCEquipment* InEquipment, class ACharacter* InOwner, const TArray<FDoActionData>& InDoActionDatas,//CWeaponStucture내의 FDoActionData const TArray<FHitData>& InHitDatas //CWeaponStucture내의 FHitData ); public: //재정의 할 수 있도록 virtual로 만든다. virtual void DoAction(); virtual void Begin_DoAction(); virtual void End_DoAction(); public: UFUNCTION() virtual void OnAttachmentBeginCollision() {} UFUNCTION() virtual void OnAttachmentEndCollision() {} UFUNCTION() virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther) { } UFUNCTION() virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class ACharacter* InOther) { } protected: bool bBeginAction; class ACharacter* OwnerCharacter; class UWorld* World; class UCMovementComponent* Movement; class UCStateComponent* State; TArray<FDoActionData> DoActionDatas; TArray<FHitData> HitDatas; };
CDoAction.cpp
더보기
#include "Weapons/CDoAction.h" #include "Global.h" #include "CAttachment.h" #include "CEquipment.h" #include "GameFramework/Character.h" #include "Components/CStateComponent.h" #include "Components/CMovementComponent.h" UCDoAction::UCDoAction() { } void UCDoAction::BeginPlay(ACAttachment* InAttachment, UCEquipment* InEquipment, ACharacter* InOwner, const TArray<FDoActionData>& InDoActionDatas, const TArray<FHitData>& InHitDatas) { OwnerCharacter = InOwner; World = OwnerCharacter->GetWorld(); State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter); Movement = CHelpers::GetComponent<UCMovementComponent>(OwnerCharacter); DoActionDatas = InDoActionDatas; HitDatas = InHitDatas; } void UCDoAction::DoAction() { State->SetActionMode(); } void UCDoAction::Begin_DoAction() { bBeginAction = true; } void UCDoAction::End_DoAction() { bBeginAction = false; State->SetIdleMode(); Movement->Move(); Movement->DisableFixedCamera(); }
CDoAction_Combo
CDoAction_Combo.h
더보기
#pragma once #include "CoreMinimal.h" #include "Weapons/CDoAction.h" #include "CDoAction_Combo.generated.h" UCLASS() class U2212_06_API UCDoAction_Combo : public UCDoAction { GENERATED_BODY() public: FORCEINLINE void EnableCombo() { bEnable = true; } FORCEINLINE void DisableCombo() { bEnable = false; } public: void DoAction() override; void Begin_DoAction() override; void End_DoAction() override; public: void OnAttachmentBeginOverlap(class ACharacter* InAttacker, AActor* InAttackCauser, class ACharacter* InOther) override; private: int32 Index; bool bEnable; bool bExist; };
변경사항 없음
CDoAction_Combo.cpp
더보기
#include "Weapons/DoActions/CDoAction_Combo.h" #include "Global.h" #include "GameFramework/Character.h" #include "Components/CStateComponent.h" void UCDoAction_Combo::DoAction() { CheckTrue(DoActionDatas.Num() < 1); if (bEnable) //bEnable이라면 Combo 구간 { bEnable = false; bExist = true; return; } CheckFalse(State->IsIdleMode()); Super::DoAction(); //첫 타격이 들어간 후에 Combo의 bEnable이 호출되어야 한다. 그래서 맨 위가 아닌 여기에 위치한다.//첫 타격 시 IsIdleMode()를 체크 통과한 후 부모의 DoAction으로 들어가 State->ActionMode()로 변경한다. DoActionDatas[Index].DoAction(OwnerCharacter); } void UCDoAction_Combo::Begin_DoAction() { Super::Begin_DoAction(); CheckFalse(bExist);//다음 Combo가 없으면 바로 End_DoAction으로 이동. bExist = false; DoActionDatas[++Index].DoAction(OwnerCharacter); } void UCDoAction_Combo::End_DoAction() { Super::End_DoAction(); Index = 0; } void UCDoAction_Combo::OnAttachmentBeginOverlap(ACharacter* InAttacker, AActor* InAttackCauser, ACharacter* InOther) { Super::OnAttachmentBeginOverlap(InAttacker, InAttackCauser, InOther);//CDoAction.h의 OnAttachmentBeginOverlap CheckNull(InOther); FActionDamageEvent e; e.HitData = &HitDatas[0]; //TakeDamage 체크용 디버그 //CLog::Log(InOther->GetName()); //InOther->TakeDamage(20, FDamageEvent(), InAttacker->GetController(), InAttackCauser);//데미지 적용. //InOther->TakeDamage(e.HitData->Power, e, InAttacker->GetController(), InAttackCauser);//업캐스팅. 업캐스팅은 항상 성립한다. HitDatas[Index].SendDamage(InAttacker, InAttackCauser, InOther); }
OnAttachment BeginOverlap 수정
실행화면

Effect 추가하기
U2212_06.Build.cs
U2212_06.Build.cs
더보기
using UnrealBuildTool; public class U2212_06 : ModuleRules { public U2212_06(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicIncludePaths.Add(ModuleDirectory); PublicDependencyModuleNames.Add("Core"); PrivateDependencyModuleNames.Add("CoreUObject"); PrivateDependencyModuleNames.Add("Engine"); PrivateDependencyModuleNames.Add("InputCore"); PrivateDependencyModuleNames.Add("Niagara"); } }
PrivateDependencyModuleNames 추가
- PrivateDependencyModuleNames.Add("Niagara");
- Niagara 파티클을 사용하기 위해 추가하였다.
CHelpers.h
CHelpers.h
더보기
#pragma once #include "CoreMinimal.h" #include "Particles/ParticleSystem.h" #include "NiagaraSystem.h" #include "NiagaraFunctionLibrary.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_06_API CHelpers { public: template<typename T> static void CreateComponent(AActor* InActor, T** OutComponent, FName InName, USceneComponent* InParent = nullptr, FName InSocketName = NAME_None) { *OutComponent = InActor->CreateDefaultSubobject<T>(InName); if (!!InParent) { (*OutComponent)->SetupAttachment(InParent, InSocketName); //이렇게 사용하면 Socket Name에 _를 사용하면 안 된다. 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; } static void AttachTo(AActor* InActor, USceneComponent* InParent, FName InSocketName) { InActor->AttachToComponent(InParent, FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSocketName); } static void PlayEffect(UWorld* InWorld, UFXSystemAsset* InAsset, const FTransform& InTransform, USkeletalMeshComponent* InMesh = nullptr, FName InSocketName = NAME_None) { UParticleSystem* particle = Cast<UParticleSystem>(InAsset); UNiagaraSystem* niagara = Cast<UNiagaraSystem>(InAsset); FVector location = InTransform.GetLocation(); FRotator rotation = FRotator(InTransform.GetRotation()); FVector scale = InTransform.GetScale3D(); if(!!InMesh) //InMesh에 붙어있다면 { if(!!particle) //particle이라면 { UGameplayStatics::SpawnEmitterAttached(particle, InMesh, InSocketName, location, rotation, scale); return; } if(!!niagara) //niagara라면 { UNiagaraFunctionLibrary::SpawnSystemAttached(niagara, InMesh, InSocketName, location, rotation, scale, EAttachLocation::KeepRelativeOffset, true, ENCPoolMethod::None);//Pooling풀링 사용. return; } } if(!!particle) //어디에 붙어있지 않고 particle이면 { UGameplayStatics::SpawnEmitterAtLocation(InWorld, particle, InTransform);//해당 위치에서 실행 return; } if (!!niagara) //어디에 붙어있지 않고 niagara면 { UNiagaraFunctionLibrary::SpawnSystemAtLocation(InWorld, niagara, location, rotation, scale);//해당 위치에서 실행 return; } } };
헤더 추가
- #include "Particles/ParticleSystem.h"
- #include "NiagaraSystem.h"
- #include "NiagaraFunctionLibrary.h"
Effect 추가
- static void PlayEffect(UWorld* InWorld, UFXSystemAsset* InAsset, const FTransform& InTransform, USkeletalMeshComponent* InMesh = nullptr, FName InSocketName = NAME_None)
오브젝트 풀링 (Object Pooling)
필요한만큼 미리 생성해서 필요할 때 꺼내 사용한다. 배열보다 vector를 사용하는게 좋다. vector가 수정하기도 체크하기도 더 낫다. 속도 차이는 없다고봐도 무방하다.
장점: 메모리 단편화를 방지할 수 있다.
단점: 처음에 메모리 구간이 많이 소요될 수 있다.
'⭐ Unreal Engine > UE RPG Weapon System' 카테고리의 다른 글
[UE] 주먹 공격 및 카메라 흔들림(Camera Shake) 효과 구현 (0) | 2023.05.11 |
---|---|
[UE] Hit Effect 구현, Status Component, Hammer 공격 구현 (0) | 2023.05.10 |
[UE] AnimNotify, DoAction (0) | 2023.05.08 |
[UE] 무기 장착 및 기본 공격하기 (0) | 2023.05.02 |
[UE] 무기 시스템 설계하기 (0) | 2023.05.01 |
댓글
이 글 공유하기
다른 글
-
[UE] 주먹 공격 및 카메라 흔들림(Camera Shake) 효과 구현
[UE] 주먹 공격 및 카메라 흔들림(Camera Shake) 효과 구현
2023.05.11 -
[UE] Hit Effect 구현, Status Component, Hammer 공격 구현
[UE] Hit Effect 구현, Status Component, Hammer 공격 구현
2023.05.10 -
[UE] AnimNotify, DoAction
[UE] AnimNotify, DoAction
2023.05.08 -
[UE] 무기 장착 및 기본 공격하기
[UE] 무기 장착 및 기본 공격하기
2023.05.02
댓글을 사용할 수 없습니다.