正文
对于真正的程序员来说,相比于蓝图还是 C++ 来的实在一些 —— Mantra
利用中午休息的时间,研究了以下 UMG 系统。参考了这位大神的知乎 工作中的UE4 ,非常不错的文章,大家可以关注。
如果只是为了测试的话,UMG 可以使用蓝图来操作,但是实际的开发中, UMG 的相关逻辑一般需要用 C++ 去实现,有的公司要求 UMG 的 EventGraph 中不允许使用任何节点。所以就要求我们了解如何用 C++ 高效快速的管理 UMG 系统。
工程设置
在工程的 Build.cs 中,进行设置
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG"});
// Uncomment if you are using Slate UI
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
创建 UserWidget 子类
新建基于 UserWidget 的 C++ 类,编译。之后新建 WidgetBlueprint ,并设置其父类为我们刚刚创建的 C++ 类。按照常规的方式将 WidgetBlueprint 添加到场景中,已达到测试的目的,此处笔者在 LevelBlueprint 中进行操作。
之后需要在 WidgetBlueprint 中创建 UI 元素,我们此处以 Button 为例,一定要勾选下图中所示的 IsVariable,否则无法在 C++ 代码中找到。
记住此处的变量名,我们只需要在 C++ 代码中定义一个同名的变量,就可以拿到该 UI 元素(底层的原理是 UE4 基于反射实现的,太高深的就不再这里讨论啦)。但是我们应该定义一个什么类型的变量呢,可以通过点击上图中绿色标记的部分,查看 Button 属于什么类型。通过查找可以发现,属于 UButton 类型
所以我们接着写我们的代码
TestUI.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "TestUI.generated.h"
UCLASS()
class MYPROJECT_API UTestUI : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(Meta = (BindWidget))
class UButton* Btn_Test;
};
我们定义了一个 UButton 类型的指针,几乎所有的 UI 元素都是指针。并且用 UPROPERTY 包裹,内部的 Meta 也非常重要,官方解释:允许开发者访问绑定在这个界面上的控件。那么接下来我们就可以使用这个变量啦。
为按钮绑定点击事件
- UMG 中有一个节点叫做
EventConstruct,在 C++ 中对应的函数为NativeConstruct,所以我们需要在 .h 中重写,并在 .cpp 中实现。我们会将按钮的绑定事件写在这个函数中 - 绑定按钮事件用的是委托,通过 Button.h 可以发现,C++ 已经提前为我们定义了委托和按钮点击对应的属性,请查看下面的截图
所以我们需要为按钮的 OnClick 事件进行绑定。绑定的本质是指定要执行的函数。在 UE4 中这样的函数都需要使用 UFUNCTION() 宏进行修饰,以便可以借助反射机制找到。
所以最终我们的头文件和实现文件如下
TestUI.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "TestUI.generated.h"
/**
*
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonClickedEventMantra);
UCLASS()
class MYPROJECT_API UTestUI : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
UPROPERTY(Meta = (BindWidget))
class UButton* Btn_Test;
UFUNCTION()
void OnBtn_Test();
};
TestUI.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestUI.h"
#include "Widgets/SWidget.h"
#include "Components/ContentWidget.h"
#include "Components/Button.h"
#include "Components/Image.h"
void UTestUI::NativeConstruct()
{
Super::NativeConstruct();
if (Btn_Test != nullptr)
{
// 绑定按钮点击事件
Btn_Test->OnClicked.AddDynamic(this, &UTestUI::OnBtn_Test);
}
UE_LOG(LogTemp, Warning, TEXT("NativeConstruct"));
}
void UTestUI::OnBtn_Test()
{
UE_LOG(LogTemp, Warning, TEXT("btn clicked"));
}
到此为止,基础知识部分就 OK 啦。接下来我们继续做一个简单的案例
借助 UMGEditor 创建一个 Image ,点击按钮时切换图片的显示与否。借助上面的知识,具体步骤如下:
- 在 UMG 中创建一个
Image元素,通过查询其 .h 文件,发现类型为UImage。 - 在代码中定义一个
UImage类型的变量并在按钮的回调方法中控制其显示与否:Get/SetVisibility
最终完成后的代码如下
TestUI.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "TestUI.generated.h"
/**
*
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonClickedEventMantra);
UCLASS()
class MYPROJECT_API UTestUI : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
UPROPERTY(Meta = (BindWidget))
class UButton* Btn_Test;
UPROPERTY(Meta = (BindWidget))
class UImage* TestImage;
UFUNCTION()
void OnBtn_Test();
};
TestUI.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestUI.h"
#include "Widgets/SWidget.h"
#include "Components/ContentWidget.h"
#include "Components/Button.h"
#include "Components/Image.h"
void UTestUI::NativeConstruct()
{
Super::NativeConstruct();
if (Btn_Test != nullptr)
{
Btn_Test->OnClicked.AddDynamic(this, &UTestUI::OnBtn_Test);
}
UE_LOG(LogTemp, Warning, TEXT("NativeConstruct"));
}
void UTestUI::OnBtn_Test()
{
UE_LOG(LogTemp, Warning, TEXT("btn clicked"));
if (TestImage->GetVisibility() == ESlateVisibility::Hidden)
{
TestImage->SetVisibility(ESlateVisibility::Visible);
UE_LOG(LogTemp, Warning, TEXT("Image Visible"));
}
else
{
TestImage->SetVisibility(ESlateVisibility::Hidden);
UE_LOG(LogTemp, Warning, TEXT("Image Invisible"));
}
}