기록

240426~240503 파트너 캐릭터 위치를 나타내는 UI (3)

hayo_su 2024. 5. 4. 16:33

목표 : 다른 캐릭터(파트너)위치를 나타내는 UI를 만들기 

  1. 위젯 만들기 (완료)
  2. 플레이어에 위젯 컴포넌트로 붙이기 (완료)
  3. 거리에 따라 위젯 on/off기능 (완료)
  4. 화면 랜더링 여부에 따라 on/off기능 + 랜더링이 되지 않을 때 화면에서 방향을 가르키도록 하기(완료!)

4. 화면 랜더링 여부에 따라 on/off기능 + 랜더링이 되지 않을 때 화면에서 방향을 가르키도록 하기

고민됐던 점

1. 화면 랜더링 여부 확인하기(완료)

2. 랜더링 되지 않았을 때 화면에 어떻게 표현할 것인지.(진행)


이전에 진행상황을 확인해보면

 

240426~240503 파트너 캐릭터 위치를 나타내는 UI (2)

목표 : 다른 캐릭터(파트너)위치를 나타내는 UI를 만들기 위젯 만들기 (완료)플레이어에 위젯 컴포넌트로 붙이기 (완료)거리에 따라 위젯 on/off기능 (완료)화면 랜더링 여부에 따라 on/off기능 +

hayo-su.tistory.com

변경 전)

- 본인의 위젯 컴포넌트는 본인이 볼 수 없게 설정해 두었고,

- 위젯 컴포넌트에서 본인이 아닌 플레이어를 찾고, 이 플레이어에서 (본인)위젯의 랜더링 여부를 확인하였다.

 

변경 후)

1. 위치에 따라 랜더링 여부를 확인할 것

2. 뷰포트 내에 존재 여부에 따라 위치를 조정할 것

다음을 만족하게 만들기 위해서 반대로 본인의 위젯 ->파트너 위치 나타내기가 더 쉬울 것이라고 생각하였다.(2인 플레이 게임이라 가능한 부분이다.)


진행

    // Set widget classes
    if (PlayerMarkerWidget)
    {
        PlayerMarkerWidget->SetWidgetClass(PlayerMarkerType);
    }
    // Set widget space to screen
    PlayerMarkerWidget->SetWidgetSpace(EWidgetSpace::Screen);
    
    // Check if the owner of the widget component is the same as the player controller
    if(Cast<APawn>(GetOwner()) == GetWorld()->GetFirstPlayerController()->GetPawn())
    {
    	// 본인인 경우 랜더링
        SetVisibility(true, true);
    }
    else
    {
    	//타인인 경우 랜더링하지 않음
        SetVisibility(false, true);
    }

 

 

1. 본인이 아닌 위젯은 visible false설정을 하였다.

 

// .h
// 위젯을 통해 위치를 표시할 타겟 플레이어 정보
APlayerBase* TargetPlayer;
// .cpp

void UPlayerMarkerUI::FindTarget()
{
    TArray<AActor*> AllPlayerActors;

    UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerBase::StaticClass(), AllPlayerActors);

    APlayerBase* me = Cast<APlayerBase>(GetOwner());
    // PlayerActors를 순회하며 각 플레이어의 처리 수행
    for (AActor* Player : AllPlayerActors)
    {
        if (me != Cast<APlayerBase>(Player))
        {
            TargetPlayer = Cast<APlayerBase>(Player);
            return;
        }
        // Player를 사용하여 플레이어 처리
    }
}
void UPlayerMarkerUI::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	if (TargetPlayer == nullptr)
	{
    		FindTarget();
    		return;
	}
}

 

2. 타겟 플레이어가 설정되지 않는경우 본인이 아닌 다른 플레이어가 target에 설정될 수 있도록 설정

 

APlayerController* FirstPlayer = GetWorld()->GetFirstPlayerController();

FVector2D ActorScreenPosition; // 타겟 플레이어의 스크린 포지션 위치를 연산
if (!FirstPlayer->ProjectWorldLocationToScreen(TargetPlayer->GetActorLocation(), ActorScreenPosition))
{
    return; // Failed to project actor's location to screen space
}

int32 ViewportX, ViewportY; // 뷰포트 가져오기
FirstPlayer->GetViewportSize(ViewportX, ViewportY);

// 화면에 랜더링 되는 경우
if ((ActorScreenPosition.X >= 0 && ActorScreenPosition.X <= ViewportX &&
    ActorScreenPosition.Y >= 0 && ActorScreenPosition.Y <= ViewportY))
{
}
else // 화면에 랜더링이 되지 않는경우
{
}

 

3. 타겟플레이어의 랜더링 여부를 확인함.

 

float Distance = FVector::Distance(FirstPlayer->GetPawn()->GetActorLocation(), TargetPlayer->GetActorLocation());

if (DistanceThreshold < Distance)
{
    SetVisibility(true, true);
}
else
{
    SetVisibility(false, true);
}
SetWorldLocation(TargetPlayer->GetActorLocation()+ WidgetDefaultLocation);

 

4. 화면에 존재하는 경우 거리에 따라 랜더링 여부를 결정하고, 타겟 플레이어 위치로 위젯 위치를 설정함.

 

SetVisibility(true, true);
if (ActorScreenPosition.X < 0)
{
    ActorScreenPosition.X = 10.0f;
}
else if (ActorScreenPosition.X > ViewportX)
{
    ActorScreenPosition.X = ViewportX - 10.0f;
}

if (ActorScreenPosition.Y < 0)
{
    ActorScreenPosition.Y = 10.0f;
}
else if (ActorScreenPosition.Y > ViewportY)
{
    ActorScreenPosition.Y = ViewportY - 10.0f;
}

 

5. 화면 밖에 존재하는 경우 랜더링하도록 설정하고 액터의 스크린 포지션을 화면 내 위치로 보정한다.

 

FVector WorldLocation, WorldDirection;
FirstPlayer->DeprojectScreenPositionToWorld(ActorScreenPosition.X, ActorScreenPosition.Y, WorldLocation, WorldDirection);

// 카메라 스페이스 좌표로 변환한 후, 카메라와 WidgetComponent의 거리 계산
FVector CameraLocation = FirstPlayer->PlayerCameraManager->GetCameraLocation();
FVector WidgetLocation = CameraLocation + WorldDirection * 1200.0f; // 예시로 거리를 100.0f로 설정
SetWorldLocation(WidgetLocation + WidgetDefaultLocation);

 

6. 보정한 스크린좌표계 값을 바탕으로 월드 좌표계값을 가져온 후 , 카메라의 스프링암이 1200.0f길이를 가지고 있기 때문에 해당 정보를 반영

 

 

 

추가 변경 예정 ) 화면 밖에 존재하는 경우 위젯의 회전이 적용될 수 있도록 변경 예정이다.

플레이어의 카메라, 뷰포트, 프로젝션 등 3D공간에서 2D공간으로 랜더링 되는 순서와 반대로 2D에서 3D공간에 적용하는 방법에 대해 오랫동안 고민해 보았다.