[Unreal_C++_DarkSoul]#6_Targeting 기능
Player 근방의 적들을 인지하고, 선택된 적 객체를 Focusing 하는 기능을 구현합니다.
포트폴리오 진행 사항을 기록하기 위한 포스팅입니다.
Overview
- 먼저, Player 근방의 적 객체들을 인식합니다.
- 인식된 적 객체들 중 가장 근거리에 위치한 적 객체를 타게팅합니다.
- 타게팅 상태에서 "휠"을 올려 현재 타깃이 된 적 객체를 기준으로 시계 방향으로 타게팅 객체를 변경합니다.
- 더불어, "휠"을 내리면 반 시계 방향으로 타게팅 객체를 변경할 수 있습니다.
* Blueprint로 개발한 과정과 세부 내용들은 같습니다. 아래 링크들을 참조하세요
코드
1. FindNearbyActors()
/*****************************************************************************************
목적:
Enemy Detection Collider 범위 안에 존재하는 Nearby Enemy들을 찾아냅니다.
설명:
1. Nearby Actor가 1명이라도 존재하면, FindTargetEnemy() 함수가 호출됩니다.
******************************************************************************************/
void APlayerCharacter::FindNearbyActors()
{
TArray<TEnumAsByte<EObjectTypeQuery>> NearbyActors;
TEnumAsByte<EObjectTypeQuery> PawnObjectType = UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_Pawn);
NearbyActors.Add(PawnObjectType);
TArray<AActor*> IgnoreActors;
IgnoreActors.Add(this);
TArray<FHitResult> HitResults;
// Trace Multi For Objects
bool Result = UKismetSystemLibrary::SphereTraceMultiForObjects(
GetWorld(),
GetActorLocation(),
GetActorLocation() + 10.f,
EnemyDetectionMaxRadius,
NearbyActors,
false,
IgnoreActors,
EDrawDebugTrace::None,
HitResults,
true
);
// When Enemy Detecetd!
if (Result == true)
{
// TODO : Decide Targeting Enemy
for (const auto& Hit : HitResults)
{
if (Cast<AEnemyBase>(Hit.GetActor()) != nullptr)
{
NearbyEnemies.AddUnique(Cast<AEnemyBase>(Hit.GetActor()));
}
}
FindTargetEnemy();
}
}
Details
- UKismetSystemLibrary에서 제공하는 Sphere Trace를 활용합니다.
- Sphere Trace에서 Hit이 발생하면 Hit이 발생한 적 객체들을 기억하고, FindTargetEnemy()를 호출합니다!
- SphereTrace 함수 위로 작성한 코드들은 Sphere Trace를 위해 필요한 인자들을 준비하는 과정이므로, 생략.
2. FindTargetEnemy()
/*****************************************************************************************
목적:
Enemy Detection Collider에 Bouding된 주변 적들과 Player 간의 상대적인 거리 계산합니다.
최종적으로, Min Distance(최소 거리)을 갖는 Enemy Actor가 Target Enemy로 선정됩니다.
설명:
1. A = (Player위치 - Enemy위치) X (Player의 Forward Vector)
2. Min Distance = Fabs( (Player의 Up Vector) * (A) )
3. Enemy & Min Distance 조합으로 Map을 생성합니다!
******************************************************************************************/
void APlayerCharacter::FindTargetEnemy()
{
float MinDistance = 100000.f;
FVector TempCrossProduct = FVector::ZeroVector;
float TempDotProduct = 0.f;
int32 TargetEnemyIndex = -1;
for (int i=0; i<NearbyEnemies.Num(); i++)
{
// Cross + Dot
TempCrossProduct
= FVector::CrossProduct(GetActorForwardVector(), NearbyEnemies[i]->GetActorLocation() - GetActorLocation());
TempDotProduct
= FVector::DotProduct(GetActorUpVector(), TempCrossProduct);
// Map
MBoundEnemies.Add(NearbyEnemies[i], TempDotProduct);
// 최소 값 찾기
float Distance = fabs(TempDotProduct);
if (Distance < MinDistance)
{
MinDistance = Distance;
TargetEnemyIndex = i;
}
}
if (TargetEnemyIndex != -1)
{
TargetEnemy = NearbyEnemies[TargetEnemyIndex];
CheckNull(TargetEnemy);
}
}
Details
- 미리 저장한 근방 적 객체들과 Player간의 거리를 계산합니다. 계산 방법은 위 링크들을 살펴보세요
- 각 객체를 "Key"로, Player 까지의 거리를 "Value"로 Pair를 생성하고, Map 컨테이너에 삽입합니다.
- 결과적으로, 최단 거리에 존재하는 적 객체를 "Target Enemy"로 설정하고 마칩니다.
3. CycleClockwise()
/*****************************************************************************************
목적:
Enemy Detectio Collider 내부에 위치한 Nearby Actor를 "시계 방향"으로 순회합니다.
설명:
1. 현재 Target Enemy로 설정된 Actor는 제외합니다.
2. 현재 Target Enemy의 Key 값보다 큰 거리 값 중 가장 작은 수를 찾습니다.
******************************************************************************************/
void APlayerCharacter::CycleClockwise()
{
if (TargetingLockOn == true)
{
// TODO : 양수 중 최소 값 찾기
float MinDistance = 100000.f;
AEnemyBase* TmpEnemy = TargetEnemy;
for (const auto& pair : MBoundEnemies)
{
// #1. Target Enemy 제외
if (pair.Key == TargetEnemy) continue;
// #2. 현 Target Enemy 거리보다 큰 거리 값 중 최소
if (pair.Value > *MBoundEnemies.Find(TargetEnemy)
&& pair.Value < MinDistance)
{
MinDistance = pair.Value;
TmpEnemy = pair.Key;
}
}
CheckNull(TmpEnemy);
TargetEnemy = TmpEnemy;
}
}
Details
- 미리 준비한 Map 컨테이너를 통해 다음으로 짧은 거리를 갖는 적 객체를 탐색합니다.
- 이때, 시계 방향으로 회전하기 위해서 "음수" 값을 갖는, 즉 반 시계 방향에 위치한 적 객체들은 무시합니다.
- 결과적으로, "양수" 거리 값을 갖는 적 객체들 중 현 타깃 다음으로 가까운 적을 타깃으로 설정합니다.
3. CycleAntiClockwise()
/*****************************************************************************************
목적:
Enemy Detectio Collider 내부에 위치한 Nearby Actor를 "반 시계 방향"으로 순회합니다.
설명:
1. 현재 Target Enemy로 설정된 Actor는 제외합니다.
2. 현재 Target Enemy의 Key 값보다 작은 거리 값 중 가장 큰 수를 찾습니다.
******************************************************************************************/
void APlayerCharacter::CycleAntiClockwise()
{
if (TargetingLockOn == true)
{
// TODO : 음수 중 최대 값 찾기
float MaxDistance = -99999.f;
AEnemyBase* TmpEnemy = TargetEnemy;
for (const auto& pair : MBoundEnemies)
{
// #1. Target Enemy 제외
if (pair.Key == TargetEnemy) continue;
// #2. 현 Target Enemy 거리보다 작은 거리 값 중 최대 값
if (pair.Value < *MBoundEnemies.Find(TargetEnemy)
&& pair.Value > MaxDistance)
{
MaxDistance = pair.Value;
TmpEnemy = pair.Key;
}
}
CheckNull(TmpEnemy);
TargetEnemy = TmpEnemy;
}
}
Details
- 위와 동일합니다.
3. FocusOnTarget()
/* ****************************************************************************
* 목적:
* Target Lock이 켜져 있을 때, Target Enemy로 선정된 Actor를 바라보도록 합니다.
* 설명:
* 1. Player Location과 Enemy Location을 활용하여 LookAtRotation 값을 구합니다.
* 2. Controller Rotation을 위에서 구한 회전 값으로 설정합니다.
*
* ****************************************************************************/
void APlayerCharacter::FocusOnTarget()
{
CheckFalse(TargetingLockOn);
CheckNull(TargetEnemy);
// #1. FindLookAtRotation
APlayerCameraManager* camManager = GetWorld()->GetFirstPlayerController()->PlayerCameraManager;
FVector Start = camManager->GetCameraLocation();
FVector Target = TargetEnemy->GetActorLocation();
FRotator Rotation = UKismetMathLibrary::FindLookAtRotation(Start, Target);
GetController()->SetControlRotation(FRotator(0.f, Rotation.Yaw, 0.f));
}
Details
- 최종적으로, Target Enemy로 선정된 적 객체를 바라보도록, Player의 Rotation 값을 세팅해줍니다.
결과 화면
'개인프로젝트' 카테고리의 다른 글
[Unreal_C++_DarkSoul]#8_문제 해결, Send Damage 방식 수정 (0) | 2022.12.18 |
---|---|
[Unreal_C++_DarkSoul]#7_문제 해결, Magic Projectile (0) | 2022.12.17 |
[Unreal_C++_DarkSoul]#5_기능 구현, Target Point, Enemy Spawn 위치 (0) | 2022.12.10 |
[Unreal_C++_DarkSoul]#4_문제 해결, Data Table 로드 함수 (0) | 2022.11.27 |
[Unreal_C++_DarkSoul]#3_기능 구현, Custom Structure, Custom Enumeration (0) | 2022.11.23 |