• UE4笔记-UMG和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:

      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
  • 相关阅读:
    STM32对HAL库的LCD驱动移植
    stm32对HAL库的DAC使用
    STM32对HAL库的ADC(多通道DMA)
    STM32对HAL库的ADC(单通道非DMA)
    STM32 fputc函数(重定向)
    STM32的HAL库DMA串口不定长度的读写操作(二)
    STM32对HAL库的PWM控制
    STM32对HAL库的定时器中断
    STM32对HAL库的外部中断处理
    C#上位机制作之串口接受数据(利用接受事件)
  • 原文地址:https://www.cnblogs.com/linqing/p/9839355.html
Copyright © 2020-2023  润新知