ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unreal] 액터가 volume 안에 있는지 확인하기
    쾌락없는 책임 (공부)/Unreal 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 이벤트를 기대하지 않아도 검사가 가능하게 됩니다.

    반응형

    댓글

Designed by Tistory.