需求是这样的,飞机机舱的mfcd,也就是3D UI显示,通常我们会使用world widget,不过widget要贴附到模型上,效果并不好。
然后见过用材质做的,但当显示内容很复杂时,这种做法就很难进行下去。
我的做法是将widget渲染到renderTarget,然后材质使用该renderTarget赋予模型,效果不错。
1.新建组件FlightInstrumentSystem:
#pragma once #include "CoreMinimal.h" #include "System/FlightSubSystem.h" #include "FlightInstrumentSystem.generated.h" USTRUCT(BlueprintType) struct FInstrumentConfig { GENERATED_USTRUCT_BODY() UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") FString WidgetName; /*改widget的index 每个widget的index应该都是独一无二的,这样在座舱按键输入时才能找到正确的widget*/ UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") FString WidgetIndex; /*显示屏是否打开 True为通电就启动*/ UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") bool bOn; /*多功能显示器widget*/ UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") TSubclassOf<class UFlightInstrumentContainer> WidgetClass; /*widget大小*/ UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") FVector2D DrawSize; UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") FVector2D SeparateWindowDrawSize; /*该UI所要渲染到的目标*/ UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") class UTextureRenderTarget2D* RenderTarget; /*显示屏在默认状态下颜色*/ UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") FLinearColor DefaultColor; /*widget实例化*/ UPROPERTY(BlueprintReadOnly, Category = "FlightSimulator") class UFlightInstrumentContainer* InstrumentWidget; /*独立窗口是否打开*/ bool bSeparateWindowsOn; /*独立窗口句柄*/ TSharedPtr<SWindow> SeparateWindow; FInstrumentConfig() { WidgetName = TEXT("MFCD"); WidgetIndex = TEXT("0"); bOn = false; InstrumentWidget = nullptr; DrawSize = FVector2D(1000, 1600); SeparateWindowDrawSize = DrawSize / 2; RenderTarget = nullptr; DefaultColor = FLinearColor(0, 0.026042, 0.001792, 0); bSeparateWindowsOn = false; SeparateWindow = nullptr; } }; /** * 多功能显示器系统 */ UCLASS(ClassGroup = (FlightSimulator), meta = (BlueprintSpawnableComponent)) class FLIGHTSIMULATOR_API UFlightInstrumentSystem { GENERATED_UCLASS_BODY() virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; /*打开一个独立的窗口用于显示UI*/ UFUNCTION(BlueprintCallable, Category = "FlightSimulator|Instrument") void ToggleSeparateUIWindowOnOff(FString WidgetIndex); public: UPROPERTY(EditDefaultsOnly, Category = "FlightSimulator") TArray<FInstrumentConfig> InstrumentConfig; };
FlightInstrumentSystem.cpp
void UFlightInstrumentSystem::BeginPlay() { Super::BeginPlay(); for (FInstrumentConfig& config : InstrumentConfig) { if (config.WidgetClass) { config.InstrumentWidget = CreateWidget<UFlightInstrumentContainer>(UGameplayStatics::GetPlayerController(this, 0), config.WidgetClass); config.InstrumentWidget->AddToViewport(); //make widget out of screen config.InstrumentWidget->SetPositionInViewport(FVector2D(3000.0f, 3000.0f)); } if (config.RenderTarget) { UKismetRenderingLibrary::ClearRenderTarget2D(this, config.RenderTarget, config.DefaultColor); } } }
widget to rendertarget
void UFlightInstrumentSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(); for (FInstrumentConfig& config : InstrumentConfig) { if (config.bOn && config.InstrumentWidget && config.RenderTarget) { //不起作用 想透明 需在引擎中设置RT为透明 //UKismetRenderingLibrary::ClearRenderTarget2D(this, config.RenderTarget, config.DefaultColor); UFlightBlueprintFunctionLibrary::WidgetRenderToTextureRenderTarget(config.RenderTarget, config.InstrumentWidget, config.DrawSize, GetWorld()->DeltaTimeSeconds); } } }
WidgetRenderToTextureRenderTarget方法:
bool UFlightBlueprintFunctionLibrary::WidgetRenderToTextureRenderTarget(UTextureRenderTarget2D* TextureRenderTarget, UUserWidget* const Widget, const FVector2D& DrawSize, const float DeltaTime) { // As long as the slate application is initialized and the widget passed in is not null continue... if (FSlateApplication::IsInitialized() && Widget != nullptr) { // Get the slate widget as a smart pointer. Return if null. TSharedPtr<SWidget> SlateWidget(Widget->TakeWidget()); if (!SlateWidget) return false; // Create a new widget renderer to render the widget to a texture render target 2D. FWidgetRenderer* WidgetRenderer = new FWidgetRenderer(true); if (!WidgetRenderer) return false; // Update/Create the render target 2D. //TextureRenderTarget = WidgetRenderer->DrawWidget(SlateWidget.ToSharedRef(), DrawSize); WidgetRenderer->DrawWidget(TextureRenderTarget, SlateWidget.ToSharedRef(), DrawSize, DeltaTime); /* TSharedRef<SWidget> ref = Widget->TakeWidget(); WidgetRenderer->DrawWidget(TextureRenderTarget, ref, DrawSize, DeltaTime);*/ return true; } return false; }
独立窗口显示
void UFlightInstrumentSystem::ToggleSeparateUIWindowOnOff(FString WidgetIndex) { CHECK_FALSE_RETURN(GetIsSystemRunning()); for (FInstrumentConfig& config : InstrumentConfig) { if (config.WidgetIndex != WidgetIndex) continue; if (config.InstrumentWidget && config.bOn) { if (config.bSeparateWindowsOn) { //关闭独立窗口 // Destroy window if (config.SeparateWindow.Get() != nullptr) { if (GetWorld()->WorldType != EWorldType::Game) { config.SeparateWindow->RequestDestroyWindow(); } else { config.SeparateWindow->DestroyWindowImmediately(); } config.bSeparateWindowsOn = false; } } else { //打开独立窗口 const FSlateBrush* RT= new FSlateImageBrush((UObject*)config.RenderTarget, config.DrawSize); // 建立窗口 TSharedPtr<SWindow> MainWindow = SNew(SWindow) .ClientSize(config.SeparateWindowDrawSize) .SizingRule(ESizingRule::FixedSize) .Title(FText::FromString(config.WidgetName)) .CreateTitleBar(true) .SupportsMaximize(false) .SupportsMinimize(false) .IsTopmostWindow(true) [ SNew(SOverlay) + SOverlay::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SImage) .Image(RT) ] ]; FSlateApplication::Get().AddWindow(MainWindow.ToSharedRef()); config.SeparateWindow = MainWindow; config.bSeparateWindowsOn = true; } } } }
效果:
FlightInstrumentSystem组件:
模型只需要在屏幕材质上将上边的RenderTarget应用就可以,不需要其他操作