需求是这样的,飞机机舱的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应用就可以,不需要其他操作