【UE4】UMG 04_自定義UMG並實時預覽


【UE4】UMG 04_自定義UMG並實時預覽

參考資料&原文鏈接

UE4中UMG與C++交互 頁面文本修改

【UE4】UMG_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,它一般就在源代碼的根目錄下:

image-20211031183457680

新建C++文件

其實新建C++文件沒有這么麻煩,不用每次都開引擎然后在打開新建,我們只需要在Rider或者VS里面按照新建C++文件的步驟就行:

image-20211031182908061

填好文件名點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;
};

注意:像TextColorAndOpacityTextFontButtonStyle這些屬性都是可以直接從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文件,然后把它的父類設置為我們的父類:

image-20211031202948452

然后把按鈕和文本框建好,注意名字和層級,並且要讓按鈕鋪滿屏幕,因為這是作為一個單獨的Widget,它的大小是在外面設置的,然后把我們想要的樣式在編輯器里面調整好,調整好之后再點保存:

image-20211031204353458

在外面使用它

然后就可以在其他的UMG中使用它啦:

image-20211031204630230

如果發現這個大小不好調的話可以先調好一個,然后直接Ctrl CV就行。

調好大小,改好名字,然后就可以像使用按鈕一樣使用它了:

image-20211031205347201

這是定義:

	
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!"));
}

效果

稍微改一下關卡藍圖:

image-20211031214034053

運行一手:

通用按鈕 00_00_00-00_00_30

找屬性的技巧

這里說一個技巧,就是我怎么知道我需要哪些屬性呢?這些屬性的表現是什么呢?我該去哪個控件的文件里面去拿呢?頭文件又該引入啥呢?

先隨便點開一個藍圖,然后拖出你想要的控件,比如我想要一個Button:

image-20211031200939772

並且我想要知道這個Appearance下面的這個Style,但是我不知道應該在C++里面該怎么寫。

那么可以點開Button的頭文件,喚起代碼編輯器:

image-20211031201137470

然后在這里面查找Appearance,記得勾上區分大小寫和全字匹配,多找幾下就能在DisplayName這個屬性下面找到Style

image-20211031201321495

然后我們就知道了這個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,然后進第一個頁面就能看到:

image-20211031202044551

直接把這行復制走就行了。

總結

這次我們可以自定義UMG了,並且也可以正確的復用了,還提了一下找屬性的技巧,是提高UMG開發的一小步~

本文標簽

游戲開發游戲開發基礎Unreal EngineUE4 用戶界面UE4 UMGUMG基礎


免責聲明!

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



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