쾌락없는 책임 (공부)/Unreal

[Unreal] 액터가 volume 안에 있는지 확인하기

허스크 2024. 4. 20. 10:16
반응형

개요

마을의 '영역'을 만들게 되면서 해당 영역에 Enter 하게 되면 플레이어가 해당 영역에 있다는걸 나타낼 예정입니다. 그런데 문제는 플레이어가 마을에서 스폰하게 되는 경우입니다. 이 경우에는 BeginPlay 시점에 Overlap 이벤트가 자동적으로 불리게 되지 않아 문제가 됩니다.

 

이를 해결하기 위해 BeginPlay 시점에 특정 액터가 안에 있음을 알게 하는 방법 2개를 알아보도록 하겠습니다.

 

 

bGenerateOverlapEventsDuringLevelStreaming

위 변수 딸깍이면 레벨이 로드되는 중에도 Overlap 이벤트를 발생시킬 수 있게 됩니다.

 

에디터에서 위 옵션을 체크하거나 생성자에서 true 로 설정해주면 됩니다.

 

void UPrimitiveComponent::BeginComponentOverlap(const FOverlapInfo& OtherOverlap, bool bDoNotifies)
{
	// ...
	const bool bComponentsAlreadyTouching = (IndexOfOverlapFast(OverlappingComponents, OtherOverlap) != INDEX_NONE);
	if (!bComponentsAlreadyTouching)
	{
		UPrimitiveComponent* OtherComp = OtherOverlap.OverlapInfo.Component.Get();
		if (CanComponentsGenerateOverlap(this, OtherComp))
		{
			//....
			// 여기서 해당 옵션이 켜져 있는지를 검사
			const bool bLevelStreamingOverlap = (bDoNotifies && MyActor->bGenerateOverlapEventsDuringLevelStreaming && MyActor->IsActorBeginningPlayFromLevelStreaming());
			if (bDoNotifies && ((World && World->HasBegunPlay()) || bLevelStreamingOverlap))
			{
				// first execute component delegates
				if (IsValid(this))
				{
                	// 이곳에서 이벤트를 부르게 됩니다.
					OnComponentBeginOverlap.Broadcast(this, OtherActor, OtherComp, OtherOverlap.GetBodyIndex(), OtherOverlap.bFromSweep, OtherOverlap.OverlapInfo);
				}

 

위 변수의 사용처는 PrimitiveComponent 의 BeginComponentOverlap입니다.(End 역시 동일)

 

해당 옵션이 켜져 있다면 HasBegunPlay 상태가 아니라 하더라도 이벤트를 호출하게 되는것을 알 수 있습니다. 이러면 추가적인 로직 없이 변수 딸깍으로 설정할 수 있게 되는 것입니다.

 

 

AVolume::EncompassesPoint

만일 Overlap 이벤트가 너무 많은 비용을 차지할 것 같다면 해당 함수를 고려해볼 수 있습니다. 이 함수는 특정 3D 좌표의 점이 Volume 안에 있는지를 파악해주는 검사로

if(PlayerPawn != nullptr && EncompassesPoint(PlayerPawn->GetActorLocation()))
{
    UE_LOG(LogTemp, Warning, TEXT("*** *** *** overlap player encompasses"));
}

 

위와 같은 방식으로 사용하면 됩니다.

 

bool AVolume::EncompassesPoint(FVector Point, float SphereRadius/*=0.f*/, float* OutDistanceToPoint) const
{
	if (GetBrushComponent())
	{
#if 1
		FVector ClosestPoint;
		float DistanceSqr;

		if (GetBrushComponent()->GetSquaredDistanceToCollision(Point, DistanceSqr, ClosestPoint) == false)
		{
			if (OutDistanceToPoint)
			{
				*OutDistanceToPoint = -1.f;
			}
			return false;
		}
#else
		FBoxSphereBounds Bounds = GetBrushComponent()->CalcBounds(GetBrushComponent()->GetComponentTransform());
		const float DistanceSqr = Bounds.GetBox().ComputeSquaredDistanceToPoint(Point);
#endif

		if (OutDistanceToPoint)
		{
			*OutDistanceToPoint = FMath::Sqrt(DistanceSqr);
		}

		return DistanceSqr >= 0.f && DistanceSqr <= FMath::Square(SphereRadius);
	}
	else
	{
		UE_LOG(LogVolume, Log, TEXT("AVolume::EncompassesPoint : No BrushComponent"));
		return false;
	}
}

 

위 함수의 구현을 보면 BrushComponent를 기반으로 동작하는걸 알 수 있습니다.

 

  • GetSquaredDistanceToCollision 에서 가장 가까운 거리를 구함
    • 만일 return 이 false 라면 가장 가까운 거리가 발견되지 않은 것이라 return
  • OutDistanceToPoint로 거리 계산
  • 이후 검사 결과와 거리 안에 들어와 있는지를 검사

 

이를 통해 따로 Overlap 이벤트를 기대하지 않아도 검사가 가능하게 됩니다.

반응형