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

浙公网安备 33010602011771号