UMG在UEC++的使用

参考文章:

【虚幻4】UE4初学者系列教程合集-全中文新手入门教程

UMG使用实例(C++)

UEC++如何绑定UI事件

UMG在UEC++中的基本使用与UI的拖拽事件

UE5 Enhanced Input 输入增强系统学习

UE5增强输入系统实现组合按键

UMG按钮事件实现

UI绑定

在C++中设置UMG的样式并不方便,我们选择在蓝图中设置样式,然后在C++中实现方法

创建一个继承于UserWidget的C++类和蓝图类

image-20250709113653007

然后根据创建好的C++类,生成一个对应的蓝图类

image-20250709113816293

[!Warning]

在蓝图类中编辑好对应的样式,注意左侧按钮的命名,要与在C++中的变量名相同

image-20250709114035367

MyUserWidget.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public:

// 定时器句柄
FTimerHandle TimerHandle;

UPROPERTY(meta=(BindWidget))
class UButton* Button_Start;

UPROPERTY(meta=(BindWidget))
class UButton* Button_Set;

UPROPERTY(meta=(BindWidget))
class UButton* Button_Quit;

virtual void NativeConstruct() override;

UFUNCTION()
void Delay();

UFUNCTION()
void OnClickedStart();

UFUNCTION()
void OnClickedQuit();

class AAStar_test2Character* player;
class APlayerController* FirstPlayerController;

在cpp中绑定按钮的点击事件UMyUserWidget.cpp

1
2
3
4
5
6
7
void UMyUserWidget::NativeConstruct()
{
Super::NativeConstruct();

Button_Start->OnClicked.AddDynamic(this, &UMyUserWidget::OnClickedStart);
Button_Quit->OnClicked.AddDynamic(this, &UMyUserWidget::OnClickedQuit);
}

按钮的点击方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void UMyUserWidget::OnClickedStart()
{
RemoveFromParent();

// find player who`s type is AAStar_test2Character
player = nullptr;
for (TActorIterator<AAStar_test2Character> It(GetWorld()); It; ++It)
{
player = *It;

// 返回true,是同一个类型
if (player->IsA(AAStar_test2Character::StaticClass())) UE_LOG(LogTemp, Warning, TEXT("是同一个类型,%s"),*player->GetName());

if (player && player->GetName().Contains("BP_TopDownCharacter"))
{
UE_LOG(LogTemp, Warning, TEXT("Found BP_TopDownCharacter: %s"), *player->GetName());
break;
}
}
if (!player)
{
UE_LOG(LogTemp, Warning, TEXT("未找到名为 BP_TopDownCharacter 的 Actor"));
}
UE_LOG(LogTemp, Warning, TEXT("the type of player is : %s"), *player->GetName());
// getPlayerController
FirstPlayerController = GetWorld()->GetFirstPlayerController();

FirstPlayerController->SetViewTargetWithBlend(player, 1.0f);

GetWorld()->GetTimerManager().SetTimer(TimerHandle,this,&UMyUserWidget::Delay,1.0f,false);

}

void UMyUserWidget::Delay()
{
UE_LOG(LogTemp, Log, TEXT("delay 1 second"));
FirstPlayerController->Possess(player);
FirstPlayerController->SetShowMouseCursor(true);
}


void UMyUserWidget::OnClickedQuit()
{
UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
}

在关卡蓝图中设置UMG的出现

image-20250709114434619

设置开始的摄像头,创建一个pawn类型的蓝图,然后在蓝图中仅放置一个摄像机,放置在直接中,设置posses为player0即可。参考【虚幻4】UE4初学者系列教程合集-全中文新手入门教程

实现效果

video_20250709_114940_edit

UI动画和暂停游戏

与个UMG同理,创建一个继承自 UserWidget的C++类,并生成对应的蓝图类

image-20250710164930325

暂停UMG的逻辑实现

Pause.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定时器句柄
FTimerHandle TimerHandle;

// 绑定按钮
UPROPERTY(meta=(BindWidget))
class UButton* Quit;

// 绑定按钮
UPROPERTY(meta=(BindWidget))
class UButton* Continue;

// 绑定动画
UPROPERTY(meta = (BindWidgetAnim), Transient)
class UWidgetAnimation* Appear;

virtual void NativeConstruct() override;

UFUNCTION()
void OnClickedContinue();

UFUNCTION()
void OnClickedQQuit();

UFUNCTION()
void PlayTheAnimation();

UFUNCTION()
void PauseDelay();

class AAStar_test2Character* player;
class APlayerController* FirstPlayerController;

Pause.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void UPause::NativeConstruct()
{
Super::NativeConstruct();


if (Continue)
Continue -> OnClicked.AddDynamic(this, &UPause::OnClickedContinue);
else UE_LOG(LogTemp, Log, TEXT("Continue is not alive"));

if (Quit)
Quit -> OnClicked.AddDynamic(this, &UPause::OnClickedQQuit);
else UE_LOG(LogTemp, Log, TEXT("Quit is not alive"));



}

void UPause::OnClickedContinue()
{
UE_LOG(LogTemp, Warning, TEXT("触发OnClickedContinue"));
FirstPlayerController = GetWorld()->GetFirstPlayerController();
// FirstPlayerController -> SetShowMouseCursor(false);
FirstPlayerController -> SetInputMode(FInputModeGameOnly());
UGameplayStatics::SetGamePaused(GetWorld(),false);
PlayAnimation(Appear, 0.0f, 1, EUMGSequencePlayMode::Reverse, 0.75f, true);
GetWorld()->GetTimerManager().SetTimer(TimerHandle,this,&UPause::PauseDelay,0.75f,false);

}

void UPause::PauseDelay()
{
SetVisibility(ESlateVisibility::Collapsed);
StopAnimation(Appear);
}

void UPause::PlayTheAnimation()
{
// InAnimation, StartAtTime = 0.0f,NumLoopsToPlay = 1, EUMGSequencePlayMode::Type PlayMode = EUMGSequencePlayMode::Forward, float PlaybackSpeed = 1.0f, bool bRestoreState = false)
PlayAnimation(Appear, 0.0f, 1, EUMGSequencePlayMode::Forward, 0.75f, true);
}



void UPause::OnClickedQQuit()
{
UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
}

在playerController中设置相关事件

主要包括Widget的初始化和键盘事件的绑定和触发

PlayerController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	// 绑定按钮使用
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* SetInputQAction;

// 创建Widget使用
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TSubclassOf<class UPause> PauseWidgetClass;

UPROPERTY()
class UPause* PauseWidget;


private:

// 设置初始化Widget
void SetupPauseWidget();

// 点击事件触发
void OnPause();

PlayerController.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
void AAStar_test2PlayerController::BeginPlay()
{
// Call the base class
Super::BeginPlay();

SetupPauseWidget();
}



void AAStar_test2PlayerController::SetupPauseWidget()
{
if (PauseWidgetClass)
{
UE_LOG(LogTemp, Warning, TEXT("PauseWidgetClass存在"));
PauseWidget = CreateWidget<UPause>(this, PauseWidgetClass);
if (PauseWidget)
{
UE_LOG(LogTemp, Warning, TEXT("PauseWidget存在"));
PauseWidget->AddToViewport();
PauseWidget->SetVisibility(ESlateVisibility::Collapsed);
}
}
}

void AAStar_test2PlayerController::OnPause()
{
UE_LOG(LogTemp, Warning, TEXT("按Q,开始执行Pause"));
bool isPlaying = PauseWidget -> IsAnimationPlaying(PauseWidget->Appear);
if (!isPlaying)
{
UE_LOG(LogTemp, Warning, TEXT("没有播放动画,走动画"));
UE_LOG(LogTemp, Warning, TEXT("%s"), *PauseWidget->GetName());
PauseWidget -> SetVisibility(ESlateVisibility::Visible);
PauseWidget -> PlayTheAnimation();
APlayerController* FirstPlayerController = GetWorld()->GetFirstPlayerController();
// FirstPlayerController->SetShowMouseCursor(true);
FirstPlayerController->SetInputMode(FInputModeUIOnly());
UGameplayStatics::SetGamePaused(GetWorld(),true);
}
}

// 绑定暂停键 Q
void AAStar_test2PlayerController::SetupInputComponent()
{
// set up gameplay key bindings
Super::SetupInputComponent();

// Add Input Mapping Context
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}

// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent))
{
// 绑定暂停键 Q
EnhancedInputComponent->BindAction(SetInputQAction, ETriggerEvent::Started, this, &AAStar_test2PlayerController::OnPause);

}
else
{
UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input Component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
}
}

蓝图上的设置

UI界面的设置参考:https://www.bilibili.com/video/BV164411Y732?spm_id_from=333.788.videopod.episodes&vd_source=6d070e5f2a4f28fd128440195fce3d9a&p=63

因为使用的是UE5的增强输入,故按键的绑定事件需要额外添加蓝图文件

首先新建一个输入操作

image-20250710170215022

找到对应的全局InputMappingContext(IMC),在里面添加新的操作

image-20250710170342023

最后绑定输入操作到IMC中,在PlayerController的细节面板中,绑定即可。同时我这里绑定了在PlayerController.h中创建的TSubclassOf PauseWidgetClass;

image-20250710170629377

实现效果

2025-07-10 17-14-52