UE4 从无到有纯 C++ & Slate 开发沙盒游戏(四) DPI屏幕适配
改变窗口大小查看UI变化:
鼠标拖动窗口改变窗口大小,会发现会窗口中发其他UI组件会随着窗口大小改变而改变
具体改变规则可以从
Edit -> Project Settings 中查看 Engine -> User Interface 如下图
其中 DPI Scale Rule 的选项有6种,分别为
> Shortest Side:根据视口的最短边评估比例曲线。
> Longest Side:根据视口的最长边计算比例曲线
> Horizontal:根据视口的X轴计算比例曲线。
> Vertical:根据视口的Y轴计算比例曲线。
> Scale to Fit:不使用比例曲线。通过使用 DesignScreenSize 来模拟缩放框的行为,并将内容相对于它进行缩放。
> Custom:允许自定义规则解释。
下图便为比例曲线,其中添加的点便为边长对应的缩放比例:
将默认的都删掉,新建一个比例为1的,接下来将会用C++写一个自己的缩放规则
这时候运行游戏,拖动窗口我们发现,任凭窗口怎么拉UI都不会改变其大小。
这里想要的效果为拖动窗口,让其中的UI随着窗口的大小变化而变化,跟着窗口的比例走。
下面来实现这个效果
定义新笔刷
SlAiMenuWidgetStyle.h
#include "CoreMinimal.h" #include "Styling/SlateWidgetStyle.h" #include "Styling/SlateWidgetStyleContainerBase.h" #include "Styling/SlateBrush.h" #include "SlAiMenuWidgetStyle.generated.h" USTRUCT() struct SLAICOURSE_API FSlAiMenuStyle : public FSlateWidgetStyle //构造体 { GENERATED_USTRUCT_BODY() FSlAiMenuStyle(); virtual ~FSlAiMenuStyle(); // FSlateWidgetStyle virtual void GetResources(TArray<const FSlateBrush*>& OutBrushes) const override; static const FName TypeName; virtual const FName GetTypeName() const override { return TypeName; }; static const FSlAiMenuStyle& GetDefault(); //定义一个笔刷,主背景图片 UPROPERTY(EditAnywhere, Category = MenuHUD) FSlateBrush MenuHUDBackgroundBrush; //定义一个笔刷,Menu背景图片 UPROPERTY(EditAnywhere, Category = Menu) FSlateBrush MenuBackgroundBrush; }; /** */ UCLASS(hidecategories=Object, MinimalAPI) class USlAiMenuWidgetStyle : public USlateWidgetStyleContainerBase { GENERATED_BODY() public: /** The actual data describing the widget appearance. */ UPROPERTY(Category=Appearance, EditAnywhere, meta=(ShowOnlyInnerProperties)) FSlAiMenuStyle WidgetStyle; virtual const struct FSlateWidgetStyle* const GetStyle() const override { return static_cast< const struct FSlateWidgetStyle* >( &WidgetStyle ); } };
添加一些获取分辨率和绑定UIScaler的函数以及类型
SSlAiMenuHUDWidget.h
#include "CoreMinimal.h" #include "Widgets/SCompoundWidget.h" class SLAICOURSE_API SSlAiMenuHUDWidget : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SSlAiMenuHUDWidget) {} SLATE_END_ARGS() /** Constructs this widget with InArgs */ void Construct(const FArguments& InArgs); private: //绑定UIScaler的函数 float GetUIScaler() const; //获取屏幕尺寸 FVector2D GetViewportSize() const; private: //获取Menu样式,MenuStyle这个结构体指针对应的就是编辑器中蓝图继承这个样式的那个资源 const struct FSlAiMenuStyle* MenuStyle; //DPI缩放系数,TAttribute里存放一些类型,用来绑定一些函数 TAttribute<float> UIScaler; };
SSlAiMenuHUDWidget.cpp
#include "UI/Widget/SSlAiMenuHUDWidget.h" #include "SlateOptMacros.h" #include "Widgets/Input/SButton.h" #include "Widgets/Images/SImage.h" #include "UI/Style/SlAiStyle.h" #include "UI/Style/SlAiMenuWidgetStyle.h" #include "Widgets/SOverlay.h" #include "Widgets/Layout/SDPIScaler.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SSlAiMenuHUDWidget::Construct(const FArguments& InArgs) { /* 获取编辑器的MenuStule的名字 */ MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle"); /* 绑定缩放规则方法,如果窗口有改变时将会调用GetUIScaler方法 */ UIScaler.Bind(this, &SSlAiMenuHUDWidget::GetUIScaler); /* ChildSlot 是层级一样的东西,UI所有的东西都会放到这个下 */ ChildSlot [ SNew(SDPIScaler) .DPIScale(UIScaler) [ /* 实例化一个SOverlay,通常用SOverlay来做UI的布局,和蓝图中添加一个Overlay一样 */ SNew(SOverlay) +SOverlay::Slot() //Slot 添加插槽 .HAlign(HAlign_Fill) //_Fill 填充 .VAlign(VAlign_Fill) [ /* 实例化SImage,给这SImage添Image,然后加个笔刷(SlAiMenuWidgetStyle.h中定义)*/ SNew(SImage).Image(&MenuStyle->MenuHUDBackgroundBrush) ] + SOverlay::Slot() .HAlign(HAlign_Center) //_Center 居中 .VAlign(VAlign_Center) [ SNew(SImage).Image(&MenuStyle->MenuBackgroundBrush) ] ] ]; } END_SLATE_FUNCTION_BUILD_OPTIMIZATION /* 绑定UIScaler的函数,通过获取的ViewportSize的高度来设置Scaler*/ float SSlAiMenuHUDWidget::GetUIScaler() const { /* 获取高度除以1080来获得组件的缩放比例*/ return GetViewportSize().Y / 2160; } /* 获取屏幕尺寸*/ FVector2D SSlAiMenuHUDWidget::GetViewportSize() const { /* 设定屏幕分辨率*/ FVector2D Result(3840, 2160); if (GEngine && GEngine->GameViewport) { GEngine->GameViewport->GetViewportSize(Result); } return FVector2D(); }
这个时候在改变窗口大小,UI分辨率会跟着窗口的高度比例一起变化