728x90
반응형
FPS 게임 같은 것을 보면, 플레이어 화면에 A사이트와 B사이트 같은 목표 지점이 있다.
(ex. 폭파 지점, 점령 거점, 아군/적 진영 등등)
이는 다음과 같이 구현한다.
- 위젯을 그린다.
- 위젯 변수 추가하고 CreateWidget, AddToViewport를 통한 전형적인 위젯 그리기
- 월드의 특정 오브젝트의 Vector3D를 기준으로, 그려진 위젯이 위치할 Vector2D 위치를 구한다.
- UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition() 함수 사용
- 구한 Vector2D를 위젯에 업데이트한다.
- UUserWidget::SetPositionInViewport() 함수 사용
- 이를 매 틱마다 업데이트한다.
// .h
private:
// 위젯 그리는 함수
void DrawWidget();
// 위젯 위치 설정 함수
void SetWidgetPosition();
// 위젯 클래스
UPROPERTY(EditDefaultsOnly, Category = "Widget")
TSubclassOf<class UUserWidget> WidgetClass;
// 위젯
UPROPERTY()
TObjectPtr<class UUserWidget> Widget;
// 대상 오브젝트 (액터나 건물 등등)
UPROPERTY()
AActor* TargetActor;
UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition()
- "Blueprint/WidgetLayoutLibrary.h" 헤더 추가
- 위젯 소유자
- 대상 위치
- 참조 저장할 위젯의 위치 (FVector2D)
- 위젯 소유자의 뷰포트 제약 여부
- 이를 true로 하면, 화면을 벗어나지 않도록 고려하여 계산
// .cpp
#include "Blueprint/UserWidget.h"
#include "Blueprint/WidgetLayoutLibrary.h"
void ATest::DrawWidget()
{
// 중복 그리기 방지
if (Widget) return;
// CreateWidget 함수를 통해 플레이어 컨트롤러에 위젯을 만들고 이를 저장
check(WidgetClass);
Widget = CreateWidget<UUserWidget>(GetController(), WidgetClass);
// 저장된 위젯을 AddToViewport 함수를 통해 화면에 그림
check(Widget);
Widget->AddToViewport();
}
void ATest::SetWidgetPosition()
{
if (!Widget || !TargetActor) { return; }
// 위젯의 뷰포트 위치
FVector2D ScreenPosition;
// 월드 객체의 위치를 기반으로 위젯의 뷰포트 위치를 계산
UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(
GetController(),
TargetActor->GetActorLocation(),
ScreenPosition,
true
);
// 위젯의 뷰포트 위치를 설정
// 위에서 이미 플레이어의 뷰포트를 기준으로 계산해서 나온 값이기 때문에, false
Widget->SetPositionInViewport(ScreenPosition, false);
}
여기서, 추가로 설정해줘야 하는 사항이 있다.
위 구현은 위젯의 크기를 고려하지 않는 구현이다.
위젯의 SizeBox를 그린다면, SizeBox의 재정의된 크기도 포함하여 계산해야 한다.
// .h
private:
// 위젯 크기
UPROPERTY()
FVector2D WidgetSize = FVector2D::ZeroVector;
위젯에서 내부 패널의 SizeBox에 접근하여 재정의된 크기를 반영해야 한다.
타깃 고정 위젯에서 WidgetTree 변수를 통해 위젯 내부의 패널들에 접근할 수 있다.
WidgetTree 변수의 ForEachWidget 함수를 통해 접근한 패널들을 순회하며 함수를 적용할 수 있다.
람다 함수를 만들어 SizeBox에 접근하여 재정의된 크기를 변수에 저장해 보자.
- 재정의된 크기를 앞서 선언한 변수에 저장해야 하기 때문에, 캡처에 this를 넣는다.
- 순회하는 인자는 UWidget*로 받는다.
- 인자로 받은 UWidget*을 USizeBox*로 캐스팅한다.
- USizeBox의 GetWidthOverride 함수를 통해 SizeBox의 재정의된 너비를 저장한다.
- USizeBox의 GetHeightOverride 함수를 통해 SizeBox의 재정의된 높이를 저장한다.
월드 액터의 위치 기반으로 구한 위젯의 뷰포트 위치에서, 재정의된 위젯의 크기 /2만큼을 뺀다.
// .cpp
#include "Blueprint/WidgetTree.h"
#include "Components/SizeBox.h"
void ATest::SetWidgetPosition()
{
// 위젯 크기 재정의
if (WidgetSize == FVector2D::ZeroVector)
{
Widget->WidgetTree->ForEachWidget(
[this](UWidget* FoundWidget)
{
if (USizeBox* FoundSizBox = Cast<USizeBox>(FoundWidget))
{
WidgetSize.X = FoundSizBox->GetWidthOverride();
WidgetSize.Y = FoundSizBox->GetHeightOverride();
}
}
);
}
FVector2D ScreenPosition;
UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(
GetHeroControllerFromActorInfo(),
CurrentLockedActor->GetActorLocation(),
ScreenPosition,
true
);
// 변경된 크기 / 2만큼 위젯의 뷰포트 위치에서 뺀다.
ScreenPosition -= (TargetLockWidgetSize / 2.f);
DrawnTargetLockWidget->SetPositionInViewport(ScreenPosition, false);
}
728x90
반응형
'📘Unreal Engine > 📝Unreal Engine' 카테고리의 다른 글
| [Unreal Engine] 언리얼 스마트 포인터 (0) | 2024.11.22 |
|---|---|
| [Unreal engine] ExposeOnSpawn 메타데이터 지정자 (0) | 2024.11.19 |
| [Unreal Engine] 가장 근접한 대상 찾기 (0) | 2024.11.11 |
| [Unreal Engine] 비헤이비어 트리(Behavior Tree) 구조 (0) | 2024.08.19 |
| [Unreal Engine] 충돌 판정 / 트레이싱 (Tracing) (0) | 2024.08.09 |