검기 투사체(이하 검기)는 캐릭터의 ObjectPoolSystem에서 생성 및 관리하고 있습니다. 게임을 시작하기 전 SkillSystem에서 검기 스킬을 DataTable의 정보를 토대로 스킬을 생성 및 초기화합니다.
// SkillSystem.cpp
// SkillSystem에서 DataTable의 정보를 토대로 Skill을 생성 및 초기화합니다.
TArray<UPRBaseSkill*> UPRSkillSystemComponent::CreateSkillFromDataTable(UDataTable* NewSkillDataTable)
{
TArray<UPRBaseSkill*> NewConstructSkills;
if(NewSkillDataTable != nullptr)
{
TArray<FName> RowNames = NewSkillDataTable->GetRowNames();
for(const auto& RowName : RowNames)
{
FPRConstructSkillInfo* DataTableRow = NewSkillDataTable->FindRow<FPRConstructSkillInfo>(RowName, FString(""));
if(DataTableRow != nullptr)
{
UPRBaseSkill* NewConstructSkill = CreateSkill(DataTableRow->ConstructSkillClass);
if(IsValid(NewConstructSkill) == true)
{
NewConstructSkill->SetSkillInfo(DataTableRow->SkillInfo);
NewConstructSkill->InitializeSkill();
NewConstructSkills.Emplace(NewConstructSkill);
}
}
}
}
return NewConstructSkills;
}
검기 스킬을 초기화하는 과정에서 스킬을 사용하는 캐릭터의 ObjectPoolSystem에서 PoolSize의 크기만큼 검기를 비활성화 상태에서 월드에 Spawn하고 Pool에 저장합니다. 검기 스킬의 상위 클래스인 PRBaseSkill 클래스의 초기화 함수에서 ObjectPool을 생성하고 검기 스킬에서 오버라이딩한 초기화 함수에서 검기 스킬에서 필요한 초기화 동작을 실행합니다.
// BaseSkill.cpp
// 스킬을 초기화합니다.
void UPRBaseSkill::InitializeSkill_Implementation()
{
if(IsValid(GetSkillOwner()) == true)
{
for(FPRPooledObjectInfo ObjectInfo : SkillInfo.ObjectInfos)
{
// 스킬에서 사용하는 Object의 정보가 있을 경우 Object의 정보를 ObjectPoolSystem에 전달합니다.
if(ObjectInfo.PooledObjectClass != nullptr
&& GetSkillOwner()->GetObjectPoolSystem()->IsCreatePooledObject(ObjectInfo.ObjectName) == false)
{
GetSkillOwner()->GetObjectPoolSystem()->AddPooledObjectInfo(ObjectInfo);
}
}
}
}
// DodgeAttack.cpp
// 스킬을 초기화합니다.
void UPRSkill_Kyle_DodgeAttack::InitializeSkill_Implementation()
{
Super::InitializeSkill_Implementation();
// 데이터 테이블에서 스킬이 사용하는 PRAnimMontage들을 가져옵니다.
if(IsValid(GetSkillOwner()) == true)
{
// ComboAnimMontage를 데이터 테이블에서 받아옵니다.
if(PRComboAnimMontage == FPRComboAnimMontage())
{
PRComboAnimMontage = FPRComboAnimMontage(GetSkillOwner()->GetAnimSystem()->GetPRAnimMontageFromPRAnimMontageDataTableByIDRangeToArray(PRComboAnimMontageFromID, PRComboAnimMontageToID));
}
// AnimMontage의 재생 Index를 초기화합니다.
PRComboAnimMontage.InitializePlayIndex();
}
}
// ObjectPoolSystem.cpp
void UPRObjectPoolSystemComponent::AddPooledObjectInfo(FPRPooledObjectInfo NewPooledObjectInfo)
{
PooledObjectInfos.Emplace(NewPooledObjectInfo);
}
void UPRObjectPoolSystemComponent::InitializeObjectPool()
{
// ObjectPool 초기화
if(ObjectPool.Num() > 0)
{
for(auto& PooledObjects : ObjectPool)
{
for(auto& PooledObject : PooledObjects.Value.Objects)
{
PooledObject->Destroy();
}
}
ObjectPool.Empty();
}
// ActivatePoolIndexes 초기화
if(ActivatePoolIndexes.Num() > 0)
{
ActivatePoolIndexes.Empty();
}
// ObjectInfo를 바탕으로 ObjectPool 생성
if(GetOwner()->GetWorld() != nullptr && PooledObjectInfos.Num() > 0)
{
for(const auto& PooledObjectInfo : PooledObjectInfos)
{
CreateObjectPool(PooledObjectInfo);
}
}
}
검기 스킬을 제자리에서 회피(후방 회피)를 하였을 때 재생되는 이팩트가 재생되는 타이밍에 일반공격을 입력하게 되면 검기 스킬 시전 애니메이션을 재생합니다. AnimNotify 클래스에서 캐릭터의 ObjectPoolSystem에서 검기 투사체를 활성화합니다. 그후 일반공격의 입력 타이밍에 따라 계속해서 검기를 발사하는 애니메이션을 재생합니다. 스킬의 지속시간은 2.5초이며 지속시간동안 화면의 하단에 있는 검 모양의 UI의 게이지가 감소합니다.
검 모양의 UI의 게이지가 모두 감소하거나 마지막 공격 애니메이션이 재생되었을 때, 캐릭터가 점프를 하거나 공중에서 착지했을 때, 캐릭터가 회피를 실행했을 때 검기 스킬을 취소하고 이후 일반공격을 입력하면 검기 스킬이 아닌 일반공격을 실행합니다.
검기 투사체의 Collision이 Overlap을 시작하게 되면 Trace를 실행하고 Trace에 Hit된 Actor들에게 대미지를 주고 Hit된 위치에 HitEffect를 재생합니다.
// ProjectileObject.cpp
HitBox = CreateDefaultSubobject<UBoxComponent>(TEXT("HitBox"));
HitBox->SetupAttachment(Root);
HitBox->OnComponentHit.AddDynamic(this, &APRBaseProjectileObject::OnHit);
HitBox->OnComponentBeginOverlap.AddDynamic(this, &APRBaseProjectileObject::OnBeginOverlap);
HitBox->OnComponentEndOverlap.AddDynamic(this, &APRBaseProjectileObject::OnEndOverlap);
// PRJudgementSlashProjectile.cpp
void APRJudgementSlashProjectile::OnBeginOverlap_Implementation(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
Super::OnBeginOverlap_Implementation(OverlappedComp, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);
if(IsValid(GetObjectOwner()) == true)
{
TArray<FHitResult> HitResults;
bool bIsHit = false;
const FVector TraceStart = GetActorLocation();
const FVector TraceEnd = GetActorLocation();
// 무기 소유자를 Trace 대상에서 제외합니다.
TArray<AActor*> ActorsToIgnore;
ActorsToIgnore.Emplace(GetObjectOwner());
// Debug 실행을 설정합니다.
EDrawDebugTrace::Type DebugType = EDrawDebugTrace::None;
if(bActivateDebug)
{
DebugType = EDrawDebugTrace::ForDuration;
}
// ECC_GameTraceChannel3는 TraceChannels의 PlayerAttack을 나타냅니다.
bIsHit = UKismetSystemLibrary::BoxTraceMulti(GetWorld(), TraceStart, TraceEnd, GetHitBox()->GetScaledBoxExtent(), GetHitBox()->GetComponentRotation(),
UEngineTypes::ConvertToTraceType(ECC_GameTraceChannel3), false, ActorsToIgnore, DebugType,
HitResults, true);
// Hit된 액터들에게 대미지를 줍니다.
if(bIsHit)
{
for(FHitResult Hit : HitResults)
{
if(Hit.Actor.IsValid() == true && IsHitActor(HitActors, *Hit.GetActor()) == false)
{
AActor* HitActor = Hit.GetActor();
HitActors.Emplace(HitActor, false);
// 대미지를 받은 액터인지 판별한 후 대미지를 받지 않았을 경우 대미지를 주고 ImpactNiagaraEffect를 Spawn하고 ImpactSound를 재생합니다.
if(IsTakeDamageActor(HitActors, *HitActor) == false)
{
ApplyDamage(HitActors, HitActor);
// ImpactNiagaraEffect를 Spawn합니다.
SpawnImpactNiagaraEffect(Hit.ImpactPoint, ImpactNiagaraEffectRotation);
// ImpactSound를 재생합니다.
if(ImpactSound != nullptr)
{
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ImpactSound, Hit.ImpactPoint);
}
}
}
}
}
}
}
'Project > Replica' 카테고리의 다른 글
[Project Replica] Vaulting / Vault / 장애물 뛰어넘기 (0) | 2024.04.15 |
---|---|
[Project Replica] DamageSystem 대미지 시스템 (0) | 2024.01.11 |
[Project Replica] AI 생성 시스템 AISpawnSystem (1) | 2023.12.04 |
[Project Replica] 극한회피 Extreme Dodge/TimeStopSystem (2) | 2023.10.26 |
[Project Replica] 범위 피해 스킬 Judgement Cut (0) | 2023.10.18 |