UE4笔记-UMG和Slate记录

摘要:
UMG和Slate的个人开发笔记和更新是UE4 UI系统的一部分:整个布局系统是标准的C/s模式(Qt/WinForm)。UMG是基于最初的Slate封装开发的GUI。UE4为用户提供了一个可视化编辑器来编辑他们自己的GUI系统。同时,UMG组件还添加了许多事件和方法,并支持完全C++编码的BPSlate。所有布局和组件创建只能在C++中实现(Slate有一些较低级别的组件,

个人开发记录笔记,随缘更新

UMG和Slate都属于UE4的UI系统的一部分: 

整套布局系统是很标准的C/S方式(Qt/WinForm)

UMG是基于原先的Slate封装开发的GUI.UE4提供了可视化编辑器用于用户编辑自己GUI系统同时UMG组件还添加了很多事件和方法并支持BP

Slate则是完全C++代码化的,所有的布局和组件创建只能用C++实现(Slate有一些更底层的组件,如SSplitter等,更便于开发复杂UI).

这篇随笔用于记录一些文档以外一些UMG和Slate的一些问题和混用例子(UPanelWidget和UContentWidget)

Umg文档:http://api.unrealengine.com/INT/Engine/UMG/index.html

Slate文档:http://api.unrealengine.com/INT/Programming/Slate/index.html

 其他一些文章Mark:

[UE4]Slate and Native UMG(C++) Notes: https://dawnarc.com/2018/12/ue4slate-and-native-umgc-notes/

Q.生命周期:

  UMG是居于UOBJECT的而Slate却是居于TSharedFromThis,所以UMG可以暴露于BP,而Slate只能应用于C++,而且声明周期也不尽相同:

    wait

  Umg:

UE4笔记-UMG和Slate记录第1张

  Slate:

     (懒癌附体,康心情补充)

Q.创建细节:

Umg:

 关于创建对象:

  因为UMG大多数都是BP类,所以当需要在C++创建时,需要通过TSubclassOf将BP类传回C++或使用LoadClass引用BP类:

  note:

    1.通常创建使用CreateWidget 函数,但是,如果想创建非UserWidget的类,如,UButton 等UContentWidget或UPanelWidget,可以用Construct Object from class函数来创建.免去无意义UUserWidget 封装

  C++创建BP类Widget的栗子:

UUserWidget* AMyProject2Character::CreateBPUserWidget(TSubclassOf<UUserWidget> SpecificBPClass)
{

    UUserWidget *newUserWidget = nullptr;
    UClass *SpecificBPClassFromCPlusPlus = LoadClass<UUserWidget>(NULL, TEXT("/Game/Blueprints/BPBaseWgt.BPBaseWgt_C"));
    if (SpecificBPClassFromCPlusPlus)
    {
      newUserWidget = CreateWidget<UUserWidget>(UGameplayStatics::GetPlayerController(GetWorld(), 0), SpecificBPClassFromCPlusPlus);
      check(newUserWidget)
    }

    return newUserWidget;

}

   

  关于UMG的C++与BP的混合使用:

  通常都会定义一个C++的UUserWidget类来作为BP UMG的基类,以暴露一些BP变量到C++中,

  一般不熟悉的情况下,会在BP中的Pre Construct 或Construct 事件下手动赋值到C++定义的变量上。

  事实上,可以选择使用UPROPERTY的Meta宏进行自动绑定

  如:绑定Editor编辑器定义的UMG的控件控件和动画类到C++基类的变量上

    UPROPERTY(BlueprintReadOnly, Category = "MainWidget", Meta = (BindWidget))
        UHorizontalBox *Container = nullptr;

    UPROPERTY(BlueprintReadOnly, Category = "MainWidget", Meta = (BindWidgetAnim))
        class UWidgetAnimation* Anim_Container = nullptr;

  当UMG继承了该基类,UE4会自动跟BP中名为Container 的容器和Anim_Container的动画 绑定

Slate的创建:

Slate在C++中 则是使用类似如下的方式创建:

TSharedPtr<SMySlateWidget> slateWidget = SNew(SMySlateWidget);

TSharedPtr<SMySlateWidget> MySlateWidget;
TSharedRef<SSplitter> MyWgtRef = SAssignNew( MySlateWidget, SMySlateWidget);

贴出SMySlateWidget实现:

.h

#pragma once
#include "CoreMinimal.h"
#include "SUserWidget.h"
class MYPROJECT2_API SMySlateWidget : public SUserWidget
{

public:
    SLATE_USER_ARGS(SMySlateWidget)
    {}
    SLATE_END_ARGS()

public:
    virtual void Construct(const FArguments& InArgs);
protected:
    FSlateBrush brush;
};

.cpp

#include "SMySlateWidget.h"
#include "Slate.h"
#include "SConstraintCanvas.h"

void SMySlateWidget::Construct(const FArguments& InArgs)
{
    TSharedRef<SBorder> border = SNew(SBorder);
    border->SetBorderBackgroundColor(FLinearColor::Red);
    border->SetForegroundColor(FLinearColor(0, 255, 0, 0.5));
    border->SetBorderImage(&brush);
    border->SetColorAndOpacity(FLinearColor::Green);

    SConstraintCanvas::FSlot &temp_slot = SConstraintCanvas::Slot();
    temp_slot.Anchors(FAnchors(0.0f, 0.0f, 1.0f, 1.0f))
        .Offset(FMargin(100.0f, 100.0f, 100.0f, 100.0f))
        .ZOrder(1)
        .AttachWidget(border);
    SUserWidget::Construct(
                            SUserWidget::FArguments()
                            [
                                SNew(SConstraintCanvas) + temp_slot
                            ] 
                          );
}

TSharedRef<SMySlateWidget> SMySlateWidget::New()
{
    return MakeShareable(new SMySlateWidget());
}

Q.在Slate中使用UMG组件:

  方法一:

    使用TakeWidget();函数转换成Slate即可

//temporary_wgt 是你的UUserWIdget类实例
    TSharedRef<SWidget> border = temporary_wgt->TakeWidget();

例如在RebuildWidget中:

TSharedRef<SWidget> UCppWgt_BaseSplitter::RebuildWidget()
{

  //temporary_wgt 是你的UUserWIdget类实例,自行Create Widget
  TSharedRef<SWidget> border = temporary_wgt->TakeWidget();

  SConstraintCanvas::FSlot &temp_slot = SConstraintCanvas::Slot();
    temp_slot.Anchors(FAnchors(0.0f, 0.0f, 1.0f, 1.0f))
        .Offset(FMargin(100.0f, 100.0f, 100.0f, 100.0f))
        .ZOrder(1)
        .AttachWidget(container);

    auto ret_wgt = SNew(SConstraintCanvas) + temp_slot;
    return  ret_wgt;
}

Q.混合使用:

方法一(覆盖形式):

如果想在UMG添加一个Slate的组件,那么你可以用UWidget子类简单封装一下,重载RebuildWidget,使用Slate的Widget来完全覆盖代替

这里就用上面创建的Slate:SMySlateWidget

例子:

.h

UCLASS()
class
项目_API UContenSlateWidget : public UUserWidget { GENERATED_BODY() public :
virtual const FText GetPaletteCategory() override; protected: virtual TSharedRef<SWidget> RebuildWidget() override; };

.cpp

const FText UContenSlateWidget::GetPaletteCategory()
{
    return NSLOCTEXT("UContenSlatetWidget","MyCustomSlate", "CustomSlate");
}

TSharedRef<SWidget> UContenSlateWidget::RebuildWidget() 
{
    TSharedRef<SMySlateWidget> mySlateCom = SNew(SMySlateWidget);
    
    return mySlateCom;
}

方法二:

重写RebuildWidget是混用最简单的方式,但是却无法在UMG编辑器里二次编辑扩展UMG类.
那么如果有相关需求,这个时候可以考虑TakeDerivedWidget函数来代替
重写RebuildWidget的方式

栗子:
待添加

Q.UPanelWidget和UContentWidget分析和栗子:

UPanelWidget和UContentWidget都是Slate对UMG暴露的封装基础实现类.

如UE4自带的UI组件:Border,Canvas,VerticalBox,SButton等都是基于以上两个类继承实现的

当你需要封装一些自定义组件的时候,可以继承它们或它们的子类

note:UContentWidget是UPanelWidget的子类,基于UPanelWidget重新封装实现的.

区别是:

  UPanelWidget是多个Slot的组件:例如VerticalBox

  UContentWidget是单个Slot的组件:例如Border,Button

源码分析:

  UPanelWidget:

    wait(懒癌附体,康心情补充)

  UContentWidget:

    wait(懒癌附体,康心情补充)

例子:

  基于UPanelWidget 自定义一个UMG 的Splitter的布局组件(CppWgt_SpliterComponent):

  需要扩展两个分别继承于UPanelSlot,UPanelWidget的类

    USplitterComponentSlot 和

    CppWgt_SpliterComponent

   Note:(这里只是对Spliter简单的UMG封装,需要自己根据情况扩展)

USplitterComponentSlot .h

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/ScriptMacros.h"
#include "Components/PanelSlot.h"
#include "Components/SlateWrapperTypes.h"

#include "Runtime/Slate/Public/Widgets/Layout/SSplitter.h"

#include "SplitterComponentSlot.generated.h"

UCLASS()
class 项目_API USplitterComponentSlot : public UPanelSlot
{
    GENERATED_UCLASS_BODY()
public :
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Layout|SSpliter Slot")
        float SizeValue = 1.0f;
public:

    void BuildSlot(TSharedRef<SSplitter> SplitterCom);

    // UPanelSlot interface
    virtual void SynchronizeProperties() override;
    // End of UPanelSlot interface

    virtual void ReleaseSlateResources(bool bReleaseChildren) override;

private:
    SSplitter::FSlot* Slot;
};

USplitterComponentSlot .cpp

  

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

#include "SplitterComponentSlot.h"

#include "Components/Widget.h"

/////////////////////////////////////////////////////
// UHorizontalBoxSlot

USplitterComponentSlot::USplitterComponentSlot(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    Slot = NULL;
}

void USplitterComponentSlot::ReleaseSlateResources(bool bReleaseChildren)
{
    Super::ReleaseSlateResources(bReleaseChildren);
    Slot = NULL;
}

void USplitterComponentSlot::BuildSlot(TSharedRef<SSplitter> SplitterCom)
{
    Slot = &SplitterCom->AddSlot()
    [
        Content == NULL ? SNullWidget::NullWidget : Content->TakeWidget()
    ].Value(SizeValue);
}

void USplitterComponentSlot::SynchronizeProperties()
{
}

CppWgt_SpliterComponent.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

#include "Runtime/UMG/Public/Components/PanelWidget.h"
#include "CppWgt_SpliterComponent.generated.h"

/**
 * 
 */
UCLASS()
class 项目_API UCppWgt_SpliterComponent : public UPanelWidget
{
    GENERATED_BODY()
public:

#if WITH_EDITOR
    // UWidget interface
    virtual const FText GetPaletteCategory() override;
    // End UWidget interface
#endif

    virtual void ReleaseSlateResources(bool bReleaseChildren) override;

protected:

    // UPanelWidget
    virtual UClass* GetSlotClass() const override;
    virtual void OnSlotAdded( UPanelSlot* Slot) override;
    virtual void OnSlotRemoved(UPanelSlot* Slot) override;
    // End UPanelWidget

protected:
    TSharedPtr<class SSplitter> MySplitter;

protected:
    // UWidget interface
    virtual TSharedRef<SWidget> RebuildWidget() override;
    // End of UWidget interface
};

CppWgt_SpliterComponent.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "CppWgt_SpliterComponent.h"

#include "Components/Border.h"
#include "Runtime/Slate/Public/Widgets/Layout/SBorder.h"

#include "Runtime/UMG/Public/Components/PanelSlot.h"
#include "SplitterComponentSlot.h"

#define LOCTEXT_NAMESPACE "UMG"

const FText UCppWgt_SpliterComponent::GetPaletteCategory()
{
    //UE_LOG(LogTemp, Log, TEXT(" GetPaletteCategory "));
    return LOCTEXT("", "QingUI");
}

void UCppWgt_SpliterComponent::ReleaseSlateResources(bool bReleaseChildren)
{
    Super::ReleaseSlateResources(bReleaseChildren);
    MySplitter.Reset();
}

UClass * UCppWgt_SpliterComponent::GetSlotClass() const
{
    UE_LOG(LogTemp, Log, TEXT(" GetSlotClass "));
    return USplitterComponentSlot::StaticClass();
}

void UCppWgt_SpliterComponent::OnSlotAdded(UPanelSlot * Slot)
{
    if (!MySplitter.IsValid())
    {
        return;
    }

    UE_LOG(LogTemp, Log, TEXT(" OnSlotAdded "));


    CastChecked< USplitterComponentSlot>(Slot)->BuildSlot(MySplitter.ToSharedRef());
}

void UCppWgt_SpliterComponent::OnSlotRemoved(UPanelSlot * Slot)
{
    //这里

  TSharedPtr<SWidget> Widget = Slot->Content->GetCachedWidget();
  if ( !MySplitter.IsValid() ||
    !Widget.IsValid() )
  {
    return;
  }

  FChildren* Children = MySplitter->GetChildren();

  for (int i = 0; i < Children->Num(); i++ )
  {
    TSharedRef<SWidget> tempWgt = Children->GetChildAt(i);

    if (Widget == tempWgt)
    {
      //Widget->SetVisibility(EVisibility::Hidden);
      MySplitter->RemoveAt(i);
      break;
    }
  }

 }


TSharedRef<SWidget> UCppWgt_SpliterComponent::RebuildWidget()
{
    MySplitter = SNew(SSplitter);

    
    for (UPanelSlot* PanelSlot : Slots) 
    {
        if (USplitterComponentSlot* TypedSlot = Cast<USplitterComponentSlot>(PanelSlot))
        {
            TypedSlot->Parent = this;
            TypedSlot->BuildSlot(MySplitter.ToSharedRef());

        }
    }
    
    return MySplitter.ToSharedRef();
}

#undef LOCTEXT_NAMESPACE

免责声明:文章转载自《UE4笔记-UMG和Slate记录》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇JS监听用户按下ESCDocker 容器内存限制下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【C++】解决c++中cout输出中文乱码问题

问题:cout输出中文乱码。例如下面的代码输出会乱码。 cout << "成功!" << endl; 输出结果:  解决方案: 控制台还原旧版即可,打开程序->右键->属性->使用旧版控制台->重启程序->解决!...

C++获取当前目录

获取当前目录对于.net来说很简单,在此不用多说了。C/C++来说挺也简单,但容易掉进MS的陷阱。在此仅作为笔记记录一下方法,以便以后备查,高手请路过。  若是VC MS 封装了好几个方法 1、最常用的GetCurrentDirectory,查一下它的原型:  DWORD GetCurrentDirectory(   DWORD nBufferLengt...

C++程序员学Python:C与Python进行交互

1.C调用python 其实C与python的交互很简单,最简单的就是下面的例子: 1 Py_Initialize(); 2 3 PyRun_SimpleString("str = \"hello\""); 4 PyRun_SimpleString("print str"); 5 6 Py_Finalize(); 与C在进入main函数前要做一些准备工...

c++ 模板元编程的一点体会

趁着国庆长假快速翻了一遍传说中的、大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身。。。论语言方面的深度,我看过的 c++ 书里大概只有 insight c++ object model 能与之一战吧?难怪 Herb 老喜欢调侃 Andrei 在模板方面是个可怕的家伙,就从这本书的质量来看,Andrei...

Python中第三方库的安装

网上的帖子挺多的,教你如何安装,安装第三方工具库的方法总共分为三类:Dos系统下pip命令;安装包下载安装;IDE集成环境下安装(Pycharm,Spyder……) http://www.jianshu.com/p/41a9c25273b1 一、pip命令 感觉这个最简单,但是需要注意几点 1、get-pip这个文件是否在您的Python目录中,Pytho...

MySQL-8.0.21安装

1.安装依赖 yum install -y ncurses-devel libaio-devel glibc autoconf openssl openssl-devel 2.安装cmake cd wget https://github.com/Kitware/CMake/releases/download/v3.19.0-rc3/cmake-3.19....