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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM