[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