Fork me on GitHub

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分辨率会跟着窗口的高度比例一起变化 

 

posted @ 2021-07-21 16:57  索智源  阅读(877)  评论(0编辑  收藏  举报