虚幻引擎5 Gameplay框架(四)

Gameplay重要类及重要功能使用方法(三)

虚幻的委托机制

  • 虚幻委托之间的区别
  • 序列化就是是否可以在蓝图中执行
    在这里插入图片描述

多播与单播的创建

  • 制作功能:使用多播与单播将血条与血量进行实时更新
  • 首先新建一个单播与一个多播委托
    在这里插入图片描述
  • 实例化这两个委托的标签
    在这里插入图片描述
  • 然后将角色属性类中的血量与最大血量封装一下
    在这里插入图片描述
  • 封装之后就得更改之前角色类里面引用到血量的逻辑
    在这里插入图片描述
    在这里插入图片描述
  • 给MaxHP也添加一个属性通知
    在这里插入图片描述
  • 进行单播与多播的创建
    在这里插入图片描述

单播与多播的绑定

补全之前的封装性
  • 首先为了体现封装性,将之前的UI封装为保护域,提供共有接口
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 之前引用到UI的变量需要变化
    在这里插入图片描述
开始进行单播与多播绑定
  • 之前写的默认参数给注释掉
    在这里插入图片描述

  • 新建两个存储变量来存储血量与最大血量
    在这里插入图片描述

  • 重写一下角色信息UI类里面的SetPlayerHPBar函数,我们不需要传递参数Percent进行设置百分比,让函数自身去实现逻辑,不采用传参方式
    在这里插入图片描述
    在这里插入图片描述

  • 在设置血量的函数中更新存储的血量变量
    在这里插入图片描述

  • 在角色信息UI类里面添加一个初始化角色数据的函数来绑定单播与多播,获取角色属性中的属性数据,然后更新血条
    在这里插入图片描述
    在这里插入图片描述

  • 重写角色属性类中的BeginPlay
    在这里插入图片描述

  • 进行调用初始化角色数据函数
    在这里插入图片描述

  • 运行结果
    在这里插入图片描述

PlayerState在各端的执行顺序

  • 在开两个客户端的情况下

    • BeginPlay 1 生成了6次
    • 第一次是在Server中客户端1中,客户端1的控制器中生成
    • 第二次就是在客户端1中,客户端1中的控制器中生成
    • 第三次是在Server中客户端2中,客户端2的控制器中生成
    • 第四次在客户端1中,客户端2控制器中生成
    • 第五次在客户端2中,客户端2控制器中生成
    • 第六次在客户端2中,客户端1控制器中生成
      在这里插入图片描述
  • 验证
    在这里插入图片描述

  • 验证结果,这也验证了虚幻引擎中服务器代码与客户端代码是一起的
    在这里插入图片描述
    在这里插入图片描述

  • 也验证了这个PlayerState既在服务器上又在所有客户端上
    在这里插入图片描述

数据表格DataTable在CPP中的使用方法

在蓝图中如何使用DataTable

  • DataTable需要创建结构体数据使用
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

C++中使用DataTable

  • 需要创建一个能被引擎识别的Struct
  • 创建一个空类然后在里面创建Struct
    在这里插入图片描述
  • 创建结构体与枚举
  • 使用FTableRowBase要加头文件,#include "Engine/DataTable.h"
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Engine/DataTable.h"
#include "CharacterTemplate.generated.h"/*** */
UCLASS()
class GAMEPLAYCODEPARSING_API UCharacterTemplate : public UObject
{GENERATED_BODY()};//第一种创建枚举的方式
UENUM(BlueprintType)
enum class ECharacterColor
{WHITE,YELLOW,BLACK
};
//第二种创建枚举的方式
namespace ECharacter
{enum  ColorType{RED,GREEN,BLUE};
}USTRUCT(BlueprintType)
struct FGPPlayerInitData : public FTableRowBase
{ //宏反射到蓝图GENERATED_USTRUCT_BODY()UPROPERTY(EditAnywhere,BlueprintReadWrite)int32 ID;UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = 10 , ClampMax = 500))float MaxHP;UPROPERTY(EditAnywhere, BlueprintReadWrite)ECharacterColor CharacterColor;//第二种创建枚举的方法声明变量就有点特别TEnumAsByte<ECharacter::ColorType> CharacterColorType;//CharacterColor必须选择YELLOW才可以编辑CharacterNameUPROPERTY(EditAnywhere,BlueprintReadWrite,meta = (EditCondition = "CharacterColor == ECharacterColor::YELLOW"))FName CharacterName;
};
  • 运行结果
  • 选择刚才类中创建的FGPPlayerInitData结构体,因为添加了宏反射,所以虚幻引擎识别得到
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在CPP中读取数据表格中的数据

  • 首先得给行数据起个名字到时候在C++里面好获取
    在这里插入图片描述
  • 对于角色数据这种属性一般在GameInstance里面进行读取
  • 使用DataTable需要加头文件,#include “Engine/DataTable.h
    在这里插入图片描述
    在这里插入图片描述
  • 新建一个FName变量为FindRow函数占位
    在这里插入图片描述
  • 去角色类里面进行数据读取修改,在Character类中PlayerState不能直接获取PlayerState进行数据修改,因为直接获取的PlayerStatePawn类里面的,我们得等PlayerState有值的时候才能再进行修改,否则就为空
    在这里插入图片描述
  • 我们追溯到PlayerState被赋值的位置
    在这里插入图片描述
  • 查看堆栈,发现这个PlayerState是从PossessedBy过来的
    在这里插入图片描述
  • 所以,我们进行数据修改时得在PossessedBy进行修改,在PlayerState赋值之后才能进行修改数据
    在这里插入图片描述
  • 将修改行数据逻辑放到PossessedBy函数中进行
    在这里插入图片描述
  • 行数据的名字添加到我们C++写的那个FName变量为FindRow函数占位的变量里面
    在这里插入图片描述
  • 运行结果,血量就写入了
    在这里插入图片描述

程序运行崩溃调试

  • 我们运行服务器批处理脚本会发现有报错,说我们没有初始化角色的行数据
    在这里插入图片描述
  • 我们将其初始化,就没问题了
    在这里插入图片描述
  • 第二个错误报错,开启服务器与客户端批处理脚本,服务器会挂掉,客户端也出现错误
    在这里插入图片描述
  • 查看项目中的日志,查看是什么问题
    在这里插入图片描述
  • 查看错误是一个未知的指针,查看堆栈发现逻辑出现在我们的PossessedBy函数
    在这里插入图片描述
  • 我们可以附加批处理脚本到vs里面进行调试,因为批处理脚本就是在编译器里进行独立游戏模式运行,先开启服务器批处理脚本,然后添加到vs调试里面
    在这里插入图片描述
    在这里插入图片描述
  • 附加之后,开启客户端批处理脚本,此时就会报错,查看堆栈是从PossessedBy进入的,去查看一下里面里面是否可能的错误
    在这里插入图片描述
  • 在这个函数里面,只有PlayerInitData最可疑,GI如果是空指针都不会进入在这里面,PlayerDataRow数据也没问题
  • PlayerInitData放到监视窗口里面发现已经为空了,已经被清空了
    在这里插入图片描述
  • 这是为什么,这是个U类在虚幻引擎机制里面,是会被垃圾回收机制给回收掉的,我们客户端连服务器的时候会连这个表,但是此时已经被回收了,所以空指针问题就出现在这,只要客户端连接服务器的时候就会崩溃的原因出现在这
  • 解决方法:添加一个UPROPERTY()宏,它就不会被回收掉了
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述

各类的执行顺序

  • 结论图:
    在这里插入图片描述
  • 验证:
    • 我们制作一个功能,来验证这个结论。之前起客户端人名是通过读取命令行文本进行读取存到GameInstance上然后在UI类的构造函数上进行设置的这个是在本地设置的。只有本地客户端知道,但是服务器是不知道的。
    • 功能:让服务器去设置客户端的名字
    • 思路:当服务器连接客户端的时候为会客户端新建一个PlayerController,然后客户端的PlayerStateHUD就会跟着改变
  • 先注释掉之前UI类中设置客户端名字的逻辑
    在这里插入图片描述
  • 之前看PlayerState源码的时候,里面提供了获取PlayerName接口,进行 了挖断,直接在初始化里面使用这个函数进行设置名字
    在这里插入图片描述
  • 然后我们批处理脚本选择了名字后,我们得把这个名字传给服务器,直接在PlayerController里面链接服务器的时候进行传递名字
    在这里插入图片描述
  • GameMode类里面的登录函数中将服务器获取的名字复制到客户端
    在这里插入图片描述
    • ParseOption:从包含多个选项的字符串中提取特定的值

      • 输入参数:
        Options(选项字符串):一个字符串,其中包含了以某种格式(通常为键值对形式)组织的多个选项。
        Key(键):需要在 Options 字符串中查找的特定键。

      • 返回值:
        如果在 Options 字符串中找到了与 Key 相对应的值,则返回该值。

      • 例如,如果 Options 字符串是 “Graphics=High,Sound=Low,Difficulty=Medium”,并且 Key 是 “Sound”,那么该函数将返回 “Low”。

    • 此函数标记有BlueprintPure,意味着它可以在蓝图中作为纯函数使用,不会改变任何状态。同时,meta=(BlueprintThreadSafe) 表示该函数是可以在线程安全的环境下从蓝图调用的,增强了使用的灵活性和安全性。
      在这里插入图片描述

  • 运行结果,名字已经复制成功
    在这里插入图片描述

SaveGame_CPP中如何存储数据

保存数据

  • 虚幻提供了一个存储游戏数据的类SaveGame类,创建这个类
    在这里插入图片描述
  • 在SaveGame类里面写上需要存储的数据,注意加宏反射
    在这里插入图片描述
  • 然后在角色属性类里面重写EndPlay函数,在EndPlay里面存储游戏数据
    在这里插入图片描述
    在这里插入图片描述
  • 运行结果,使用批处理脚本打开服务器和一个客户端看看数据是否在本地保存了,服务器与客户端都保存了一份,这是不合理的,应该客户端不保存数据,就服务器保存,然后到时候服务器复制数据到客户端
    在这里插入图片描述
  • 改变一下逻辑,添加一个判断限制
    在这里插入图片描述
  • 删除项目中的SaveGames文件夹,重新启动服务器与客户端生成一下,这个就是服务器给我们生成的保存的数据
    在这里插入图片描述

获取数据

  • 读取数据,因为要用到角色属性类,读表也是在PossessedBy函数里面读的,所以读取存档也在这里读取
void AGamePlayCodeParsingCharacter::PossessedBy(AController* NewController)
{Super::PossessedBy(NewController);if (GetNetMode() == NM_DedicatedServer){UGPProjectGameInstance* GI = GetGameInstance<UGPProjectGameInstance>();AGPProjectPlayerState* PS = GetPlayerState<AGPProjectPlayerState>();bool IsNeedDataTable = true;//设置一个bool值设计逻辑读了存档后就不需要读表数据了if (GI){//读存档UMyDataSaveGame* SaveGame = Cast<UMyDataSaveGame>(UGameplayStatics::LoadGameFromSlot(FString("MySaveGame_" + PS->GetPlayerName()),0));if (SaveGame){//设置存档里面的数据PS->SetPlayerCurHP(SaveGame->SG_CurHP);PS->SetPlayerMaxHP(SaveGame->SG_MaxHP);}IsNeedDataTable = false;}//读表数据if (IsNeedDataTable){if (GI->PlayerInitData){//获取到行数据FGPPlayerInitData* PlayerData = GI->PlayerInitData->FindRow<FGPPlayerInitData>(PlayerDataRow, TEXT(""));if (ensure(PS)){PS->SetPlayerCurHP(PlayerData->MaxHP);PS->SetPlayerMaxHP(PlayerData->MaxHP);}}}}
}
  • 删除项目中的SaveGames文件夹,重新启动服务器与客户端生成一下,然后关闭客户端,重新打开刚才选择名字的客户端
    在这里插入图片描述

在ini配置文件中为CPP中变量设置初始值

  • 查看项目目录的Config文件夹,里面会有一些ini文件,这些是虚幻引擎提供的给代码里的一些属性进行初始值
    在这里插入图片描述
  • 实现一个功能,将表的引用路径存储到配置文件里,然后进行读取数据
  • 如果你想写入到Editor配置文件里面就config=EditorEngine里面的话就config=engine
  • 存储路径要加上Config宏,表明这个属性源自ini文件的
    在这里插入图片描述
  • 然后打开DefaultGame.ini文件进行格式填写
    在这里插入图片描述
  • 然后我们重新写一下获取数据表格逻辑,进行测试
    在这里插入图片描述
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述
  • 修改一下DataTable表数据看看是否改变
    在这里插入图片描述
    在这里插入图片描述

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

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

相关文章

神经网络---卷积神经网络CNN

一、从前馈神经网络到CNN 前馈神经网络&#xff08;Feedforward Neural Networks&#xff09;是最基础的神经网络模型&#xff0c;也被称为多层感知机&#xff08;MLP&#xff09;。 它由多个神经元组成&#xff0c;每个神经元与前一层的所有神经元相连&#xff0c;形成一个“…

Ubuntu24.04 LTS安装中文输入法

前言 最近&#xff0c;windows玩没了&#xff0c;一怒之下决定换一个操作系统&#xff0c;当然就是最新的Ubuntu24.04 LTS.&#xff0c;其中魔法和咒语&#xff08;汉语&#xff09;是inux遇到的第一大难关&#xff0c;我权限不够教不了魔法&#xff0c;但我可以教你咒语(๑•…

大模型之路,从菜鸟到模型大师只需要一步

前言&#xff1a; 在这个数据爆炸的时代&#xff0c;大模型技术正以前所未有的速度发展。从自然语言处理到计算机视觉&#xff0c;从智能推荐到自动驾驶&#xff0c;大模型正逐渐渗透到我们生活的方方面面。那么&#xff0c;如何从菜鸟成长为模型大师呢&#xff1f;本文将为你…

1.8k Star!RAGApp:在任何企业中使用 Agentic RAG 的最简单方法!

原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 1.8k Star&#xff01;RAGApp&#xff1a;在任何企业中使用 Agentic RAG 的最简单方法&#xff01; &#x1f31f;在任何企业中使用 Agent…

9.Halcon3D点云力矩求解-平面拟合用法

1.实现效果 我们在使用3d相机对产品进行扫描生成点云的时候,由于安装问题,所以我们不可能保证每次产品扫描出来都在坐标系中位置和姿态非常标准。 上述算法描述的就是在某一个维度或者某几个维度上将点云数据和坐标系对齐; 至于怎么对齐,如何实现就是今天的内容。 本人能…

如何评价GPT-4o?GPT-4o和ChatGPT4.0的区别是啥呢?

如何评价GPT-4o? GPT-4o代表了人工智能领域的一个重要里程碑&#xff0c;它不仅继承了GPT-4的强大智能&#xff0c;还在多模态交互方面取得了显著进步。以下是几个方面的分析&#xff1a; 技术特点 多模态交互能力&#xff1a;GPT-4o支持文本、音频和图像的任意组合输入与输出…

vue3组件通信与props

title: vue3组件通信与props date: 2024/5/31 下午9:00:57 updated: 2024/5/31 下午9:00:57 categories: 前端开发 tags: Vue3组件Props详解生命周期数据通信模板语法Composition API单向数据流 Vue 3 组件基础 在 Vue 3 中&#xff0c;组件是构建用户界面的基本单位&#…

判断自守数-第13届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第75讲。 判断自守数&#…

蓝桥杯高频考点-与日期相关的题目

文章目录 前言1. 如何枚举合法日期1.1 预存每个月的天数1.2 封装一个判断日期是否合法的函数1.3 枚举日期并判断日期是否合法 2. 判断日期是否为回文日期2.1 将日期当作字符串进行处理2.2 将日期当作一个8位数进行处理 3. 给定初始日期&#xff0c;计算经过n天后对应的日期3.1 …

职场中,那些35岁以上的测试猿到底去哪了?

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

使用KEPServer连接欧姆龙PLC获取对应标签数据(标签值类型改为字符串型)

1.创建通道&#xff08;通道&#xff09;&#xff0c;&#xff08;选择对应的驱动&#xff0c;跟当前型号PLC型号对应&#xff09;。 2.创建设备&#xff0c;&#xff08;填入IP地址以及欧姆龙的默认端口号&#xff1a;44818&#xff09; 3.创建对应的标签。这里关键讲诉下字…

AI 网页解锁器,用于网页抓取一切 | 最快的验证码解决服务

想象一下&#xff0c;解锁互联网的全部潜力&#xff0c;数据自由流动&#xff0c;没有任何障碍阻挡你获取所需信息。在网络爬虫的世界里&#xff0c;这个梦想常常会遇到障碍&#xff1a;CAPTCHA和反机器人措施&#xff0c;这些措施旨在保护网站免受自动化访问的侵害。但如果有一…

【VSCode】快捷方式log去掉分号

文章目录 一、引入二、解决办法 一、引入 我们使用 log 快速生成的 console.log() 都是带分号的 但是我们的编程习惯都是不带分号&#xff0c;每次自动生成后还需要手动删掉分号&#xff0c;太麻烦了&#xff01; 那有没有办法能够生成的时候就不带分号呢&#xff1f;自然是有…

uni-app的网络请求库封装及使用(同时支持微信小程序)

其实uni-app中内置的uni.request()已经很强大了&#xff0c;简单且好用。为了让其更好用&#xff0c;同时支持拦截器&#xff0c;支持Promise 写法&#xff0c;特对其进行封装。同时支持H5和小程序环境&#xff0c;更好用啦。文中给出使用示例&#xff0c;可以看到使用变得如此…

【C++】——string模拟实现

前言 string的模拟实现其实就是增删改查&#xff0c;只不过加入了类的概念。 为了防止与std里面的string冲突&#xff0c;所以这里统一用String。 目录 前言 一 初始化和销毁 1.1 构造函数 1.2 析构函数 二 迭代器实现 三 容量大小及操作 四 运算符重载 4.1 bool…

Unity【入门】脚本基础

Unity脚本基础 文章目录 1、脚本基本规则1、创建规则2、MonoBehavior基类3、不继承MonoBehavior的类4、执行的先后顺序5、默认脚本内容 2、生命周期函数1、概念2、生命周期函数有哪些3、生命周期函数支持继承多态 3、Inspector窗口可编辑的变量4、Mono中的重要内容1、重要成员2…

冯喜运:5.31晚间黄金原油行情分析及尾盘操作策略

【黄金消息面分析】&#xff1a;周五&#xff08;5月31日&#xff09;&#xff0c;最新发布的数据显示&#xff0c;美国4月核心PCE物价指数月率录得0.2%&#xff0c;低于预期(0.3%)&#xff0c;经济学家认为&#xff0c;核心指数比整体指数更能反映通胀。除此之外&#xff0c;美…

HackTheBox-Machines--Sense

Popcorn 测试过程 1 信息收集 服务器开启80、443端口 80端口 访问 80 跳转到 443 – https://10.129.196.51/ &#xff0c;该页面是 pfSense 登录界面&#xff0c;默认密码是&#xff1a; admin/pfSense&#xff0c;使用默认账号密码登录失败 目录扫描 ./gobuster dir -u htt…

深度神经网络——什么是线性回归?

线性回归是一种用于预测或可视化的算法 两个不同特征/变量之间的关系。 在线性回归任务中&#xff0c;要检查两种变量&#xff1a; 因变量和自变量。 自变量是独立的变量&#xff0c;不受其他变量的影响。 随着自变量的调整&#xff0c;因变量的水平将会波动。 因变量是正在研究…

三体中的冯诺依曼

你叫冯诺依曼&#xff0c;是一位科学家。你无法形容眼前的现态&#xff0c;你不知道下一次自己葬身火海会是多久&#xff0c;你也不知道会不会下一秒就会被冰封&#xff0c;你唯一知道的&#xff0c;就是自己那寥寥无几的科学知识&#xff0c;你可能会抱着他们终身&#xff0c;…