【转载】用代码控制 UMG

1,046 阅读4分钟

原文链接:【小功能】用代码控制UMG | Mantra

正文

对于真正的程序员来说,相比于蓝图还是 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 中进行操作。

image.png

之后需要在 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"));
	}

}

动画123.gif