UE4笔记-UMG和Slate记录
個(gè)人開(kāi)發(fā)記錄筆記,隨緣更新
UMG和Slate都屬于UE4的UI系統(tǒng)的一部分:
整套布局系統(tǒng)是很標(biāo)準(zhǔn)的C/S方式(Qt/WinForm)
UMG是基于原先的Slate封裝開(kāi)發(fā)的GUI.UE4提供了可視化編輯器用于用戶編輯自己GUI系統(tǒng)同時(shí)UMG組件還添加了很多事件和方法并支持BP
Slate則是完全C++代碼化的,所有的布局和組件創(chuàng)建只能用C++實(shí)現(xiàn)(Slate有一些更底層的組件,如SSplitter等,更便于開(kāi)發(fā)復(fù)雜UI).
這篇隨筆用于記錄一些文檔以外一些UMG和Slate的一些問(wèn)題和混用例子(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只能應(yīng)用于C++,而且聲明周期也不盡相同:
wait
Umg:
Slate:
(懶癌附體,康心情補(bǔ)充)
Q.創(chuàng)建細(xì)節(jié):
Umg:
關(guān)于創(chuàng)建對(duì)象:
因?yàn)閁MG大多數(shù)都是BP類,所以當(dāng)需要在C++創(chuàng)建時(shí),需要通過(guò)TSubclassOf將BP類傳回C++或使用LoadClass引用BP類:
note:
1.通常創(chuàng)建使用CreateWidget 函數(shù),但是,如果想創(chuàng)建非UserWidget的類,如,UButton 等UContentWidget或UPanelWidget,可以用Construct Object from class函數(shù)來(lái)創(chuàng)建.免去無(wú)意義UUserWidget 封裝
C++創(chuàng)建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;
}
關(guān)于UMG的C++與BP的混合使用:
通常都會(huì)定義一個(gè)C++的UUserWidget類來(lái)作為BP UMG的基類,以暴露一些BP變量到C++中,
一般不熟悉的情況下,會(huì)在BP中的PreConstruct 或Construct 事件下手動(dòng)賦值到C++定義的變量上。
事實(shí)上,可以選擇使用UPROPERTY的Meta宏進(jìn)行自動(dòng)綁定
如:綁定Editor編輯器定義的UMG的控件控件和動(dòng)畫類到C++基類的變量上
UPROPERTY(BlueprintReadOnly, Category = "MainWidget", Meta = (BindWidget))
UHorizontalBox *Container = nullptr;
UPROPERTY(BlueprintReadOnly, Category = "MainWidget", Meta = (BindWidgetAnim))
class UWidgetAnimation* Anim_Container = nullptr;
當(dāng)UMG繼承了該基類,UE4會(huì)自動(dòng)跟BP中名為Container 的容器和Anim_Container的動(dòng)畫 綁定
Slate的創(chuàng)建:
Slate在C++中則是使用類似如下的方式創(chuàng)建:
TSharedPtr<SMySlateWidget> slateWidget = SNew(SMySlateWidget);
或
TSharedPtr<SMySlateWidget> MySlateWidget;
TSharedRef<SSplitter> MyWgtRef = SAssignNew( MySlateWidget, SMySlateWidget);
貼出SMySlateWidget實(shí)現(xiàn):
.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();函數(shù)轉(zhuǎn)換成Slate即可
//temporary_wgt 是你的UUserWIdget類實(shí)例
TSharedRef<SWidget> border = temporary_wgt->TakeWidget();
例如在RebuildWidget中:
TSharedRef<SWidget> UCppWgt_BaseSplitter::RebuildWidget()
{
//temporary_wgt 是你的UUserWIdget類實(shí)例,自行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添加一個(gè)Slate的組件,那么你可以用UWidget子類簡(jiǎn)單封裝一下,重載RebuildWidget,使用Slate的Widget來(lái)完全覆蓋代替
這里就用上面創(chuàng)建的Slate:SMySlateWidget
例子:
.h
UCLASS()
class 項(xiàng)目_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是混用最簡(jiǎn)單的方式,但是卻無(wú)法在UMG編輯器里二次編輯擴(kuò)展UMG類.
那么如果有相關(guān)需求,這個(gè)時(shí)候可以考慮TakeDerivedWidget函數(shù)來(lái)代替重寫RebuildWidget的方式
栗子:
待添加
Q.UPanelWidget和UContentWidget分析和栗子:
UPanelWidget和UContentWidget都是Slate對(duì)UMG暴露的封裝基礎(chǔ)實(shí)現(xiàn)類.
如UE4自帶的UI組件:Border,Canvas,VerticalBox,SButton等都是基于以上兩個(gè)類繼承實(shí)現(xiàn)的
當(dāng)你需要封裝一些自定義組件的時(shí)候,可以繼承它們或它們的子類
note:UContentWidget是UPanelWidget的子類,基于UPanelWidget重新封裝實(shí)現(xiàn)的.
區(qū)別是:
UPanelWidget是多個(gè)Slot的組件:例如VerticalBox
UContentWidget是單個(gè)Slot的組件:例如Border,Button
源碼分析:
UPanelWidget:
wait(懶癌附體,康心情補(bǔ)充)
UContentWidget:
wait(懶癌附體,康心情補(bǔ)充)
例子:
基于UPanelWidget 自定義一個(gè)UMG 的Splitter的布局組件(CppWgt_SpliterComponent):
需要擴(kuò)展兩個(gè)分別繼承于UPanelSlot,UPanelWidget的類
USplitterComponentSlot 和
CppWgt_SpliterComponent
Note:(這里只是對(duì)Spliter簡(jiǎn)單的UMG封裝,需要自己根據(jù)情況擴(kuò)展)
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 項(xiàng)目_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 項(xiàng)目_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
總結(jié)
以上是生活随笔為你收集整理的UE4笔记-UMG和Slate记录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 扫雷网页版
- 下一篇: 中文输入法不触发onkeyup事件的解决