UE4纯C++实现游戏快捷栏之将快捷栏注册到玩家状态
我们有了UI有了物品信息,接下来我们便需要给每一个玩家绑定一个快捷栏了,我们分以下几部分来注册我们玩家的快捷栏。
1.Types.h:定于ShortcutContainer类,定义快捷栏的单个容器结构体,其内部存储玩家所引用的快捷栏的单个格子的信息数据
·基础的,我们将在结构体中保存{单个物品ID、物品数量、容器笔刷、物品笔刷、物品名称、容器未被选中笔刷、容器被选中笔刷、物品笔刷列表}
·此外,我们定义其初始化构造函数来完成信息填充、定义是否选中该格子来反馈正确的笔刷、定义设置新物品ID和Num的函数

1 // 快捷栏容器结构体
2 struct ShortcutContainer
3 {
4 int ObjectIndex; // 物品ID
5 int ObjectNum; // 物品数量
6 TSharedPtr<SBorder> ContainerBorder;
7 TSharedPtr<SBorder> ObjectImage;
8 TSharedPtr<STextBlock> ObjectNumText;
9 const FSlateBrush* NormalContainerBrush;
10 const FSlateBrush* ChoosedContainerBrush;
11 TArray<const FSlateBrush*>* ObjectBrushList;
12
13 // 初始化构造函数
14 ShortcutContainer(TSharedPtr<SBorder> CB, TSharedPtr<SBorder> OI, TSharedPtr<STextBlock> ONT,
15 const FSlateBrush* NCB, const FSlateBrush* CCB, TArray<const FSlateBrush*>* OBL) :
16 ContainerBorder(CB), ObjectImage(OI), ObjectNumText(ONT), NormalContainerBrush(NCB),
17 ChoosedContainerBrush(CCB), ObjectBrushList(OBL){
18 // 初始化显示设置
19 ObjectIndex = 0;
20 ObjectNum = 0;
21 ContainerBorder->SetBorderImage(NormalContainerBrush);
22 ObjectImage->SetBorderImage((*ObjectBrushList)[0]);
23 }
24
25 // 设置是否选中当前物品
26 int SetChoosed(bool Option) {
27 if (Option) {
28 ContainerBorder->SetBorderImage(ChoosedContainerBrush);
29 }
30 else {
31 ContainerBorder->SetBorderImage(NormalContainerBrush);
32 }
33 return ObjectIndex;
34 }
35
36 // 物品栏设置摆放的物品,传入物品id
37 ShortcutContainer* SetObject(int NewIndex) {
38 ObjectIndex = NewIndex;
39 ObjectImage->SetBorderImage((*ObjectBrushList)[ObjectIndex]);
40 return this;
41 }
42
43 // 设置数量及数字显示
44 ShortcutContainer* SetObjectNum(int Num = 0) {
45 ObjectNum = Num;
46 // 如果物品数量为0or1时,不显示数字
47 if (ObjectNum == 0 || ObjectNum == 1) {
48 ObjectNumText->SetText(FString(""));
49 }
50 else {
51 ObjectNumText->SetText(FString::FromInt(ObjectNum));
52 }
53 return this;
54 }
55
56 };
2.GameWidgetStyle.h:填充我们需要使用的物品笔刷,并在蓝图中配置

1 USTRUCT()
2 struct SLAICOURSE_API FSDGameStyle : public FSlateWidgetStyle
3 {
4 // 物品的 Brush
5 UPROPERTY(EditAnywhere, Category = "Package")
6 FSlateBrush ObjectBrush_1;
7 UPROPERTY(EditAnywhere, Category = "Package")
8 FSlateBrush ObjectBrush_2;
9 UPROPERTY(EditAnywhere, Category = "Package")
10 FSlateBrush ObjectBrush_3;
11 UPROPERTY(EditAnywhere, Category = "Package")
12 FSlateBrush ObjectBrush_4;
13 UPROPERTY(EditAnywhere, Category = "Package")
14 FSlateBrush ObjectBrush_5;
15 UPROPERTY(EditAnywhere, Category = "Package")
16 FSlateBrush ObjectBrush_6;
17 UPROPERTY(EditAnywhere, Category = "Package")
18 FSlateBrush ObjectBrush_7;
19
20 }
3.DataHandle:获取物品列表的笔刷数组,以便在游戏中的其他地方使用笔刷绘制出物品图标
DataHandle.h:添加物品笔刷数组、GameStyle指针

1 class SLAICOURSE_API SDDataHandle
2 {
3 public:
4 // 物品贴图资源数组
5 TArray<const FSlateBrush*> ObjectBrushList;
6
7 private:
8 // 获取 GameStyle
9 const struct SDGameStyle* GameStyle;
10 };
DataHandle.cpp:在初始物品数据函数中先获取GameStyle在填充笔刷数组

1 void SDDataHandle::InitObjectAttr()
2 {
3 SDSingleton<SDJsonHandle>::Get()->ObjectAttrJsonRead(ObjectAttrMap);
4
5 // 获取GameStyle
6 GameStyle = &SDStyle::Get().GetWidgetStyle<FSDGameStyle>("BPSDGameStyle");
7
8 // 填充笔刷数组
9 ObjectBrushList.Add(&GameStyle->EmptyBrush);
10 ObjectBrushList.Add(&GameStyle->ObjectBrush_1);
11 ObjectBrushList.Add(&GameStyle->ObjectBrush_2);
12 ObjectBrushList.Add(&GameStyle->ObjectBrush_3);
13 ObjectBrushList.Add(&GameStyle->ObjectBrush_4);
14 ObjectBrushList.Add(&GameStyle->ObjectBrush_5);
15 ObjectBrushList.Add(&GameStyle->ObjectBrush_6);
16 ObjectBrushList.Add(&GameStyle->ObjectBrush_7);
17
18 }
4.ShortcutWidget:
·在InitializeContainer()函数中,先创建快捷栏中多个容器组成的数组,并循环初始化九个(在未读表之前,我们先全部初始化为空数据)

1 void SSDShortcutWidget::InitContainer()
2 {
3 TArray<TSharedPtr<ShortcutContainer>> ContainerList; // 由单个容器组成的列表
4
5 // 容器列表初始化
6 for (int i = 0; i < 9; i++) {
7 // 创建容器
8 TSharedPtr<SBorder> ContainerBorder;
9
10 // 构建UI框架
11 TSharedPtr<SBorder> ObjectImage; // 物品图片
12 TSharedPtr<STextBlock> ObjectNumText; // 物品数量
13 SAssignNew(ContainerBorder, SBorder)
14 .Padding(FMargin(10.f))
15 [
16 SAssignNew(ObjectImage, SBorder)
17 .HAlign(HAlign_Right)
18 .VAlign(VAlign_Bottom)
19 .Padding(FMargin(0.f, 0.f, 5.f, 0.f))
20 [
21 SAssignNew(ObjectNumText, STextBlock)
22 .Font(GameStyle->Font_Outline_20)
23 .ColorAndOpacity(GameStyle->FontColor_Black)
24 ]
25 ];
26
27 // 每初始完一个组件ContainerBorder就将其添加到GridPanel
28 GridPanel->AddSlot(i, 0)
29 [
30 ContainerBorder->AsShared()
31 ] ;
32
33 // 给快捷栏单个物品框赋值(实例化一个容器结构体)
34 TSharedPtr<ShortcutContainer> Container = MakeShareable(new ShortcutContainer(ContainerBorder
35 ,ObjectImage , ObjectNumText, &GameStyle->NormalContainerBrush, &GameStyle->ChoosedContainerBrush,
36 &SDDataHandle::Get()->ObjectBrushList));
37
38 // 快捷栏第一个为选中
39 if (i == 0) Container->SetChoosed(true);
40
41 // 添加到数组
42 ContainerList.Add(Container);
43 }
44
45 }
经过上面的步骤,我们已经有了一个完整的滑动窗口,每个窗口还可以自定义其包含的物品数据,接下来我们将该滑动窗口绑定到玩家,并设置滑轮切换快捷栏选中的事件。
为了降低耦合度,我们不让玩家控制器来持有快捷栏UI对象,而是让PlayerState来持有存储该快捷栏对象,同时PlayerState也是存储游戏用户数据的对象,适合同时持有快捷栏里数据和操控快捷栏。
快捷栏UI和PlayerState之间我们通过委托来实现数据交互。
1.ShortcutWidget.h:声明委托、创建委托变量

1 // 注册容器到PlayerState类的委托
2 DECLARE_DELEGATE_TwoParams(FRegisterShortcutContainer, TArray<TSharedPtr<ShortcutContainer>>*, TSharedPtr<STextBlock>)
3
4 class SANDBOXGAME_API SSDShortcutWidget : public SCompoundWidget
5 {
6 public:
7 // 创建委托变量
8 FRegisterShortcutContainer RegisterShortcutContainer;
9 }
2.ShortcutWidget.cpp:执行委托

1 void SSDShortcutWidget::InitContainer()
2 {
3
4 // 将实例化的结构体注册进PlayerState的容器数组
5 RegisterShortcutContainer.ExecuteIfBound(&ContainerList, shortcutInfoTextBlock);
6 }
3.PlayerState.h:添加与委托变量(这里的TAttribute也是委托,使用的是TAttribute委托函数Get,具体是移步这篇文字:TAttribute原理)同名的委托函数

1 UCLASS()
2 class SANDBOXGAME_API ASDPlayerState : public APlayerState
3 {
4 GENERATED_BODY()
5
6 public:
7
8 ASDPlayerState();
9
10 // 提供给ShortcutWidget的添加快捷栏容器委托
11 void RegisterShortcutContainer(TArray<TSharedPtr<ShortcutContainer>>* ContainerList, TSharedPtr<STextBlock> ShortcutInfoTextBlock);
12
13 private:
14 // 获取快捷栏物品信息
15 FText GetShortcutInfoText() const;
16
17 private:
18 // 快捷栏序列
19 TArray<TSharedPtr<ShortcutContainer>> ShortcutContainerList;
20
21 // 快捷栏信息参数(TAttribute为保存了值和获取该值委托的结构)
22 TAttribute<FText> ShortcutInfoTextAttr;
23
24 };
4.PlayerState.cpp:实现RegisterShortcutContainer函数、GetShortcutInfoText()函数(test版)
将传入的ContainerList初始化PlayerState持有的ShortcutContainerList变量
将PlayerState持有的ShortcutInfoTextAttr委托函数绑定为GetShortcutInfoText()
将ShortcutInfoTextBlock快捷栏文本指针所指内容绑定为ShortcutInfoTextAttr

1 void ASDPlayerState::RegisterShortcutContainer(TArray<TSharedPtr<ShortcutContainer>>* ContainerList, TSharedPtr<STextBlock> ShortcutInfoTextBlock)
2 {
3 for (TArray<TSharedPtr<ShortcutContainer>>::TIterator It(*ContainerList); It; ++It) {
4 ShortcutContainerList.Add(*It);
5 }
6
7 // 绑定函数
8 ShortcutInfoTextAttr.BindUObject(this, &ASDPlayerState::GetShortcutInfoText);
9
10 // 绑定快捷栏信息TextBlokc
11 ShortcutInfoTextBlock->SetText(ShortcutInfoTextAttr);
12
13
14 // 物品栏测试
15 ShortcutContainerList[1]->SetObject(1)->SetObjectNum(5);
16 ShortcutContainerList[2]->SetObject(2)->SetObjectNum(15);
17 ShortcutContainerList[3]->SetObject(3)->SetObjectNum(1);
18 ShortcutContainerList[4]->SetObject(4)->SetObjectNum(35);
19 ShortcutContainerList[5]->SetObject(5)->SetObjectNum(45);
20 ShortcutContainerList[6]->SetObject(6)->SetObjectNum(55);
21 ShortcutContainerList[7]->SetObject(7)->SetObjectNum(65);
22
23 }
24
25 FText ASDPlayerState::GetShortcutInfoText() const
26 {
27 return FText::FromString("ha123");
28 }
最后我们通过GameMode来持有PlayerState,在UIWidget中通过获取GameMode来将快捷栏容器绑定注册
1.GameMode.h:持有PlayerState指针并定义初始化函数(避免调用崩溃)

1 class SANDBOXGAME_API ASDGameMode : public AGameModeBase
2 {
3 GENERATED_BODY()
4
5 public:
6 //...
7 // 组件赋值,给GameHUD调用,避免空引用
8 void InitGamePlayModule();
9
10 public:
11 // 单人游戏中由GameMode掌控这些指针
12 ASDPlayerController* SPController;
13
14 ASDPlayerCharacter* SPCharacter;
15
16 ASDPlayerState* SPState;
17
18 };
2.GameMode.cpp:初始化PlayerState指针函数实现

1 void ASDGameMode::InitGamePlayModule()
2 {
3 // 添加引用,初始化指针
4 SPController = Cast<ASDPlayerController>(UGameplayStatics::GetPlayerController(GetWorld(), 0));
5 SPCharacter = Cast<ASDPlayerCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
6 SPState = Cast<ASDPlayerState>(SPController->PlayerState);
7
8 }
9
10 void ASDGameMode::BeginPlay()
11 {
12 // 游戏数据初始化
13 SDDataHandle::Get()->InitGameData();
14
15 if (!SPController) InitGamePlayModule();
16
17 }
3.GameHUD.h:获取GM指针,重写BeginPlay()函数完成快捷栏UI绑定PlayerState(若PlayerState中数据改变则UI收通知改变)

1 class SANDBOXGAME_API ASDGameHUD : public AHUD
2 {
3
4 public:
5 // 保存GameMode指针
6 ASDGameMode* GM;
7
8 protected:
9 virtual void BeginPlay() override;
10
11 private:
12 TSharedPtr<SSDGameHUDWidget> GameHUDWidget;
13 };
4.GameHUD.cpp:实现BeginPlay()完成

1 void ASDGameHUD::BeginPlay()
2 {
3 // 先调用父类BeginPlay()绑定Controller和Character
4 Super::BeginPlay();
5
6 GM = Cast<ASDGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
7 if (!GM) return;
8 // 确保所需组件都初始化
9 GM->InitGamePlayModule();
10
11 // 绑定注册快捷栏容器
12 GameHUDWidget->ShortcutWidget->RegisterShortcutContainer.BindUObject(GM->SPState,
13 &ASDPlayerState::RegisterShortcutContainer);
14 }
注:这里如果要在GameHUD()中成功调用GM->InitGamePlayModule();初始化数据,需将游戏改为独立运行,而非以客户端运行,客户端上是没有GM的,故一直为空
效果图:

UE4纯C++实现游戏快捷栏之将快捷栏注册到玩家状态的更多相关文章
- UE4开发神秘海域类游戏原型 初阶(二):动画资源的整合
前一篇已经确定神海类游戏原型的目标,首先要做的就是3C's(Character, Controls, Camera)的开发. UE4的3C's的程序部分开发主要也就是基于他的GamePlay Fr ...
- 张瀚荣:如何用UE4制作3D动作游戏
转自:http://www.gamelook.com.cn/2015/06/218267 GameLook报道/ 6月5日,2015年第三期GameLook开放日‧虚幻引擎专场活动在上海正式举行,此次 ...
- 使用UE4公布安卓平台游戏
使用了几天的UE4 ,总算是将游戏在安卓平台执行起来了.当中遇到非常多问题,而且终于依旧有一些问题没能解决. 整体感觉是UE4这款引擎眼下还不够成熟.问题较多. 没有unity使用起来方便. 可是既然 ...
- UE4 Windows平台部署游戏到IOS 第一部分
UE4 Version 4.11.2 or 4.12.2 方法步骤: 1.申请IOS开发者账号,大概三个工作日左右激活. 2.下载iTunes 3.创建项目因为是在Windows平台,根据官方的提示只 ...
- [UE4]纯函数的执行时机
一.纯函数是在需要的时候被调用 二.纯函数内不应当修改任何数据 三.如果同一个函数需要多个得到多个纯函数的返回值,则多个纯函数的调用顺序不是固定的,并且一个纯函数的调用顺序也不应当影响下一个纯函数的返 ...
- UE4虚幻引擎独立游戏制作教程 UE4编程教学 虚幻引擎4
非常好的一套UE4入门教学课程,语言诙谐幽默,并且是中文语音中文语音中文语音 赠送[精通Unreal引擎技术——关卡设计艺术]PDF版 目录 FLV格式,大小5G,中文语音 扫码时备注或说明中留下邮箱 ...
- UE4 Windows平台部署游戏到IOS 第二部分
点击加号后会出来如下截图 勾选上红色单选框处(因为这个我已经申请过了所以是灰色),然后continue到后面会出现下图 选择一个之前我提到申请证书会用的的那个.csr后缀文件夹,完成以后就可以下载证书 ...
- RPG游戏中如何判断敌人是否在玩家的攻击范围之内
// 方式1:通过主角和场景中的所有敌人比较 private void AtkCondition1(float _range,float _angle) { // 搜索所有敌人列表(在动态创建敌人时生 ...
- 如何使用纯 CSS 制作四子连珠游戏
序言:你是否想过单纯使用 CSS 也可以制作一款游戏?甚至可以双人对决!这是一篇非常有趣的文章,作者详细讲解了使用纯 CSS 制作四子连珠游戏的思路以及使用奇淫巧技解决困难问题的方法.因为案例本身比较 ...
- UE4 游戏中csv配置文件使用
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接: http://blog.csdn.net/cartzhang/article/details/76549463 作者:ca ...
随机推荐
- Linux入门的基础知识点,有这篇就够了(持续更新)
很多粉丝给一口君留言,想要学习Linux资料,其实关注一口君的公众号,后台回复 1024 ,就有很多非常不错的电子书,但是有一个问题,很多粉丝是初学者,而这一大堆电子书,估计随便一本,还没看完就基本上 ...
- 学习redis问题记录
2024年5月25日 倒腾了很长时间 突然发现的问题 ide提示改为toList() 我顺便就改过去了 但是实际业务中redis序列化会产生无法反序列化的问题 造成缓存挂壁 业务直接G collect ...
- k8s-使用Network Policies实现网络隔离
一.需求 Kubernetes 的命名空间主要用于组织和隔离资源,但默认情况下,不同命名空间中的 Pod 之间是可以相互通信的.为了实现更严格的网络隔离,同一套k8s需要根据不同的命名空间进行网络环境 ...
- LaTeX 插入代码
LaTeX 插入代码可以使用的宏包有 verbatim.fancyvrb.listings 以及 minted.个人最推荐使用 minted. verbatim verbatim 没有语法高亮功能,只 ...
- 一个小小空格问题引起的bug
程序员会遇到一种情况,一个bug排查到最后是由一个很小的问题导致的.在昨天的日常搬砖中遇到一个问题,耽搁了我大半天的时间,最后查明原因让我很无语. 首先介绍一下背景,我是做算法模型训练,目前手上的工作 ...
- 如何在 Web 前端做 3D 音效处理
一.背景 在社交元宇宙.大逃杀等类型的游戏场景下,用户在通过简单语音交流外,结合场景也需要一些立体声效果来让用户感知游戏角色周围其他用户的存在及其对应的距离和方位,提高语音互动的趣味性. 为了满足 ...
- WebRTC 简单入门与实践
一.前言 WebRTC 技术已经广泛在各个行业及场景中被应用,但对多数开发者来说,实时音视频及相关技术却是比较不常接触到的. 做为一名 Web 开发者,WebRTC 这块的概念着实花了不少时间才搞明白 ...
- 加快 hdfs block 块复制的参数调整
共涉及三个参数: dfs.namenode.replication.max-streams 30 => 70 dfs.namenode.replication.max-streams-hard- ...
- codeforces 1931E
题目链接 简介:对一些数字,余念安可以反转一个数字,齐夏将两个数字首尾相连变为一个数字.每个人都采取最优策略. 名单上只剩下一个号码.如果该整数不小于 10的m次方 ,则齐夏获胜.否则余念安就赢了. ...
- mac M1,M2,M3芯片踩坑 nodejs ruby brew
问题&解决方法 先说解决方法, 感兴趣的可以了解事情的经过, 也许我描述的问题不专业, 但确实解决了当下的问题, 欢迎留言讨论 这里主要是两个问题, 一个是启用rosetta模式失败, 一个是 ...