[UE] 추석동안 부채꼴 공격 구현하기

suyoung·2024년 9월 20일

UE5

목록 보기
8/12

이번 추석 기간 동안 (대략 5일간) 부채꼴 공격을 구현했는데!! 이것 저것 붙이다보니까 더 오래 시간을 썼다..^_^.. 하지만 그래도 시간활용해서 싱글플레이 하나 만든건 괜찮을지듀? 앞으로 취준 기간동안 몇 개의 더 많은 프로젝트를 진행해야할진..모르지만! 그래도 도전합니다!

이 부채꼴 공격을 구현해보자! 라는 마음을 먹게 된 이유는 부채꼴 공격에 대한 코딩테스트 문제를 본적이 있어서 로직을 제대로 이해할려면 응용이 답이다! 생각해서 UNSEEN 2기 몇 분과 함께 진행해봤습니다!

계획적으로 아래와 같은 목표를 정했습니다!
1. DebugDraw로 부채꼴 표현하기 (14일까지)
2. 만들어 놓은 Decal Material Instance에 정해진 세타값을 사용해서 부채꼴 표현 (15일까지)
3. 플레이어 애니메이션 작업, AttackNPC AI 만들기 (16일 ~ 19일 까지)
4. 간단한 NPC AI 패턴 만들어서 배포

  1. DebugDraw로 부채꼴 표현하기
    실제로, 언리얼 엔진에서 원뿔이 아닌 실제 2D용 부채꼴을 제공하지 않는데 그래서 실제로 만들었다!
  • 외적을 통해서 오른쪽을 알아내고, Cos,Sin을 통해 원형 모형을 그리고 직선까지 표현
	
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);
}


위 코드를 통해서 이와 같은 결과를 얻을 수 있다!

  1. 부채꼴 Attack 구현
    • 실제 오버랩을 사용해서 모든 AI 들을 가져왔다. (Distance 거리 측정)
    • 부채꼴에 해당하는 공격을 구현하기 위해서는 내적을 사용한다.
    • 내적을 사용하게 되면 cos theta 값을 알 수 있는데, acos을 통해서 theta 값만 분리가능하다.
    • 정규화를 하면, 각 벡터의 크기를 알 필요 없기 때문에 좋다.
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; //해당 값이 사이에 들어가는지 히트 판정을 수행한다
}


실제 더 자세한 코드를 확인하기 위해서는 아래 깃허브를 보면 됩니다!

  • AI나 레벨디자인 부분은 BT, 각 꾸미는 것이라 코드가 별로 없음으로 생략합니다!

https://github.com/YOLO-Game-Developer/ProjectSP
(SP약자: Side Project)

profile
게임 클라이언트 프로그래머

0개의 댓글