ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 240426~240503 파트너 캐릭터 위치를 나타내는 UI (3)
    기록 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공간에 적용하는 방법에 대해 오랫동안 고민해 보았다.

    댓글

Designed by Tistory.