【UE4】UMG 04_自定義UMG並實時預覽
參考資料&原文鏈接
簡介
上節我們已經研究過UMG的生命周期了:【UE4】UMG_UMG生命周期,這節來試一試自己搞一個UMG出來,然后看是否能方便我們。
在項目里面風格要統一,比如按鈕的大小、圖片、里面的字體樣式等等這些風格都要統一,而如果每次都在UMG里面拖出來然后又改的話實在是太浪費時間了,做了大量的重復勞動。
我希望有一個通用的按鈕,它能夠統一我們的風格,並且在替換的時候只需要修改與它相關的C++和藍圖即可,其他的都可以跟着變。
修改Build.cs文件
以下內容來自UE4中UMG與C++交互 頁面文本修改:
在.Build.cs文件中將以下行:
PublicDependencyModuleNames.AddRange(``new` `string``[] { ``"Core"``, ``"CoreUObject"``, ``"Engine"``, ``"InputCore"` `});
修改為:
PublicDependencyModuleNames.AddRange(``new` `string``[] { ``"Core"``, ``"CoreUObject"``, ``"Engine"``, ``"InputCore"``, ``"UMG"``, ``"Slate"``, ``"SlateCore"` `});
修改完成后,打開和你項目同名的頭文件然后添加以下包含:
#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"
#include "Runtime/UMG/Public/Slate/SObjectWidget.h"
#include "Runtime/UMG/Public/IUMGModule.h"
比如我的項目名是TestProject
,那么這個文件名就是TestProject.Build.cs
,它一般就在源代碼的根目錄下:
新建C++文件
其實新建C++文件沒有這么麻煩,不用每次都開引擎然后在打開新建,我們只需要在Rider或者VS里面按照新建C++文件的步驟就行:
填好文件名點OK,然后把以前的代碼復制過來改個名字就完事了。比如我就直接復制另一個C++文件的頭文件和CPP文件的,然后改了個名(注意遵守命名規范):
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "WC_CommonBtn.generated.h"
/**
* 通用按鈕。
*/
UCLASS()
class TESTPROJECT_API UWC_CommonBtn : public UUserWidget
{
GENERATED_BODY()
};
我給它取名為「UWC_CommonBtn」。話不多說,先上代碼。
UWC_CommonBtn.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Engine/Font.h"
#include "Components/Button.h"
#include "Components/TextBlock.h"
#include "Styling/SlateTypes.h"
#include "WC_CommonBtn.generated.h"
class UButton;
class UTextBlock;
/**
* 通用按鈕。
*/
UCLASS()
class TESTPROJECT_API UWC_CommonBtn : public UUserWidget
{
GENERATED_BODY()
public:
//學一下UserWidget,把構造函數從UserWidget直接復制過來再改個名兒,拿來初始化屬性。
UWC_CommonBtn(const FObjectInitializer& ObjectInitializer);
public:
UPROPERTY(Meta = (BindWidget))
UTextBlock * Txt_Main;
UPROPERTY(Meta = (BindWidget))
UButton * Btn_Main;
/*
*屬性,這些屬性都是到對應的控件的頭文件里面去復制過來的,可以直接用。
*/
public:
/** The text to display */
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = "Content", meta = (DisplayName = "TextContent", MultiLine = "true"))
FText Text = FText::FromString(L"");//寬字符
/** The color of the text */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance" ,meta = (DisplayName = "TextColorAndOpacity"))
FSlateColor TextColorAndOpacity;
/** The font to render the text with */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Appearance", meta = (DisplayName = "TextFont"))
FSlateFontInfo TextFont;
/** The button style used at runtime */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance", meta = (DisplayName = "ButtonStyle"))
FButtonStyle ButtonStyle;
/** The default button style */
FButtonStyle* DefaultButtonStyle = nullptr;
protected:
virtual bool Initialize() override;
virtual void NativePreConstruct() override;
};
注意:像TextColorAndOpacity
、TextFont
、ButtonStyle
這些屬性都是可以直接從UE原來的控件的源代碼中去拿的,並且還有注釋,何樂而不為呢。
上節說過了如果是需要在PIE里面實時預覽的話就把預覽相關的代碼寫到NativePreConstruct()
即可。詳見下文。
UWC_CommonBtn.cpp
#include "WC_CommonBtn.h"
UWC_CommonBtn::UWC_CommonBtn(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
{
Text = FText::FromString(L"請重新填寫文字!");
//這幾行代碼可以直接從Button里面拿來用。
if (DefaultButtonStyle == nullptr)
{
// HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BE DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS
DefaultButtonStyle = new FButtonStyle(FCoreStyle::Get().GetWidgetStyle<FButtonStyle>("Button"));
// Unlink UMG default colors from the editor settings colors.
DefaultButtonStyle->UnlinkColors();
}
ButtonStyle = *DefaultButtonStyle;
//這幾行代碼可以直接從TextBlock里面拿來用。
if (!IsRunningDedicatedServer())
{
static ConstructorHelpers::FObjectFinder<UFont> RobotoFontObj(*UWidget::GetDefaultFontName());
TextFont = FSlateFontInfo(RobotoFontObj.Object, 24, FName("Bold"));
}
TextColorAndOpacity = FLinearColor::White;
}
bool UWC_CommonBtn::Initialize()
{
//安全判斷不要忘了,避免空指針異常,讓程序崩潰。
if (!Super::Initialize())
{
return false;
}
return true;
}
void UWC_CommonBtn::NativePreConstruct()
{
Super::NativePreConstruct();
//控件其實已經在Initialize()的時候用BindWidget拿到了,不過為了避免崩潰還是判斷一下。
if (Txt_Main)
{
Txt_Main->SetText(Text);
Txt_Main->SetFont(TextFont);
Txt_Main->SetColorAndOpacity(TextColorAndOpacity);
}
if (Btn_Main)
{
Btn_Main->SetStyle(ButtonStyle);
}
}
這幾行代碼都比較簡單,並且為什么這么做已經在前面說過了,所以就不再贅述啦。
新建藍圖並調整好樣式
新建UMG文件,然后把它的父類設置為我們的父類:
然后把按鈕和文本框建好,注意名字和層級,並且要讓按鈕鋪滿屏幕,因為這是作為一個單獨的Widget,它的大小是在外面設置的,然后把我們想要的樣式在編輯器里面調整好,調整好之后再點保存:
在外面使用它
然后就可以在其他的UMG中使用它啦:
如果發現這個大小不好調的話可以先調好一個,然后直接Ctrl CV就行。
調好大小,改好名字,然后就可以像使用按鈕一樣使用它了:
這是定義:
public:
UPROPERTY(Meta = (BindWidget))
UTextBlock * Txt_Main = nullptr;
UPROPERTY(Meta = (BindWidget))
UWC_CommonBtn * WB_CommonBtn_1;
UPROPERTY(Meta = (BindWidget))
UWC_CommonBtn * WB_CommonBtn_2;
public:
UFUNCTION()
void OnBtnClickCommonBtn_1();
UFUNCTION()
void OnBtnClickCommonBtn_2();
注意的是在綁定按鈕事件或者訪問其他屬性的時候需要指定到Widget里面的Button:
void UWC_TestUI::NativeConstruct()
{
Super::NativeConstruct();
if (WB_CommonBtn_1)
{
WB_CommonBtn_1->Btn_Main->OnClicked.AddDynamic(this,&UWC_TestUI::OnBtnClickCommonBtn_1);
}
if (WB_CommonBtn_2)
{
WB_CommonBtn_2->Btn_Main->OnClicked.AddDynamic(this,&UWC_TestUI::OnBtnClickCommonBtn_2);
}
}
//銷毀的時候要取消綁定
void UWC_TestUI::NativeDestruct()
{
//UE_LOG(LogTemp,Warning,TEXT("UWC_TestUI:NativeDestruct"));
Super::NativeDestruct();
if (WB_CommonBtn_1)
{
WB_CommonBtn_1->Btn_Main->OnClicked.RemoveDynamic(this,&UWC_TestUI::OnBtnClickCommonBtn_1);
}
if (WB_CommonBtn_2)
{
WB_CommonBtn_2->Btn_Main->OnClicked.RemoveDynamic(this,&UWC_TestUI::OnBtnClickCommonBtn_2);
}
}
//按鈕的兩個響應事件
void UWC_TestUI::OnBtnClickCommonBtn_1()
{
UE_LOG(LogTemp,Warning,TEXT("UWC_TestUI:OnBtnClickCommonBtn_1!"));
}
void UWC_TestUI::OnBtnClickCommonBtn_2()
{
UE_LOG(LogTemp,Warning,TEXT("UWC_TestUI:OnBtnClickCommonBtn_2!"));
}
效果
稍微改一下關卡藍圖:
運行一手:
找屬性的技巧
這里說一個技巧,就是我怎么知道我需要哪些屬性呢?這些屬性的表現是什么呢?我該去哪個控件的文件里面去拿呢?頭文件又該引入啥呢?
先隨便點開一個藍圖,然后拖出你想要的控件,比如我想要一個Button:
並且我想要知道這個Appearance下面的這個Style,但是我不知道應該在C++里面該怎么寫。
那么可以點開Button的頭文件,喚起代碼編輯器:
然后在這里面查找Appearance
,記得勾上區分大小寫和全字匹配,多找幾下就能在DisplayName
這個屬性下面找到Style
。
然后我們就知道了這個Style其實是FButtonStyle
類型的,你找到了這個就可以直接復制到你的代碼里啦。
最后一步是找到它的頭文件,雖然一般情況下會自動引入,但是還是說一下找頭文件的方法,以備不時之需。
有兩種方法,第一種是直接在這個界面往上翻,就能發現:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Input/Reply.h"
#include "Styling/SlateTypes.h"
#include "Widgets/SWidget.h"
#include "Components/ContentWidget.h"
#include "Button.generated.h"
class SButton;
class USlateWidgetStyleAsset;
猜一下是哪個呢?當然是#include "Styling/SlateTypes.h"
這一行啦。
要是猜不准的話,還有第二種方法。
先點開UE的官方文檔,然后在搜索框你需要的類名,比如說我想搜索FButtonStyle
,然后進第一個頁面就能看到:
直接把這行復制走就行了。
總結
這次我們可以自定義UMG了,並且也可以正確的復用了,還提了一下找屬性的技巧,是提高UMG開發的一小步~
本文標簽
游戲開發
、游戲開發基礎
、Unreal Engine
、UE4 用戶界面
、UE4 UMG
、UMG基礎
。