[Unreal_C++_DarkSoul]#13_Impact Effect
Combo 공격 무기들의 충격 이팩트를 구현합니다.
Overview
- 개요 및 설계
- 코드
- 결과 영상
개요
1. 목적
- Combo 공격을 수행하는 무기들의 Impact Effect를 구현합니다.
- Combo 공격 무기의 충돌 이벤트에서 적절한 위치에 Impact Effect를 활성화하는 것이 목표입니다.
2. 설계
- 먼저, Power 컴포넌트는 데이터 멤버로 속해 있는 무기 클래스에 대한 정보를 Data Table을 통해 전달받습니다.
- 이때, Power 컴포넌트는 Imapct Effect 정보를 "TArray< TTuple<FName, bool >>"에 저장하고, Fname과 bool 자료형은 각각 무기의 Sekeltal Mesh에 별도로 추가한 Socket 이름과 활성화 여부를 의미합니다.
- Power 컴포넌트는 피 충돌 객체에게 대미지를 전달하고, Imapct Effect를 활성화합니다.
- 자세한 내용은 아래 코드를 살펴보며 설명하겠습니다.
#1. 코드
1. UC_PowerComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Global/Global.h"
#include "C_PowerComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DARKSOUL_API UC_PowerComponent : public UActorComponent
{
//...
private:
UFUNCTION()
void LoadPowerInfoFromDataTable();
public:
UFUNCTION()
bool SendDamage(AActor* DamageCauser, AActor* OtherActor, bool IsSkillDamage = false, float SkillDamage = 0.f);
void SpawnImpactEffect(const class ACharacter** InCharacter);
private:
UPROPERTY(EditDefaultsOnly, category = "Data")
class UDataTable* DataTable_PowerInfo;
UPROPERTY(VisibleAnywhere, category = "Data")
FPowerInfo PowerInfo_Struct;
private:
TArray< TTuple<FName, bool> > IsImpactEffectSocketNamesActive;
//...
};
2. UC_PowerComponent::UC_PowerComponent()
UC_PowerComponent::UC_PowerComponent()
{
PrimaryComponentTick.bCanEverTick = false;
// #0 Left
IsImpactEffectSocketNamesActive.Add(TTuple<FName, bool>(TEXT("ImpactEffect_L"), false)) ;
// #1 Right
IsImpactEffectSocketNamesActive.Add(TTuple<FName, bool>(TEXT("ImpactEffect_R"), false));
}
Details
- 먼저, Power 컴포넌트의 생성자를 살펴보겠습니다.
- TArray<TTuple<FName, bool>>에 두 개의 항목을 삽입합니다.
- 하나는 캐릭터 기준 오른손에 Attach 한 무기의 Imapct Effect를 렌더링할 위치(Socket Name)와 활성화 여부입니다. 물론, 생성 당시에는 Imapct Effec를 비 활성화하기위해 bool 변수는 "false"로 초기화합니다.
- 다른 하나는 캐릭터 기준 왼손에 Attach한 무기의 Impact Effect 렌더링 위치와 활성화 여부입니다. 이유는 위와 같습니다.
3. UC_PowerComponent::LoadPowerInfoFromDataTable()
void UC_PowerComponent::LoadPowerInfoFromDataTable()
{
CheckNull(DataTable_PowerInfo);
AActor* OwnerActor = GetOwner();
CheckNull(OwnerActor);
TArray<FName>RowNames = DataTable_PowerInfo->GetRowNames();
for (auto RowName : RowNames)
{
FString RowNameStr = RowName.ToString();
FString OwnerName = OwnerActor->GetName().Mid(3, OwnerActor->GetName().Len() - 7);
if (OwnerName.Contains(RowNameStr))
{
const FString ContextString(TEXT("Weapon Power Info"));
FPowerInfo* RowStruct = DataTable_PowerInfo->FindRow<FPowerInfo>(RowName, ContextString, true);
PowerInfo_Struct = *RowStruct;
}
}
}
Details
- LoadPowerInfoFromDataTable 함수는 말 그대로, 전달받은 Data Table(무기 정보)을 별도의 데이터 멤버 PowerInfo_Struct(구조체)에 Load 하는 함수입니다.
4. UC_PowerComponent::SendDamage()
bool UC_PowerComponent::SendDamage(AActor* DamageCauser, AActor* OtherActor, bool IsSkillDamage, float SkillBasePower)
{
const ACharacter* OwnerCharacter = Cast<ACharacter>(GetOwner()->GetOwner());
// #0 Send Damage
float damage = UGameplayStatics::ApplyDamage(
OtherActor,
IsSkillDamage ? SkillBasePower : PowerInfo_Struct.AttackPower,
OwnerCharacter->GetInstigatorController(),
GetOwner(),
(IsSkillDamage && PowerInfo_Struct.DamageTypeClass) ? PowerInfo_Struct.DamageTypeClass : UDamageType::StaticClass()
);
// #1 Impact Effect Spawn
SpawnImpactEffect(&OwnerCharacter);
return damage > 0.f;
}
Details
- SendDamage() 메서드는 피 충돌 객체에게 해당 무기의 공격력만큼 Damage를 전달하는 함수입니다.
- 함수 내부에서 호출하는 SpawnEffect() 함수를 눈여겨봐야 합니다.
- 피 충돌 객체에게 대미지를 전달하고, SpawnEffect() 멤버 함수를 호출함으로써, 무기의 Impact Effect를 활성화합니다.
5. UC_PowerComponent::SpawnEffect(const ACharacter** InCharacter)
void UC_PowerComponent::SpawnImpactEffect(const ACharacter** InCharacter)
{
CheckNull(PowerInfo_Struct.ImpactEffect);
UNiagaraComponent* Effect;
for (uint8 i = 0; i < IsImpactEffectSocketNamesActive.Num(); i++)
{
if (IsImpactEffectSocketNamesActive[i].Get<1>() == true)
{
Effect = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
(*InCharacter)->GetWorld(),
PowerInfo_Struct.ImpactEffect,
(*InCharacter)->GetMesh()->GetSocketLocation(IsImpactEffectSocketNamesActive[i].Get<0>()),
(*InCharacter)->GetMesh()->GetSocketRotation(IsImpactEffectSocketNamesActive[i].Get<0>())
);
}
}
}
Details
- SpawnEffect() 메서드는 "ACharacter" 데이터 타입의 참조 값을 인자로 넘겨받습니다.
- 넘겨받은 "ACharacter" 유형의 참조 값으로 캐릭터의 스켈레톤 트리 중 Impact Effect를 스폰할 소켓 위치를 구하고, 해당 위치에 Impact Effect를 스폰합니다.
- 각 Socket Name의 활성화 여부는 이후에 선언할 Anim Notify State들을 활용할 예정입니다.
- 이때, TTuple 데이터 타입을 활용합니다. 자세한 내용은 위 링크를 참조해 주세요.
5. ANS_OnAndOffImpactEffect_R.cpp
#include "OnAndOffImpactEffect_R.h"
#include "Global/Global.h"
#include "Weapon/Weapon.h"
#include "Interface/OwnerWeaponInterface.h"
#include "ActorComponents/C_PowerComponent.h"
void UOnAndOffImpactEffect_R::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
{
AActor* PlayerActor = MeshComp->GetOwner();
CheckNull(PlayerActor);
if (PlayerActor->GetClass()->ImplementsInterface(UOwnerWeaponInterface::StaticClass()))
{
IOwnerWeaponInterface* Interface = Cast<IOwnerWeaponInterface>(PlayerActor);
CheckNull(Interface);
AWeapon* CurrentWeapon = Interface->GetOwnerCurrentWeapon();
CheckNull(CurrentWeapon);
CurrentWeapon->GetPowerComponent()->SetIsImpactEffectSocketNamesActive(1, true);
}
}
void UOnAndOffImpactEffect_R::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
AActor* PlayerActor = MeshComp->GetOwner();
CheckNull(PlayerActor);
if (PlayerActor->GetClass()->ImplementsInterface(UOwnerWeaponInterface::StaticClass()))
{
IOwnerWeaponInterface* Interface = Cast<IOwnerWeaponInterface>(PlayerActor);
CheckNull(Interface);
AWeapon* CurrentWeapon = Interface->GetOwnerCurrentWeapon();
CheckNull(CurrentWeapon);
CurrentWeapon->GetPowerComponent()->SetIsImpactEffectSocketNamesActive(1, false);
}
}
Details
- Combo 무기들의 공격 시 재생할 Anim Motage들에 추가할 애님 노티파이 스테이트를 구현했습니다.
- 위 코드는 캐릭터 기준 오른손에 장착된 Combo 무기들의 Anim Montage들에 삽입할 노티파이 스테이트입니다.
- 노티파이 스테이트 클래스에서 정의한 왼손 무기의 활성화 여부(0), 그리고 오른손 무기의 활성화 여부(1)는 해당 무기의 충돌 활성화 여부를 결정하는 노티파이 스테이트와 동일한 구간으로 설정되어, 오직 피 충돌 객체가 존재할 때만 활성화될 수 있도록 설계했습니다.
- 캐릭터 기준 왼손에 장착된 무기 또한 위 코드와 같습니다.
#3. 결과 영상
1. 영상
'개인프로젝트' 카테고리의 다른 글
[Unreal_C++_DarkSoul]#15_문제 해결, Data Table 로드 함수 (1) | 2023.05.08 |
---|---|
[Unreal_C++_DarkSoul]#14_문제 해결, Actor Component 간 소통 (0) | 2023.03.26 |
[Unreal_C++_DarkSoul]#12_문제 해결, Targeting 회전 속도 (0) | 2022.12.25 |
[Unreal_C++_DarkSoul]#11_기능 구현, Parkour(Vaulting) (0) | 2022.12.25 |
[Unreal_C++_DarkSoul]#10_문제 해결, AIController (0) | 2022.12.25 |