9. UE5 RPG创建UI(下)

news2024/11/25 20:32:36

在上一篇文章里,制作了显示血量和蓝量的ui,并且还将ui和获取数据使用的控制器层创建出来并初始化成功。现在只有主用户控件上面被添加了控制器层,还未给每个用户控件赋予控制器层。接下来要实现对属性的广播功能,在属性值变化的时候,能够在蓝图中获取到数值的变化并更新到用户控件上面。

使用广播设置初始值

首先在控制器层里面添加一个函数

virtual void BroadcastInitialValues();

这个函数用于广播初始化函数。我们上一文章里基于控制器层类创建了一个子类,专门用于Overlay的,在里面复写这个函数

public:
	virtual void BroadcastInitialValues() override;

接下来就是重点,我们将以委托的形式设置每个属性的广播功能,这里就需要用到委托对应的函数。不清楚的小伙伴可以看下这里
一文理解透UE委托Delegate

这里使用了动态多播委托函数绑定,下面是实现,委托的函数DelegateName 首字母必须以F开头。
一下是定义了获取血量的委托,返回一个参数就是当前的血量浮点数。

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangedSignature,float, NewHealth);

接下来定义一个变量,实现对回调的引用

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnHealthChangedSignature FOnHealthChanged;

然后最后通过广播形式将数据广播出去

FOnHealthChanged.Broadcast(50.f);

当前,我们需要实际的血量数值,所以广播出去的就是从AttributeSet里面获取到的值

	const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);

	OnHealthChanged.Broadcast(AttributeSetBase->GetHealth());
	OnMaxHealthChanged.Broadcast(AttributeSetBase->GetMaxHealth());

函数有了,需要有个地方调用,实例化这个函数我们是在HUD类里面实例化的,那就在实例化完成后面调用。也就是MyHUD.cpp里面的InitOverlay()里面,这个顺序不能错,首先需要设置控制器层,这时会触发蓝图里面可以调用的设置控制器层回调通知,用户控件会以此来绑定广播回调。然后再广播初始的值,用户控件里就实现了对值的初始化设置。

	OverlayWidgetController = GetOverlayWidgetController(WidgetControllerParams); //获取控制器层

	OverlayWidget->SetWidgetController(OverlayWidgetController); //设置用户控件的控制器层
	OverlayWidgetController->BroadcastInitialValues(); //初始化广播

这样设置完成以后,打开UE,在设置的WBP_Overlay里面,在上一章最后设置的设置完成控制器层对象后的回调中,将控制器层对象传递给需要显示的用户控件节点。
在这里插入图片描述
做到这一步,我们在需要同步数据的UI上面就都可以获取到控制器层对象。接着,我们要将Overlay的控制器层类公开给蓝图。

UCLASS(BlueprintType, Blueprintable)
class AURA_API UOverlayWidgetController : public UMyWidgetController

直接在类上面的UCLASS()内添加上两个参数。
BlueprintType 将此类公开为可用于蓝图中的变量的类型。
Blueprintable 将此类公开为用于创建蓝图的可接受基类。默认为NotBlueprintable,除非继承时就并非如此。此说明符由子类继承。
设置完成这两项,我们就可以把控制器层对象转换为控制器层类的实例了。
为了方便后面的工作,推荐不要直接使用c++的类,通过蓝图创建一个c++的基类去使用。

在这里插入图片描述
将HUD里面的控制器层类的引用修改成蓝图的。
在这里插入图片描述
在进度条的蓝图基类WBP_GlobeProgressBar中添加一个函数,用于设置进度条百分比,接收一个参数
在这里插入图片描述
之前在OverlayWidget里面,将在c++里面生成的控制器层对象传递给了对应的用户组件。我们在血量控件中,也可以使用WidgetControllerSet通知,获得通知后将变量保存。
在这里插入图片描述
之前我们在OverlayWidgetController里面添加了对血量和最大血量的广播,所以,在蓝图里,我们可以实现绑定血量变化。在数值变化后,可以通过调用设置进度条百分比进行进度条设置。
在这里插入图片描述
顺便也绑定最大血量。
在这里插入图片描述

监听数值的变化

上面我们实现通过广播的形式对用户控件内的值进行初始化,还未实现对数值变化后触发广播更新ui显示。
我们需要通过使用AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate()来注册一个监听一个值的变化,如果监听的数值变化了接着将变化后的数值进行广播。比如下面就是监听血量属性是否变化

AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute())

为了区分开,我们需要在控制器层类里面新加一个函数,专门用来注册监听数据变化回调的。

virtual void BindCallbacksToDependencies() override;

接着创建两个保护类型的函数,用于监听到数值变化后的回调函数

protected:
	void HealthChanged(const FOnAttributeChangeData& Data) const;
	void MaxHealthChanged(const FOnAttributeChangeData& Data) const;

接着,在绑定监听函数内实现监听:

void UOverlayWidgetController::BindCallbacksToDependencies()
{
	const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);

	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
		AttributeSetBase->GetHealthAttribute()).AddUObject(this, &UOverlayWidgetController::HealthChanged);

	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
		AttributeSetBase->GetMaxHealthAttribute()).AddUObject(this, &UOverlayWidgetController::MaxHealthChanged);
	
}

在数值变化时,就会触发去调用Changed函数,我们只需要在changed函数内,将数值广播给蓝图即可。

void UOverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
{
	OnHealthChanged.Broadcast(Data.NewValue);
}

void UOverlayWidgetController::MaxHealthChanged(const FOnAttributeChangeData& Data) const
{
	OnMaxHealthChanged.Broadcast(Data.NewValue);
}

最后,需要找一个地方调用监听函数绑定,我选在了HUD对初始化数值调用之后

	OverlayWidget->SetWidgetController(OverlayWidgetController); //设置用户控件的控制器层
	OverlayWidgetController->BroadcastInitialValues(); //初始化广播的值
	OverlayWidgetController->BindCallbacksToDependencies(); //绑定监听数值变化

接下来,按照之前的方式将蓝的设置添加上,这里不在编写实现,因为和血量的方式一行按照上面一步步来即可。下面列一下代码。

控制器层代码

MyWidgetController.h

// 版权归暮志未晚所有。

#pragma once

#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "UObject/NoExportTypes.h"
#include "MyWidgetController.generated.h"

class UAttributeSet;
class UAbilitySystemComponent;

/**
 * 生成用户控件控制器的结构体,需要四项内容去生成。
 */
USTRUCT(BlueprintType)
struct FWidgetControllerParams
{
	GENERATED_BODY()
	FWidgetControllerParams(){}
	FWidgetControllerParams(APlayerController* PC, APlayerState* PS, UAbilitySystemComponent* ASC, UAttributeSet* AS)
	: PlayerController(PC),
	PlayerState(PS),
	AbilitySystemComponent(ASC),
	AttributeSet(AS)
	{}

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<APlayerController> PlayerController = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<APlayerState> PlayerState = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UAttributeSet> AttributeSet = nullptr;
};

/**
 * 用户控件控制器层,用户控件可从控制器层更新数据显示,以及为控制器层提供输入。
 */
UCLASS()
class AURA_API UMyWidgetController : public UObject
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable)
	void SetWidgetControllerParams(const FWidgetControllerParams& WCParams);
	virtual void BroadcastInitialValues(); //广播初始化的值
	virtual void BindCallbacksToDependencies(); //绑定数值变动后回调的广播

protected:

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<APlayerController> PlayerController;

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<APlayerState> PlayerState;

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;

	UPROPERTY(BlueprintReadOnly, Category="WidgetController")
	TObjectPtr<UAttributeSet> AttributeSet;
	
};

MyWidgetController.cpp

// 版权归暮志未晚所有。


#include "UI/WidgetController/MyWidgetController.h"

void UMyWidgetController::SetWidgetControllerParams(const FWidgetControllerParams& WCParams)
{
	PlayerController = WCParams.PlayerController;
	PlayerState = WCParams.PlayerState;
	AbilitySystemComponent = WCParams.AbilitySystemComponent;
	AttributeSet = WCParams.AttributeSet;
}

void UMyWidgetController::BroadcastInitialValues()
{
}

void UMyWidgetController::BindCallbacksToDependencies()
{
}

OverlayWidgetController.h

// 版权归暮志未晚所有。

#pragma once

#include "CoreMinimal.h"
#include "UI/WidgetController/MyWidgetController.h"
#include "OverlayWidgetController.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangedSignature,float, NewHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMaxHealthChangedSignature,float, NewMaxHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnManaChangedSignature,float, NewMana);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMaxManaChangedSignature,float, NewMaxMana);

/**
 * 屏幕覆盖用户控件控制器层基类,继承与用户控件控制器
 */
UCLASS(BlueprintType, Blueprintable)
class AURA_API UOverlayWidgetController : public UMyWidgetController
{
	GENERATED_BODY()

public:
	virtual void BroadcastInitialValues() override;
	virtual void BindCallbacksToDependencies() override;

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnHealthChangedSignature OnHealthChanged;

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnMaxHealthChangedSignature OnMaxHealthChanged;

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnManaChangedSignature OnManaChanged;

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnMaxManaChangedSignature OnMaxManaChanged;

protected:
	void HealthChanged(const FOnAttributeChangeData& Data) const;
	void MaxHealthChanged(const FOnAttributeChangeData& Data) const;
	void ManaChanged(const FOnAttributeChangeData& Data) const;
	void MaxManaChanged(const FOnAttributeChangeData& Data) const;
};

OverlayWidgetController.cpp

// 版权归暮志未晚所有。


#include "UI/WidgetController/OverlayWidgetController.h"

#include "AbilitySystem/AttributeSetBase.h"

void UOverlayWidgetController::BroadcastInitialValues()
{
	const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);

	OnHealthChanged.Broadcast(AttributeSetBase->GetHealth());
	OnMaxHealthChanged.Broadcast(AttributeSetBase->GetMaxHealth());
	OnManaChanged.Broadcast(AttributeSetBase->GetMana());
	OnMaxManaChanged.Broadcast(AttributeSetBase->GetMaxMana());
}

void UOverlayWidgetController::BindCallbacksToDependencies()
{
	const UAttributeSetBase* AttributeSetBase = CastChecked<UAttributeSetBase>(AttributeSet);

	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
		AttributeSetBase->GetHealthAttribute()).AddUObject(this, &UOverlayWidgetController::HealthChanged);

	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
	AttributeSetBase->GetMaxHealthAttribute()).AddUObject(this, &UOverlayWidgetController::MaxHealthChanged);

	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
		AttributeSetBase->GetManaAttribute()).AddUObject(this, &UOverlayWidgetController::ManaChanged);

	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(
		AttributeSetBase->GetMaxManaAttribute()).AddUObject(this, &UOverlayWidgetController::MaxManaChanged);
	
}

void UOverlayWidgetController::HealthChanged(const FOnAttributeChangeData& Data) const
{
	OnHealthChanged.Broadcast(Data.NewValue);
}

void UOverlayWidgetController::MaxHealthChanged(const FOnAttributeChangeData& Data) const
{
	OnMaxHealthChanged.Broadcast(Data.NewValue);
}

void UOverlayWidgetController::ManaChanged(const FOnAttributeChangeData& Data) const
{
	OnManaChanged.Broadcast(Data.NewValue);
}

void UOverlayWidgetController::MaxManaChanged(const FOnAttributeChangeData& Data) const
{
	OnMaxManaChanged.Broadcast(Data.NewValue);
}



多人模式调试UI显示

在运行下拉中,将人数修改为2
在这里插入图片描述
然后将客户端同时作为服务器使用
在这里插入图片描述
接着会发现运行了两个实例,并且拥有相同的血量和蓝量
在这里插入图片描述
接下来,控制一个角色去吃掉药瓶,会发现她的血和蓝都产生了变化,而另外一个没有变化
在这里插入图片描述
到这里功能实现了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1405421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

时限挑战——深度解析Pytest插件 pytest-timeout

在软件开发中,测试用例的执行时间通常是一个关键考虑因素。Pytest插件 pytest-timeout 提供了一个强大的插件,允许你设置测试用例的超时时间。本文将深入介绍 pytest-timeout 插件的基本用法和实际案例,助你精确掌控测试用例的执行时限。 什么是pytest-timeout? pytest-tim…

实现AVL树

王有志&#xff0c;一个分享硬核Java技术的互金摸鱼侠 加入Java人的提桶跑路群&#xff1a;共同富裕的Java人 上一篇我们学习了平衡二分搜索树的理论知识&#xff0c;并学习了AVL树是如何保持二分搜索树的平衡的&#xff0c;今天我们一起来实现AVL树。Tips&#xff1a; AVL树和…

操作系统-进程控制(如何实现进程控制 如何实现原子性 相关进程控制原语)

文章目录 什么是进程控制总览如何实现进程控制&#xff1f;如何实现原语的“原子性”&#xff1f;进程控制相关的原语创建原语撤销原语子进程与父进程阻塞与唤醒原语切换原语 小结 什么是进程控制 控制进程的状态变换 总览 如何实现进程控制&#xff1f; 原语实现 假设不是原…

使用DockerFile构建镜像与镜像上传

目录 前言&#xff1a;为什么要使用Dockerfile &#xff1f; DockerFile构建镜像 1、构建基础对象 2、Dockerfile文件结构 3、构建Dockerfile文件镜像 二、镜像上传&#xff08;阿里云&#xff09; 前言&#xff1a;为什么要使用Dockerfile &#xff1f; 首先Dockerfile …

Linux系统Shell脚本 ----- 编程规范和变量详细解读

一、Shell脚本概述 1、什么是Shell Linux系统中运行的一种特殊程序在用户和内核之间充当“翻译官”用户登录Linux系统时&#xff0c;自动加载一个Shell程序Bash是Linux系统中默认使用的Shell程序 2、Shell的作用 Linux系统中的shell是一个特殊的应用程序&#xff0c;它介于操…

Ansys Lumerical|如何将Klayout Cell动态导入Lumerical Multiphysics

附件下载 联系工作人员获取附件 说明 在本例中&#xff0c;演示了如何将KLayout Library Cell动态导入 Lumerical 以执行设计扫描和表征。该功能支持动态导入到Lumerical FDTD、MODE以及Multiphysics的所有工具&#xff0c;包括CHARGE、HEAT、FEEM、MQW、DGTD。本例适用于&am…

【nginx实战】nginx正向代理、反向代理、由反向代理实现的负载均衡、故障转移详解

文章目录 一. 正向代理与反向代理的概念二. Nginx服务器的正向代理服务1. Nginx服务器正向代理服务的配置的3个指令1.1. resolver指令1.2. resolver_timeout指令1.3. proxy_pass指令 2. Nginx服务器正向代理服务的使用 三. Nginx服务器的反向代理服务1. 反向代理的基本指令1.1.…

pytest配置文件pytest.ini

说明&#xff1a; pytest.ini是pytest的全局配置文件&#xff0c;一般放在项目的根目录下是一个固定的文件-pytest.ini可以改变pytest的运行方式&#xff0c;设置配置信息&#xff0c;读取后按照配置的内容去运行 pytest.ini 设置参数 addopts 设置自定义执行参数&#xff0…

IDEA启动项目遇到的异常汇总,包括插件异常,版本依赖异常,启动异常等以及对应的解决办法

该文章旨在记录开发中遇到的一些异常&#xff0c;以供遇到似错误进行参考修改 一、项目在多个环境下切换&#xff0c;有一次启动后编译失败&#xff0c;报异常 背景&#xff1a;项目在不同环境下有对应的分支&#xff0c;切换分支后运行项目&#xff0c;报错如下 错误:Kotlin:…

赠书活动~

关注公众号获得&#xff0c;发送抽奖

PolarDB无感切换特性助力游戏领域高可用实践

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

《WebKit 技术内幕》学习之六(2): CSS解释器和样式布局

2 CSS解释器和规则匹配 在了解了CSS的基本概念之后&#xff0c;下面来理解WebKit如何来解释CSS代码并选择相应的规则。通过介绍WebKit的主要设施帮助理解WebKit的内部工作原理和机制。 2.1 样式的WebKit表示类 在DOM树中&#xff0c;CSS样式可以包含在“style”元素中或者使…

依托物联网、互联网,建立云端大数据管理平台,形成“端+云+大数据”的智慧工地

概述&#xff1a; 智慧工地&#xff0c;是将物联网应用到建筑工地中&#xff0c;从施工现场源头抓起&#xff0c;最大程度的收集人员、安全、环境、材料等关键业务数据&#xff0c;依托物联网、互联网&#xff0c;建立云端大数据管理平台&#xff0c;的业务体系和新的管理模式…

Linux下用树莓派DS18B20温度传感器读取温度并上传至服务端

目录 一、DS18B20温度传感器 二、逻辑分析 三、实战操作 1、服务端 2、客户端 3、运行结果 一、DS18B20温度传感器 DS18B20是比较常用到的温度传感器&#xff0c;采用单总线控制。是美国DALLAS半导体公司继DS1820之后最新推出的一种改进型智能温度传感器。关于该温度传感…

Leetcode刷题笔记题解(C++):LCR 153. 二叉树中和为目标值的路径

思路&#xff1a;利用回溯的思想&#xff0c;回溯的退出条件为当前节点为空&#xff0c;是符合路径的判断条件为路径和为目标值且叶子节点包含了&#xff0c;代码如下&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *…

Elasticsearch 常用信息

简述 本文针对 Elasticsearch&#xff08;简称ES&#xff09;集群6.x版本出现故障时&#xff0c;可通过提供的命令进行排查。 1、集群健康状态 集群健康状态状态说明red不是所有的主要分片都可用。表示该集群中存在不可用的主分片。可以理解为某个或者某几个索引存在主分片丢失…

Vue2:全局事件总线

一、场景描述 之前我们学习了&#xff0c;通过props实现父子组件之间的通信。通过自定义组件&#xff0c;实现了子给父传递数据。 那么&#xff0c;兄弟关系的组件&#xff0c;如何通信了&#xff1f;任意组件间如何通信了&#xff1f; 这个时候&#xff0c;就要学习全局事件总…

测试工程师必看!测试用例设计全解析,让你彻底掌握

测试工程师在入行时&#xff0c;都会接触到一个名词——测试用例&#xff0c;都知道测试用例是干什么用的&#xff0c;提到设计测试用例的方法&#xff0c;大部分测试工程师都会侃侃而谈&#xff1a;等价类法、边界值法、判定表法、正交分解法……这些方法说起来都如数家珍&…

8-Python 工匠:使用装饰器的技巧

Python 工匠&#xff1a;使用装饰器的技巧 前言 这是 “Python 工匠”系列的第 8 篇文章。[查看系列所有文章] 装饰器 (Decorator) 是 Python 里的一种特殊工具&#xff0c;它为我们提供了一种在函数外部修改函数的灵活能力。它有点像一顶画着独一无二 符号的神奇帽子&#x…

仰暮计划|“说是操场,那就是个土坡,我们在那儿上边种种树啊,拔拔草,有的时候还会有同学来喂喂羊啥的,这都是我们的娱乐”

我是1948年农历二月份在河南省许昌市五女店镇的一个乡村里边出生的。从我记事的时候&#xff0c;中华人民共和国就已经成立了。当时是好多年&#xff0c;经历了三大改造呀、生产队呀、大队呀&#xff0c;乱七八糟的很多&#xff0c;估计你们现在这些孩子们啊&#xff0c;都没有…