240426~240503 파트너 캐릭터 위치를 나타내는 UI (3)
목표 : 다른 캐릭터(파트너)위치를 나타내는 UI를 만들기
- 위젯 만들기 (완료)
- 플레이어에 위젯 컴포넌트로 붙이기 (완료)
- 거리에 따라 위젯 on/off기능 (완료)
- 화면 랜더링 여부에 따라 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공간에 적용하는 방법에 대해 오랫동안 고민해 보았다.