이번 추석 기간 동안 (대략 5일간) 부채꼴 공격을 구현했는데!! 이것 저것 붙이다보니까 더 오래 시간을 썼다..^_^.. 하지만 그래도 시간활용해서 싱글플레이 하나 만든건 괜찮을지듀? 앞으로 취준 기간동안 몇 개의 더 많은 프로젝트를 진행해야할진..모르지만! 그래도 도전합니다!
이 부채꼴 공격을 구현해보자! 라는 마음을 먹게 된 이유는 부채꼴 공격에 대한 코딩테스트 문제를 본적이 있어서 로직을 제대로 이해할려면 응용이 답이다! 생각해서 UNSEEN 2기 몇 분과 함께 진행해봤습니다!
계획적으로 아래와 같은 목표를 정했습니다!
1. DebugDraw로 부채꼴 표현하기 (14일까지)
2. 만들어 놓은 Decal Material Instance에 정해진 세타값을 사용해서 부채꼴 표현 (15일까지)
3. 플레이어 애니메이션 작업, Attack 및 NPC AI 만들기 (16일 ~ 19일 까지)
4. 간단한 NPC AI 패턴 만들어서 배포
void USPSkillComponent::DrawDebugCircleArcFanWithDirection(UWorld* World, FVector Center, FVector Direction, float Radius, float StartAngle, float EndAngle, int32 Segments, FColor Color, float Thickness, bool bPersistentLines, float LifeTime)
{
FVector Forward = Direction.GetSafeNormal();
// Calculate right vector for generating fan points around the direction
FVector Right = FVector::CrossProduct(Forward, FVector::UpVector);
FVector PreviousPoint = Center;
float AngleStep = (EndAngle - StartAngle) / Segments; //원을 표현하기 위해서 얼마나 그려야하는지 표현
float Angle = FMath::DegreesToRadians(StartAngle); //시작 앵글 값을 라디안으로 바꾸고
FVector RotatedDirection = Forward * FMath::Cos(Angle) + Right * FMath::Sin(Angle); //시작 위치를 가져옴
FVector CurrentPoint = Center + RotatedDirection * Radius; //현재 시작점에서 얼마나 이동해야하는가를 표현
//시작점과 현재 Center 연결
DrawDebugLine(World, PreviousPoint, CurrentPoint, Color, bPersistentLines, LifeTime, 0, Thickness);
// Loop through each segment to draw the arc
for (int32 i = 0; i <= Segments; i++)
{
// Calculate current angle in radians, 원형 그리기
Angle = FMath::DegreesToRadians(StartAngle + i * AngleStep);
// Calculate the point on the arc based on direction and right vector
RotatedDirection = Forward * FMath::Cos(Angle) + Right * FMath::Sin(Angle);
CurrentPoint = Center + RotatedDirection * Radius;
// 이전 지점을 기준으로 원형 그리기
if (i > 0)
{
DrawDebugLine(World, PreviousPoint, CurrentPoint, Color, bPersistentLines, LifeTime, 0, Thickness);
}
PreviousPoint = CurrentPoint;
}
//마지막에 도달한 Point 지점과 현재 Center지점을 연결
DrawDebugLine(World, Center, PreviousPoint, Color, bPersistentLines, LifeTime, 0, Thickness);
}

위 코드를 통해서 이와 같은 결과를 얻을 수 있다!
void USPSkillComponent::CheckAttackCollision()
{
//Overlap
ASPCharacterBase* Character = GetPawn<ASPCharacterBase>();
if (Character)
{
TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypes;
TArray<AActor*> IgnoreActors;
TArray<AActor*> OutActors;
ObjectTypes.Add(UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_Pawn));
IgnoreActors.Add(Character);
FVector Location = Character->GetActorLocation();
float Damage = Character->GetStat()->GetAttack();
bool Result = UKismetSystemLibrary::SphereOverlapActors(GetWorld(), Location, 100.f, ObjectTypes, nullptr, IgnoreActors, OutActors);
if (Result)
{
for (AActor* Actor : OutActors)
{
//충돌 체크
if (IsHitByAttack(Character, Actor))
{
//데미지를 입힌다.
FDamageEvent DamageEvent;
Actor->TakeDamage(Damage, DamageEvent, Character->GetController(), Character);
}
}
}
}
}
bool USPSkillComponent::IsHitByAttack(AActor* CurrentActor, AActor* OtherActor)
{
FVector Origin = CurrentActor->GetActorLocation();
FVector Dest = OtherActor->GetActorLocation();
Origin.Z = 0;
Dest.Z = 0;
FVector ForwardVector = CurrentActor->GetActorForwardVector(); // 현재 Actor의 바라보는 방향
FVector ToTarget = (Dest - Origin).GetSafeNormal(); // 타겟 방향 정규화
float DotResult = FVector::DotProduct(ForwardVector, ToTarget); //내적
float Radian = FMath::Acos(DotResult); //내적에 대해서 Acos을 해주면 라디안 값 나옴
// 라디안을 각도로 변환
float Angle = FMath::RadiansToDegrees(Radian); //라디안을 Degree로 바꿔주면
bool IsHit = MinDegree <= Angle && Angle <= MaxDegree;
return IsHit; //해당 값이 사이에 들어가는지 히트 판정을 수행한다
}


실제 더 자세한 코드를 확인하기 위해서는 아래 깃허브를 보면 됩니다!
https://github.com/YOLO-Game-Developer/ProjectSP
(SP약자: Side Project)