Judgement Cut 스킬의 이펙트 및 범위 피해를 주는 오브젝트(이하 Area)는 캐릭터의 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;
}
Judgement Cut 스킬을 초기화하는 과정에서 스킬을 사용하는 캐릭터의 ObjectPoolSystem에서 PoolSize의 크기만큼 Area를 비활성화 상태에서 월드에 Spawn하고 Pool에 저장합니다. Judgement Cut 스킬의 상위 클래스인 PRBaseSkill 클래스의 초기화 함수에서 ObjectPool을 생성하고 Judgement Cut 스킬에서 오버라이딩한 초기화 함수에서 Judgement Cut 스킬에서 필요한 초기화 동작을 실행합니다.
// 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);
}
}
}
}
void UPRSkill_Kyle_JudgementCut::InitializeSkill_Implementation()
{
Super::InitializeSkill_Implementation();
ActivateableCount = SkillInfo.MaxActivatableCount;
// 데이터 테이블에서 스킬이 사용하는 PRAnimMontage를 가져옵니다.
// JudgementCutPRAnimMontage를 데이터 테이블에서 받아옵니다.
if(IsValid(GetSkillOwner()) == true && JudgementCutPRAnimMontage == FPRAnimMontage())
{
JudgementCutPRAnimMontage = FPRAnimMontage(GetSkillOwner()->GetAnimSystem()->GetPRAnimMontageFromPRAnimMontageDataTableByID(JudgementCutPRAnimMontageID));
}
}
// 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);
}
}
}
스킬은 최대 5번 실행할 수 있으며 스킬을 사용할 때마다 횟수를 차감하며 0일 경우 스킬을 실행할 수 없습니다. 공중에서 실행할 시 캐릭터가 잠시동안 중력의 영향을 받지 않습니다.
스킬을 실행하면 스킬에 해당하는 AnimMontage를 재생하며 AnimNotify 클래스로 일정 범위에 대미지를 주는 Area Object를 캐릭터의 ObjectSystem에서 가져와 활성화합니다.
ObjectPoolSystem에서 가져온 Area는 플레이어가 타겟팅을 하고 있는 상태가 아닐 경우 캐릭터의 전방으로 일정범위의 Trace를 실행합니다.
Trace에 Hit되는 액터의 위치로 Area를 이동시키고 활성화합니다. 활성화한 Area는 주변의 액터들을 자신의 위치로 끌어당기고 대미지를 줍니다.
플레이어가 타겟팅을 하고 있는 상태일 경우 타겟팅을 하고 있는 Target의 위치에 Area를 이동시키고 활성화합니다.
// JudgementArea.cpp
void APRJudgementCutArea::InitializeSpawnLocation_Implementation()
{
NearestTarget = nullptr;
if(IsValid(GetObjectOwner()) == true)
{
APRPlayerCharacter* PRPlayerCharacter = Cast<APRPlayerCharacter>(GetObjectOwner());
if(PRPlayerCharacter != nullptr && PRPlayerCharacter->GetTargetingSystem() != nullptr)
{
UPRTargetingSystemComponent* TargetSystem = PRPlayerCharacter->GetTargetingSystem();
if(TargetSystem->IsActivateLockOnTarget() == true && IsValid(TargetSystem->GetLockedOnTarget()) == true)
{
// Target을 LockOn하고 있을 때
SetActorLocation(FindFloorLocation(TargetSystem->GetLockedOnTarget()));
return;
}
// Target을 LockOn하고 있지 않을 때
TArray<FHitResult> HitResults;
bool bIsHit = false;
// 오브젝트 소유자를 Trace 대상에서 제외합니다.
TArray<AActor*> ActorsToIgnore;
ActorsToIgnore.Emplace(GetObjectOwner());
// Debug 실행을 설정합니다.
EDrawDebugTrace::Type DebugType = EDrawDebugTrace::None;
if(bActivateDebug)
{
DebugType = EDrawDebugTrace::ForDuration;
}
const FVector TraceStart = PRPlayerCharacter->GetActorLocation();
const FVector TraceEnd = TraceStart + (PRPlayerCharacter->GetActorForwardVector() * TargetSystem->GetMaxSearchTargetalbeDistance());
// ECC_GameTraceChannel3는 TraceChannels의 PlayerAttack을 나타냅니다.
bIsHit = UKismetSystemLibrary::SphereTraceMulti(GetWorld(), TraceStart, TraceEnd, SpawnRadius, UEngineTypes::ConvertToTraceType(ECC_GameTraceChannel3),
false, ActorsToIgnore, DebugType, HitResults, true);
if(bIsHit)
{
// 가장 가까운 Target을 탐색합니다.
for(FHitResult Hit : HitResults)
{
if(Hit.Actor.IsValid() == true)
{
if(NearestTarget != nullptr)
{
AActor* NewNearestTarget = GetNearestTarget(NearestTarget, Hit.GetActor());
if(NewNearestTarget != nullptr)
{
NearestTarget = NewNearestTarget;
}
}
else
{
NearestTarget = Hit.GetActor();
}
}
}
// 가장 가까운 Target의 바닥으로 위치를 옮깁니다.
if(NearestTarget != nullptr)
{
SetActorLocation(FindFloorLocation(NearestTarget));
return;
}
}
if(PRPlayerCharacter->GetMovementSystem()->IsEqualMovementState(EPRMovementState::MovementState_InAir) == true)
{
// PlayerCharacter가 공중에 존재할 경우
SetActorLocation(PRPlayerCharacter->GetActorLocation() + GetObjectOwner()->GetActorForwardVector() * DefaultSpawnDistance);
}
else
{
// PlayerCharacter가 공중에 존재하지 않을 경우
SetActorLocation(FindFloorLocation(GetObjectOwner()) + (GetObjectOwner()->GetActorForwardVector() * DefaultSpawnDistance));
}
}
}
}
void APRJudgementCutArea::Activate_Implementation()
{
Super::Activate_Implementation();
CooldownElapsed = 0.0f;
DamageCount = 0;
JudgementCutEffect->Activate(true);
bActivateDamageArea = true;
}
// PRPooledObject.cpp
void APRPooledObject::Activate_Implementation()
{
bActivate = true;
InitializeSpawnLocation();
SetActorHiddenInGame(!bActivate);
// 오브젝트의 수명이 끝나면 오브젝트를 비활성화합니다.
GetWorldTimerManager().SetTimer(LifespanTimerHandle, this, &APRPooledObject::Deactivate_Implementation, Lifespan, false);
}
'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] 검기 스킬 Slash Projectile Skill (1) | 2023.10.16 |