`
aigo
  • 浏览: 2645776 次
  • 性别: Icon_minigender_1
  • 来自: 宜昌
社区版块
存档分类
最新评论

[UE4]C++操作UMG Widget实例(demo, example,例子)

UE4 
阅读更多

这里演示在UE4中UMG使用和C++代码操作Widget对象的示例。实现的功能有:

  1. 如何在widget蓝图中制作UI模版;
  2. 如何用C++动态加载资源(UTexture2D);
  3. 当触发widget组件的事件时如何调用C++函数;

 

例子的完整工程(包括所有代码)在文章末尾下载。

这个例子的部分代码参考自UE4官方项目的源码,有兴趣深入研究可以去看那些UI比较丰富的官方项目。

注:当前使用的是最新版本v4.10。下面的代码涉及的API可能在后续版本中变动,注意下。

 

具体步骤:

1,先新建一个空的C++工程



 

 

2,然后我们新建一个自己的PlayerController:工程编辑器中的点击File -》 new C++ class -》 选择Player Controller -》 下一步 -》 起一个类名:MyPlayerController



 

自定义PlayerController是为了添加角色控制的逻辑,每个游戏都有特定的角色(可以是3d人身角色,也可以是一个2d面板),所以一个正常的游戏都会需要添加自己的PlayerController,当前例子没有角色,这里添加PlayerController是为了将后面的UMG widget实例加入到PlayerController中。

 

我们先在PlayerController构造函数中添加一些配置代码,来实现鼠标显示,默认是不显示:

AMyPlayerController::AMyPlayerController()
{
	//显示鼠标
	bShowMouseCursor = true;
	DefaultMouseCursor = EMouseCursor::Crosshairs;

	//启用鼠标事件
	bEnableClickEvents = true;
	bEnableMouseOverEvents = true;
}
 

 

 

3,再GameMode类的构造函数中,设置默认的PlayerController为刚刚我们新建的PlayerController:

 UMGDemoGameMode.h

// Fill out your copyright notice in the Description page of Project Settings.

#include "UMGDemo.h"
#include "UMGDemoGameMode.h"
#include "MyPlayerController.h"


AUMGDemoGameMode::AUMGDemoGameMode()
{
	//使用自定义PlayerController类
	PlayerControllerClass = AMyPlayerController::StaticClass();
}

 

 

4,再新建一个自定义的UBlueprintFunctionLibrary,这个类是一个提供各种通用的静态函数的管理类,我们将在这个类中实现一个获取自定义PlayerController的静态函数。工程编辑器中的点击File -》 new C++ class -》 勾选Show All Classes -》 找到BlueprintFunctionLibrary并选中 -》 下一步 -》 类名我们起为:MyBlueprintFunctionLibrary



 

添加完毕后会自动编译,这里会提示需要在编辑器中重新Compile编译一次,否则编辑器的内容视图中看不到这个文件,这个窗口我们先关掉,后面会统一编译



 

 

5,在“工程名.h”头文件加入我们刚刚新建的BlueprintFunctionLibrary:

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Engine.h"
#include "MyBlueprintFunctionLibrary.h"

 

 

并实现一个静态的函数用于获取自定义PlayerController:

MyBlueprintFunctionLibrary.h

public:
	/** 获取所有PlayerController列表,然后再找到本地客户端的第一个PlayerController并返回 */
	UFUNCTION(BlueprintCallable, Category = "TD_Test Gameplay")
		static APlayerController* GetLocalPlayerController(UObject* WorldContextObject);
	

 

  MyBlueprintFunctionLibrary.cpp

 

APlayerController* UMyBlueprintFunctionLibrary::GetLocalPlayerController(UObject* WorldContextObject)
{
	if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject))
	{
		for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator)
		{
			APlayerController* PlayerController = *Iterator;
			if (PlayerController->IsLocalController())
			{
				// For this project, we will only ever have one local player.
				return PlayerController;
			}
		}
	}
	return nullptr;
}

 

 

6,新建一个自定义UserWidget类,添加的目的是:将widget组件需要调用的C++逻辑都放到这个类中,且只能放到自定义UserWidger类中,如果你想在UMG蓝图中让widget组件调用UserWidget类之外的函数是行不通的。工程编辑器中的点击File -》 new C++ class -》 勾选Show All Classes -》 找到UserWidget并选中 -》 下一步 -》 类名我们起为:MyUserWidget



 
 

添加完以后还无法编译,需求在“工程名.Build.cs”中加入配置,加入红色部分即可:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG", "Slate", "SlateCore" });

 

7,添加Widget蓝图,并将此蓝图的父类指定为刚刚新建的MyUserWidget class。右击内容视图-》用户界面-》Widget蓝图,名字起为:MyWidgetBlueprint



 

 

然后双击打开蓝图,点击Graph(图形)界面 -》 点击 Class settings -》 找到Parent Class -》 选择我们刚刚新建的自定义UserWidget类。(如果找不到自定义UserWidget,在编辑器中重新编译下代码,实在不行重启下编辑器)



 



 

 

8,再回到Designer界面,从左侧的组件列表中,我们拖拽几个组件进来,分别是:Button,Overlay,Image。层级关系是:Button 《 Overlay 《 Image



 

后面会实现这样的一个功能:给定两张图片(Texture2d),每点击一次,button的图片变一次。这个功能其实也可以用存蓝图脚本实现,这里为了演示蓝图调用C++函数,我们把替换图片的逻辑放在C++代码中。官方文档说,对于调用非常频繁的逻辑,若要提高性能,建议用C++实现而不要在蓝图中实现。

 

先修改widget组件的一些参数:

设置设置button的大小,因为我们使用的图片大小是205X115,所以button的大小也这样设置



 

 

属性Style中Normal、Hovered、Pressed中的Draw As都修改为None,因为按钮的图片我们准备设置在Image组件当中。之所以不直接在button中设置背景图片,是因为仅仅依靠一个button组件,无法只用一张图片就实现按钮下压的效果,这里我们想只用一张图片来实现按钮下压的效果,所以除了button,还用了Overlay和Image两种组件



 

 

然后再设置下Image组建的size,设置为图片一样大小:205x115。



 

 

控制button按下后图片浮动的上下左右距离,在Style下的Pressed Padding中设置,如果想要浮动的效果明显一点,根据需要设置大一下:



 

 

到此我们把UMG widget蓝图模版绘制完成,接下来我们用C++代码操作这些widget组件。

 

9,首先我们在GameMode类中重载父类的BeginPlay()函数

/** Called when the game starts. */
virtual void BeginPlay() override;

 

同时在GameMode内定义了几个函数:

//创建widget对象并添加到Viewport

void CreateWidgetInstance();

//获取需要操作数据的widget对象

void FindImageComponents();

//动态加载icon所需Texture2D

void LoadAssetsDynamic();

 

其中CreateWidgetInstance()函数用来创建Widget实例对象(以之前的MyUserWidgetBlueprint为模版),并添加到游戏的Viewport中;

FindImageComponents()用来获取创建的widget实例中的Image组件的引用,因为我们后面要用代码操作这个Image组件,所以先把这个组件的指针存起来,不用每次操作时再查找;

LoadAssetsDynamic()用来加载Texture资源。我们对Image组件更换图片时,实际是对Texture的更换,所以,进入游戏时我们就先加载到内存中,等后面点击button的时候,直接修改Image组件中对这些Texture内存的引用,来实现图片切换。

具体代码如下:

UMGDemoGameMode.h

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/GameMode.h"
#include "Blueprint/UserWidget.h"
#include "Runtime/UMG/Public/Components/Image.h"
#include "UMGDemoGameMode.generated.h"

/**
 * 
 */
UCLASS()
class UMGDEMO_API AUMGDemoGameMode : public AGameMode
{
	GENERATED_BODY()
	
	
public:

	AUMGDemoGameMode();
	
	/** Called when the game starts. */
	virtual void BeginPlay() override;

private:
	UImage* imageHero;

	UTexture2D* texHero1;
	UTexture2D* texHero2;

	UTexture2D* defaultTexHero;

protected:

	/** The widget class we will use as our game over screen when the player wins. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Game")
		TSubclassOf<UUserWidget> MyWidgetClass;

	/** The widget instance that we are using as our menu. */
	UPROPERTY()
		UUserWidget*  MyWidgetInstance;

	//创建widget对象并添加到Viewport
	void CreateWidgetInstance();

	//获取需要操作数据的widget对象
	void FindImageComponents();

	//动态加载icon所需Texture2D
	void LoadAssetsDynamic();

public:

	//修改icon图片:切换英雄时调用
	void ChangeHeroIcon();
};
 

 

UMGDemoGameMode.cpp

 下面代码中的实现思路有点不科学,因为把很多UI相关的操作放在GameMode内部。建议参考新文章中的方法:http://aigo.iteye.com/blog/2296218

// Fill out your copyright notice in the Description page of Project Settings.

#include "UMGDemo.h"
#include "UMGDemoGameMode.h"
#include "MyPlayerController.h"


AUMGDemoGameMode::AUMGDemoGameMode()
{
	//使用自定义PlayerController类
	PlayerControllerClass = AMyPlayerController::StaticClass();

	imageHero = NULL;
	texHero1 = NULL;
	texHero2 = NULL;
	defaultTexHero = NULL;
}

void AUMGDemoGameMode::BeginPlay()
{
	Super::BeginPlay();

	UMyBlueprintFunctionLibrary::SetGameModeInstance(this);
	CreateWidgetInstance();
	FindImageComponents();
	LoadAssetsDynamic();

	ChangeHeroIcon();
}

//创建widget对象并添加到Viewport
void AUMGDemoGameMode::CreateWidgetInstance()
{
	if (MyWidgetInstance)
	{
		MyWidgetInstance->RemoveFromViewport();
		MyWidgetInstance = nullptr;
	}
	if (MyWidgetClass)
	{
		if (AMyPlayerController* PC = Cast<AMyPlayerController>(UMyBlueprintFunctionLibrary::GetLocalPlayerController(this)))
		{
			MyWidgetInstance = CreateWidget<UUserWidget>(PC, MyWidgetClass);
			if (MyWidgetInstance)
			{
				MyWidgetInstance->AddToViewport();
			}
		}
	}
}

//获取需要操作数据的widget对象
void AUMGDemoGameMode::FindImageComponents()
{
	if (UCanvasPanel* CanvasPanelWidget = Cast<UCanvasPanel>(MyWidgetInstance->GetRootWidget()))
	{
		//获取英雄头像的UImage指针
		if (UButton* btn = Cast<UButton>(CanvasPanelWidget->GetChildAt(0)))
		{
			if (UOverlay* overlay = Cast<UOverlay>(btn->GetChildAt(0)))
			{
				if (UImage* image = Cast<UImage>(overlay->GetChildAt(0)))
				{
					imageHero = image;
				}
			}
		}
	}
}

//动态加载icon所需Texture2D
void AUMGDemoGameMode::LoadAssetsDynamic()
{
	//加载英雄头像的Texture
	if (texHero1)
	{
		//如果已经加载过,则先销毁掉
		texHero1->ConditionalBeginDestroy();
		texHero1 = NULL;
		GetWorld()->ForceGarbageCollection(true);
	}
	texHero1 = Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("Texture2D'/Game/Textures/hero1.hero1'")));

	if (texHero2)
	{
		texHero2->ConditionalBeginDestroy();
		texHero2 = NULL;
		GetWorld()->ForceGarbageCollection(true);
	}
	texHero2 = Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), NULL, TEXT("Texture2D'/Game/Textures/hero2.hero2'")));

	defaultTexHero = texHero2;
}

void AUMGDemoGameMode::ChangeHeroIcon()
{
	if (!defaultTexHero || !texHero1 || !texHero1 || !imageHero)
	{
		return;
	}

	defaultTexHero = defaultTexHero == texHero1 ? texHero2 : texHero1;

	imageHero->SetBrushFromTexture(defaultTexHero);
}
 

 

上面代码涉及到一些Widget组件的变量,需要在"工程名.h"中加入以下头文件(后五行)才能编译通过:
UMGDemo.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Engine.h"
#include "MyBlueprintFunctionLibrary.h"

#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "Runtime/UMG/Public/Slate/SObjectWidget.h"
#include "Runtime/UMG/Public/IUMGModule.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"
 

 

10,在UMyUserWidget类中添加一个蓝图函数,在这个函数内调用GameMode的ChangeHeroIcon()函数。这里的宏定义关键字使用了“BlueprintCallable”,表示蓝图中可以调用的函数

MyUserWidget.h

 

public:

	//暴露给蓝图调用的函数,处理Button的OnClicked事件
	UFUNCTION(BlueprintCallable, Category = "WidgetFunction")
		void handleMyButtonClick();
 

 

 

MyUserWidget.cpp

 

void UMyUserWidget::handleMyButtonClick()
{
	AUMGDemoGameMode* GameModePtr = UMyBlueprintFunctionLibrary::GetGameModeInstance();
	GameModePtr->ChangeHeroIcon();
}
 

 

 

11,在Widget蓝图中添加鼠标点击事件OnClicked



 

然后这OnClicked节点中拖出一个连线,输入函数名handleMyButtonClick,即可看到这个函数(如果看不到需要重新编译下,前提时上面第7个步骤执行过),并选择这个函数



 

 

 12,建立一个GameMode蓝图。

因为我们再GameMode代码中定义了一个MyWidgetClass成员变量,这个变量类型是一个Widget蓝图模版,要对这个变量赋初值,需要再蓝图脚本中设置(可能也有代码方式来执行对蓝图模版类型的变量赋初值的方法,但我还没找到相关资料)。

操作方式是:右击GameMode class,选择创建GameMode蓝图,名字起为:MyUMGDemoGameMode_BP



 

 

建好以后打开GameMode蓝图,然后找到C++代码里面定义的那个成员变量MyWidgetClass,然后选择我们之前创建的Widget蓝图MyWidgetBlueprint。(之前遇到一个很诡异的问题,在GameMode蓝图中设置好MyWidgetClass变量的初始值以后没有生效,需要这个初始值清空后重新设置一遍,或者在菜单Settings -》 World Settings下面设置下默认的GameMode



 

 

13,最后,设置默认GameMode为这个GameMode蓝图,菜单Edit -》Project Settings -》 Project -> Maps & Modes -》 Default Modes -》 Default GameMode:



 

 

 

最终效果如下图,每当点击一次按钮,按钮的图片就会切换一次



 

 

此实例的完成工程下载为UMGDemo.rar附件

再给一个备用的站外链接:

http://pan.baidu.com/s/1cw7HjK

  • 大小: 156.3 KB
  • 大小: 127 KB
  • 大小: 113.8 KB
  • 大小: 60.8 KB
  • 大小: 94.1 KB
  • 大小: 42 KB
  • 大小: 106 KB
  • 大小: 86.8 KB
  • 大小: 55.1 KB
  • 大小: 58.1 KB
  • 大小: 76.5 KB
  • 大小: 67.2 KB
  • 大小: 82.3 KB
  • 大小: 61.7 KB
  • 大小: 78.5 KB
  • 大小: 58.9 KB
  • 大小: 79.3 KB
  • 大小: 37.4 KB
  • 大小: 88.3 KB
分享到:
评论

相关推荐

    UE4 UI简单例子 C++

    在UE4(Unreal Engine 4)中,UI系统基于UMG(Unreal Motion Graphics),它提供了一种可视化的方式去创建用户界面,并且可以与C++代码深度集成。本示例"UE4 UI简单例子 C++"展示了如何利用C++编程语言在UE4中创建一...

    UE4 CanvasUI遮罩

    在UE4中,CanvasUI基于UMG(Unreal Motion Graphics),这是一个强大的2D UI设计工具,允许通过直观的可视化编辑器创建复杂的用户界面。Canvas是UMG中的一个基本元素,它是一个二维画布,可以添加各种UI组件,如文本...

    UMG图表控件+UE5插件+曲线图、饼图、环状图、柱状图

    这款插件的特点在于它完全基于UMG(User Interface Meta Language)实现,意味着无需借助WebBrowser或者WebUI嵌套,而是通过纯C++编程语言以及蓝图系统来构建。这使得图表的集成更为流畅,性能更佳,同时也便于开发者...

    UE5 蓝图 UMG实现日历系统

    UE5 蓝图 UMG实现日历系统.

    MyColorPicker4.22.zip——UE4拾色器插件

    通过C++的高效性和对UMG及蓝图系统的全面支持,它提升了UE4项目的色彩管理体验,使得游戏的视觉效果和用户交互得以进一步优化。对于那些致力于创造丰富多彩游戏世界的UE4开发者来说,这是一个不可多得的工具。

    UE4 跑马灯文本

    UE4 UMG 跑马灯字幕效果 大概的思路就是将Text 放置到ScrollBox 中 通过tick 不断移动scroll 的Offset 达到Text文本自动滚动的效果 唯一有点难度的就是判断scroll 是否滚动到末尾 然后从头播放 蓝图就是

    UE5 UMG滚动框可从前从后添加新UI思路

    UE5 UMG滚动框可从前从后添加新UI思路

    UE4-InventorySystem:一个用 C++ 编写的基本库存系统,结合了 Unreal Engine 4.6 的 UMG

    本项目“UE4-InventorySystem”提供了一个基于C++实现的基础库存系统,它充分利用了UE4.6版本中的用户界面(UI)框架——UMG(Unreal Motion Graphics)。下面将详细介绍这个库存系统的实现原理和关键知识点。 1. *...

    UE4插件VictoryPlugin-master

    《UE4插件VictoryPlugin-master深度解析:通过路径加载图片技术详解》 在Unreal Engine 4(简称UE4)的开发过程中,插件扮演着至关重要的角色,它们能够扩展游戏引擎的功能,提高开发效率。VictoryPlugin是UE4的一...

    UE4文件拖拽窗口插件

    在UE4(Unreal Engine 4)中,文件拖拽窗口插件是一种增强用户交互功能的工具,允许用户通过简单的拖放操作将外部文件导入到游戏引擎中。这种功能对于内容创作者、开发者以及测试者来说非常实用,因为它提高了工作...

    UE4外发光材质

    UE4后处理材质资源,描边高亮效果。UE4后处理材质资源,描边高亮效果。

    UE4(虚幻4)制作跑酷小游戏

    UE4的UMG(Unreal Motion Graphics)系统允许创建自定义的2D和3D界面组件。 6. **音效与音乐**:合适的音效和背景音乐可以极大地提升游戏氛围。UE4支持导入音频文件,并通过事件驱动的方式在特定时刻播放。 7. **...

    UE4 局域网多人联机项目

    UE4局域网多人联机,其中包含背包系统,动画系统,角色生成系统,计时器,敌人生成系统,UI系统,救人系统(模仿吃鸡救人),子弹后坐力系统,十字准心位置,掉血系统,伤害系统,UMG界面布局系统,一套完整的局域网...

    RenderWidgetToTarget:用于将UMG小部件呈现到RenderTarget的插件

    而RenderTarget是UE4中的一种特殊纹理,可以捕获场景的渲染结果,也可以作为其他渲染操作的目标。 该插件的核心功能是将UMG小部件的渲染输出导向一个特定的RenderTarget。这通常涉及到以下几个关键步骤: 1. 创建和...

    ue-gjapi-umg:用于与GameJoltAPI交互的UMG小部件

    《UE-GJAPI-UMG:游戏开发中的GameJolt API交互工具》 在Unreal Engine的游戏开发过程中...在实际应用中,开发者需要结合UE4、UMG、C++以及GameJolt API的知识,才能充分发挥其潜力,打造出更加生动和互动的游戏世界。

    KantanCharts:UE4 插件提供简单的 UMGSlate 图表绘制

    《KantanCharts:UE4插件轻松实现UMG Slate图表绘制》 KantanCharts是一款针对Unreal Engine 4(简称UE4)开发的...然而,为了充分利用其功能,开发者需要对UE4的UMG系统有一定了解,并且熟悉C++或蓝图的基本操作。

    Learning C++ by Building Games with Unreal Engine 4-2nd-20190701.epub

    Learning C++ by Building Games with Unreal Engine 4: A beginner’s guide to learning 3D game development with C++ and UE4, 2nd Edition by Sharan Volin--July 1, 2019 English | 2018 | ISBN: 1788476249 |...

    UE4_TinyGUI.zip

    UE4使用蓝prints和C++来创建UI元素,如按钮、文本框等,并通过UMG(User Made Graphics)系统进行布局管理。UMG允许开发者以可视化的方式创建和组织UI组件,但其灵活性和扩展性在面对大型项目时可能会显得不足。这就...

    UE4赛车游戏源码

    UE4赛车游戏源码,使用虚幻4引擎C++、蓝图、UMG等开发...

    UE4多关卡切换_详细讲解案例.doc

    本文将详细介绍如何在UE4中实现多关卡之间的流畅切换,以及如何与UMG(Unreal Motion Graphics)结合以创建丰富的用户界面。 首先,我们要了解在UE4中,流关卡(Streaming Level)的概念。流关卡允许游戏在运行时...

Global site tag (gtag.js) - Google Analytics