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 ...
随机推荐
- .NET 高效开发Nuget管理工具(开源)
我们.NET开发会引用很多外部Nuget包,多项目.多个解决方案.甚至多个仓库. 简单的Nuget包管理,通过VS就能比较简单处理好.但复杂的场景呢,比如: 1.一个仓库里,有多个解决方案的Nuget ...
- 微信小程序wx.getUserInfo授权获取用户信息(头像、昵称)
这个接口只能获得一些非敏感信息,例如用户昵称,用户头像,经过用户授权允许获取的情况下即可获得用户信息,至于openid这些,需要调取wx.login来获取. index.wxml <!-- 当已 ...
- pikachu靶场 暴力破解(验证码绕过 on server)
先随便输入账号和密码.验证码,来判断前端是否对验证码进行判断对错 先随便输入账号和密码不输入验证码,来判断前端是否允许验证码留空 先随便输入账号和密码,输入正确的验证码,来判断账号和密码是否存在 1. ...
- ICMAN触摸滑条滚轮方案
ICMAN触摸滑条滚轮调光是一种利用触摸技术实现的调光控制方式,是一种更简单.直观且节能的调光方式,有效改善了用户的照明体验,并在智能家居和节能照明领域发挥着重要作用. 基于厦门晶尊微电子(ICMAN ...
- Linux (Debian) 安装MySQL 后如何获取登录密码
树莓派安装MySQL后获取登录密码 树莓派基于Debian系统. 成功安装MySQL后 su root vim /etc/mysql/debian.cnf 其中 user 和 password 就是你 ...
- CSS – RWD (Responsive Web Design) 概念篇
介绍 Only PC 以前是没有手机的, 只有电脑, 所以做开发, 只需要开发电脑版本就可以了. Mobile Version 后来手机诞生, 有钱的公司就做两个版本, 一个手机版, 一个电脑版. 没 ...
- Java获取Object中Value的方法
在Java中,获取对象(Object)中的值通常依赖于对象的类型以及我们希望访问的属性.由于Java是一种静态类型语言,直接从一个Object类型中访问属性是不可能的,因为Object是所有类的超类, ...
- `std::optional` 函数返回值
std::optional 是 C++17 中引入的一个模板类,用于表示一个值可能存在也可能不存在的情况. 它可以存储一个值,或者表示没有值的状态,类似于其他编程语言中的"可选"类 ...
- 自己动手,通过源码找回 Ant-Design-Blaozr 中 Tree 组件的搜索筛选效果
最近更新一个Blazor server的项目,顺带把用到的Ant-Design-Blazor 升级到了最新的 0.14.4,结果发现之前在 0.8.4 版本中 Tree 组件的搜索显示效果变了,从仅显 ...
- USB协议详解第4讲(USB描述符-标准配置描述符)
1.USB描述符 USB描述符有设备描述符.标准配置描述符.接口描述符.端点描述符.字符串描述符,HID设备有HID描述符.报告描述符和物理描述符.今天主要是学习USB标准配置描述符的组成. 2.标准 ...