액터의 Bounding Box를 기반으로 영역이 맞춰지는 위젯 컴포넌트를 만들었다.
이것으로 게임 화면에서 직접 3D 모델링을 클릭하거나 터치할 수 있다!
EWidgetSpace::Screen
으로 설정한다.UBoundingWidgetComponent::UBoundingWidgetComponent() { PrimaryComponentTick.bCanEverTick = true; SetWidgetSpace(EWidgetSpace::Screen); } void UBoundingWidgetComponent::UpdateBoxVertices(float DeltaTime) { if (GetAttachParent()) { FBox ParentBox = GetAttachParent()->Bounds.GetBox(); ParentBox.GetVertices(BoxVertices); } break; // 디버그용 꼭지점 시각화 for (FVector Verticle : BoxVertices) { DrawDebugPoint(GetWorld(), Verticle, 10.f, FColor::Red, false, DeltaTime); } }
void UBoundingWidgetComponent::UpdateWidgetSize(float DeltaTime) { FVector2D ScreenMin(FLT_MAX, FLT_MAX); FVector2D ScreenMax(-FLT_MAX, -FLT_MAX); if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController()) { // Box Vertices 화면 좌표 변환 for (const FVector& Vertex : BoxVertices) { FVector2D ScreenPos; if (PlayerController->ProjectWorldLocationToScreen(Vertex, ScreenPos)) { FGeometry Geometry = UWidgetLayoutLibrary::GetPlayerScreenWidgetGeometry(PlayerController); FVector2D LocalToAbsoluteScale = Geometry.GetLocalSize() / Geometry.GetAbsoluteSize(); ScreenPos *= LocalToAbsoluteScale; ScreenMin.X = FMath::Min(ScreenMin.X, ScreenPos.X); ScreenMin.Y = FMath::Min(ScreenMin.Y, ScreenPos.Y); ScreenMax.X = FMath::Max(ScreenMax.X, ScreenPos.X); ScreenMax.Y = FMath::Max(ScreenMax.Y, ScreenPos.Y); } } // 위젯 크기 설정 SetDrawSize(ScreenMax - ScreenMin); } }
void UBoundingWidgetComponent::UpdateWidgetPosition(float DeltaTime) { FVector BoxOrigin = FVector::ZeroVector; for (const FVector& Vertex : BoxVertices) { BoxOrigin += Vertex; } BoxOrigin /= 8; SetWorldLocation(BoxOrigin); }
🚀 BoundingWidgetComponent.h
UENUM(BlueprintType) enum class EBoundingWidgetTarget : uint8 { AttachedComponent, RootComponent, AllMeshes }; UCLASS(Blueprintable, meta=(BlueprintSpawnableComponent)) class UBoundingWidgetComponent : public UWidgetComponent { GENERATED_BODY() public: UBoundingWidgetComponent(); UPROPERTY(EditAnywhere, BlueprintReadWrite) EBoundingWidgetTarget BoundingTarget = EBoundingWidgetTarget::RootComponent; UPROPERTY(EditAnywhere, BlueprintReadWrite) FVector2D WidgetScale = FVector2D(1.f, 1.f); public: virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; private: UPROPERTY() FVector BoxVertices[8]; void UpdateBoxVertices(float DeltaTime); void UpdateWidgetSize(float DeltaTime); void UpdateWidgetPosition(float DeltaTime); };
🚀 BoundingWidgetComponent.cpp
UBoundingWidgetComponent::UBoundingWidgetComponent() { PrimaryComponentTick.bCanEverTick = true; SetWidgetSpace(EWidgetSpace::Screen); } void UBoundingWidgetComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); UpdateBoxVertices(DeltaTime); UpdateWidgetSize(DeltaTime); UpdateWidgetPosition(DeltaTime); } void UBoundingWidgetComponent::UpdateBoxVertices(float DeltaTime) { switch (BoundingTarget) { case EBoundingWidgetTarget::RootComponent: if (GetOwner() && GetOwner()->GetRootComponent()) { FBox RootBox = GetOwner()->GetRootComponent()->Bounds.GetBox(); RootBox.GetVertices(BoxVertices); } break; case EBoundingWidgetTarget::AttachedComponent: if (GetAttachParent()) { FBox ParentBox = GetAttachParent()->Bounds.GetBox(); ParentBox.GetVertices(BoxVertices); } break; case EBoundingWidgetTarget::AllMeshes: if (GetOwner()) { FVector Origin; FVector Extent; GetOwner()->GetActorBounds(true, Origin, Extent); BoxVertices[0] = Origin + FVector(Extent.X, Extent.Y, Extent.Z); BoxVertices[1] = Origin + FVector(-Extent.X, Extent.Y, Extent.Z); BoxVertices[2] = Origin + FVector(Extent.X, -Extent.Y, Extent.Z); BoxVertices[3] = Origin + FVector(-Extent.X, -Extent.Y, Extent.Z); BoxVertices[4] = Origin + FVector(Extent.X, Extent.Y, -Extent.Z); BoxVertices[5] = Origin + FVector(-Extent.X, Extent.Y, -Extent.Z); BoxVertices[6] = Origin + FVector(Extent.X, -Extent.Y, -Extent.Z); BoxVertices[7] = Origin + FVector(-Extent.X, -Extent.Y, -Extent.Z); } break; } } void UBoundingWidgetComponent::UpdateWidgetSize(float DeltaTime) { FVector2D ScreenMin(FLT_MAX, FLT_MAX); FVector2D ScreenMax(-FLT_MAX, -FLT_MAX); if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController()) { for (const FVector& Vertex : BoxVertices) { FVector2D ScreenPos; if (PlayerController->ProjectWorldLocationToScreen(Vertex, ScreenPos)) { FGeometry Geometry = UWidgetLayoutLibrary::GetPlayerScreenWidgetGeometry(PlayerController); FVector2D LocalToAbsoluteScale = Geometry.GetLocalSize() / Geometry.GetAbsoluteSize(); ScreenPos *= LocalToAbsoluteScale; ScreenMin.X = FMath::Min(ScreenMin.X, ScreenPos.X); ScreenMin.Y = FMath::Min(ScreenMin.Y, ScreenPos.Y); ScreenMax.X = FMath::Max(ScreenMax.X, ScreenPos.X); ScreenMax.Y = FMath::Max(ScreenMax.Y, ScreenPos.Y); } } float WidgetSizeX = (ScreenMax - ScreenMin).X * WidgetScale.X; float WidgetSizeY = (ScreenMax - ScreenMin).Y * WidgetScale.Y; SetDrawSize(FVector2D(WidgetSizeX, WidgetSizeY)); } } void UBoundingWidgetComponent::UpdateWidgetPosition(float DeltaTime) { FVector BoxOrigin = FVector::ZeroVector; for (const FVector& Vertex : BoxVertices) { BoxOrigin += Vertex; } BoxOrigin /= 8; SetWorldLocation(BoxOrigin); }
BoundingTarget
을 바꿔가며 테스트했다.EBoundingWidgetTarget::RootComponent
사용EBoundingWidgetTarget::AttachedComponent
사용EBoundingWidgetTarget::AllMeshes
사용