Fork me on GitHub

UE4 从无到有纯 C++ & Slate 开发沙盒游戏(七) 自定义Button控件

这里将会自定义一个按钮

 首先在 Public 下创建一个 SlateWidget 类

 

d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Public\UI\Widget\SSlAiMenuWidget.h

#include "CoreMinimal.h"
#include "Data/SlAiTypes.h"
#include "Widgets/SCompoundWidget.h"

class SBox;
class STextBlock;
class SVerticalBox;

class SLAICOURSE_API SSlAiMenuWidget : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(SSlAiMenuWidget)
    {}
    SLATE_END_ARGS()

        /** Constructs this widget with InArgs */
        void Construct(const FArguments& InArgs);

private:
    //绑定到各个MenuItem的方法
    void MenuItemOnClicked(EMenuItem::Type ItemType);

private:
    //保存根节点,用来动态的修改SBox的大小
    TSharedPtr<SBox> RootSizeBox;

    //获取MenuStyle
    const struct FSlAiMenuStyle* MenuStyle;

    //保存标题
    TSharedPtr<STextBlock> TitleText;

    //用来保存垂直列表,所有的菜单按钮组件都会放置到这ContentBox中
    TSharedPtr<SVerticalBox> ContentBox;
};

 

 d:\ue4 project\ue26.2\courseproject\slaicourse\Source\SlAiCourse\Private\UI\Widget\SSlAiMenuWidget.cpp

#include "UI/Widget/SSlAiMenuWidget.h"
#include "SlateOptMacros.h"
#include "UI/Style/SlAiStyle.h"
#include "UI/Style/SlAiMenuWidgetStyle.h"
#include "Widgets/Layout/SBox.h"                                        //一版会把SlateWidget的根组件设成SBox
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/STextBlock.h"
#include "Internationalization/Internationalization.h"                //一个关于本地化的头文件
#include "UI/Widget/SSlAiMenuItemWidget.h"
#include "Widgets/SBoxPanel.h"


BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SSlAiMenuWidget::Construct(const FArguments& InArgs)
{
    //获取MenuStyle
    MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle");
    
    /**
    *切换语言
    */
    FInternationalization::Get().SetCurrentCulture(TEXT("en"));
    //FInternationalization::Get().SetCurrentCulture(TEXT("ch"));

    ChildSlot
    [
        /**
        *没有Slot,没有Slot要么不能插入子组件,要么只能插入一个子组件,SizeBox 只能插入一个子组件
        */
        SAssignNew(RootSizeBox, SBox)
        [
            SNew(SOverlay)

            +SOverlay::Slot()                                        //主菜单背景
                .HAlign(HAlign_Fill)
                .VAlign(VAlign_Fill)
                .Padding(FMargin(0.f, 50.f, 0.f, 0.f))        //FMargin 间隔(左 上 右 下)
                [
                    SNew(SImage)
                    .Image(&MenuStyle->MenuBackgroundBrush)
                ]

            +SOverlay::Slot()                                        //菜单左侧图片
                .HAlign(HAlign_Left)
                .VAlign(VAlign_Center)
                .Padding(FMargin(0.f, 25.f, 0.f, 0.f))
                [
                    SNew(SImage).Image(&MenuStyle->LeftIconBrush)
                ]

            +SOverlay::Slot()                                        //菜单右侧图片
                .HAlign(HAlign_Right)
                .VAlign(VAlign_Center)
                .Padding(FMargin(0.f, 25.f, 0.f, 0.f))
                [
                    SNew(SImage).Image(&MenuStyle->RightIconBrush)
                ]

            +SOverlay::Slot()                                        //菜单标题图片
                .HAlign(HAlign_Center)
                .VAlign(VAlign_Top)
                [
                    SNew(SBox)
                    .WidthOverride(400.f)
                    .HeightOverride(100.f)
                    [
                        SNew(SBorder)
                        .BorderImage(&MenuStyle->TitleBorderBrush)
                        .HAlign(HAlign_Center)
                        .VAlign(VAlign_Center)
                        [
                            SAssignNew(TitleText, STextBlock)
                            .Font(SlAiStyle::Get().GetFontStyle("MenuItemFort"))
                            .Text(NSLOCTEXT("SlAiMenu", "Menu", "Menu"))
                            //.Font(MenuStyle->Font_60)
                        ]
                    ]
                ]

            +SOverlay::Slot()                            //菜单按钮组件
                .HAlign(HAlign_Center)
                .VAlign(VAlign_Top)
                .Padding(FMargin(0.f, 130.f, 0.f, 0.f))
                [
                    //菜单组件创建到这里
                    SAssignNew(ContentBox, SVerticalBox)
                ]
        ]
    ];

    /**
    * RootSizeBox在生成的时候没有设置他的宽和高,这里设置下
    */
    RootSizeBox->SetWidthOverride(600.f);
    RootSizeBox->SetHeightOverride(510.f);

    /*******************************************************************
    *ContentBox->AddSlot() 等同于在 ChildSlot 下的 SAssignNew(ContentBox, SVerticalBox) 层级下添加:
    * 
        SAssignNew(ContentBox, SVerticalBox)
        +SVerticalBox::Slot()
        [
            SNew(SSlAiMenuItemWidget)
            .ItemText(NSLOCTEXT("SlAimenu", "StartGame", "StartGame"))
            .ItemType(EMenuItem::StartGame)
            .OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)
        ]
    * 
    ****************************************************************/
    ContentBox->AddSlot()
    [
        SNew(SSlAiMenuItemWidget)
        .ItemText(NSLOCTEXT("SlAimenu", "StartGame", "StartGame"))
        .ItemType(EMenuItem::StartGame)
        .OnClicked(this, &SSlAiMenuWidget::MenuItemOnClicked)
    ];
}

END_SLATE_FUNCTION_BUILD_OPTIMIZATION


void SSlAiMenuWidget::MenuItemOnClicked(EMenuItem::Type ItemType)
{
}

 

 

D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Public\UI\Style\SlAiMenuWidgetStyle.h

#include "CoreMinimal.h"
#include "Styling/SlateWidgetStyle.h"
#include "Styling/SlateWidgetStyleContainerBase.h"
#include "Styling/SlateBrush.h"
#include "Fonts/SlateFontInfo.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;

    /*定义笔刷
    *     主背景图片*/
    UPROPERTY(EditAnywhere, Category = Menu)
        FSlateBrush MenuBackgroundBrush;

    /*定义笔刷
    *     Menu左图标的Brush*/
    UPROPERTY(EditAnywhere, Category = Menu)
        FSlateBrush LeftIconBrush;

    /*定义笔刷
    *     Menu右图标的Brush*/
    UPROPERTY(EditAnywhere, Category = Menu)
        FSlateBrush RightIconBrush;

    /*定义笔刷
    *     Menu标题Border的Brush*/
    UPROPERTY(EditAnywhere, Category = Menu)
        FSlateBrush TitleBorderBrush;

    /*定义笔刷
    *     MenuItem的Brush*/
    UPROPERTY(EditAnywhere, Category = MenuItem)
        FSlateBrush MenuItemBrush;

    /**
    * 30号字体    */
    UPROPERTY(EditAnywhere, Category = Common)
        FSlateFontInfo Font_30;

    /**
    * 40号字体    */
    UPROPERTY(EditAnywhere, Category = Common)
        FSlateFontInfo Font_40;

    /**
    * 60号字体    */
    UPROPERTY(EditAnywhere, Category = Common)
        FSlateFontInfo Font_60;
};

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 );
    }
};

 

 

D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Public\UI\Widget\SSlAiMenuItemWidget.h

#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "Data/SlAiTypes.h"


/**
* 定义委托
* 按钮点击事件需要先定义一个委托,_OneParam 一个参数的委托,(委托的名字,传入的变量)
*/
DECLARE_DELEGATE_OneParam(FItemClicked, const EMenuItem::Type)

/**
 * 
 */
class SLAICOURSE_API SSlAiMenuItemWidget : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(SSlAiMenuItemWidget)
    {}

    SLATE_EVENT(FItemClicked, OnClicked)                    //运行这个控件,委托FItemClicked,触发 OnClicked, OnClicked绑定MenuItemOnClicked函数指针
    SLATE_ATTRIBUTE(FText, ItemText)                        //变量类型,变量名
    SLATE_ATTRIBUTE(EMenuItem::Type, ItemType)

    SLATE_END_ARGS()

    /** Constructs this widget with InArgs */
    void Construct(const FArguments& InArgs);

    /**
    * 重写组件的OnMouseButtonDown方法*/
    virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
    /**
    * 重写按钮起来的方法*/
    virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
    /**
    * 重写鼠标离开的方法 */
    virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;

private:
    /*
    获取颜色*/
    FSlateColor GetTineColor() const;

private:
    //按下事件委托
    FItemClicked OnClicked;                /**
    * SSlAiMenuItemWidget.cpp 中的 FArguments& InArgs 可以获取到该类中的所有东西,
    * 如 OnClicked = InArgs._OnClicked;
    * 他对应的是  SLATE_EVENT 或 SLATE_ATTRIBUTE 中的那些名字
    */

    //保存按钮类型
    EMenuItem::Type ItemType;

    //获取MenuStyle
    const struct FSlAiMenuStyle* MenuStyle;

    //按钮是否已经按下
    bool IsMouseButtonDown;

};

 

 

D:\UE4 Project\UE26.2\CourseProject\SlAiCourse\Source\SlAiCourse\Private\UI\Widget\SSlAiMenuItemWidget.cpp

#include "UI/Widget/SSlAiMenuItemWidget.h"
#include "SlateOptMacros.h"
#include "UI/Style/SlAiStyle.h"
#include "UI/Style/SlAiMenuWidgetStyle.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/STextBlock.h"


BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SSlAiMenuItemWidget::Construct(const FArguments& InArgs)
{
    //获取MenuStyle
    MenuStyle = &SlAiStyle::Get().GetWidgetStyle<FSlAiMenuStyle>("BPSlAiMenuStyle");

    /**
    *SSlAiMenuWidget.cpp 新建控件的时候,通过AddSlot中传入的属性,在此处赋值到自己定义的变量里边
    */
    OnClicked = InArgs._OnClicked;
    ItemType = InArgs._ItemType.Get();


    ChildSlot
    [
        SNew(SBox)
        .WidthOverride(500.f)
        .HeightOverride(100.f)
        [
            SNew(SOverlay)

        +SOverlay::Slot()                    //按钮
            .VAlign(VAlign_Fill)
            .HAlign(HAlign_Fill)
            [
                SNew(SImage)
                .Image(&MenuStyle->MenuItemBrush)
                /*颜色和透明值绑定一个方法
                * 绑定之后每一帧都会调用一次这个方法
                * 如果发现这个方法值改变了,那么就会改变自己颜色
                */
                .ColorAndOpacity(this, &SSlAiMenuItemWidget::GetTineColor)
            ]
        
        +SOverlay::Slot()                    //文字
            .HAlign(HAlign_Center)
            .VAlign(VAlign_Center)
            [
                SNew(STextBlock)
                .Text(InArgs._ItemText)                //可直接通过InArgs获取ItemText
                .Font(MenuStyle->Font_60)
            ]
        ]
    ];

    //初始化
    IsMouseButtonDown = false;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION

//鼠标按下触发
FReply SSlAiMenuItemWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
    IsMouseButtonDown = true;
    return FReply::Handled();
}

//鼠标抬起触发
FReply SSlAiMenuItemWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
    /*如果按下了并且OnClicked委托绑定有方法那就执行
    */
    if (IsMouseButtonDown)
    {
        IsMouseButtonDown = false;
        /*运行委托,.Execute不会判断有没有绑定,.ExecuteIfBound是有绑定
        */
        OnClicked.ExecuteIfBound(ItemType);
    }
    return FReply::Handled();
}

//鼠标移开触发
void SSlAiMenuItemWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
{
    /*鼠标按着滑到别的地方,要将鼠标事件false掉
    */
    IsMouseButtonDown = false;
}

/*当这个方法传回来的值 FLinearColor 有改变的时候,调用的 ColorAndOpacity 就会有变化
*/
FSlateColor SSlAiMenuItemWidget::GetTineColor() const
{
    /*根据鼠标是否有按下来触发
    */
    if (IsMouseButtonDown)
    {
        /*函数类型是 FSlateColor,返回值是FLinearColor,这样是没有问题的
        * FSlateColor 重写了一些构造函数,如果传给他一个 FLinearColor 会转换成 FSlateColor 
        * 这是C++的知识
        */
        return FLinearColor(1.f, 0.f, 0.1f, 0.5f);
    }
    else
    {
        return FLinearColor(1.f, 1.f, 1.f, 1.f);
    }
}

 

posted @ 2021-08-06 14:06  索智源  阅读(875)  评论(0编辑  收藏  举报