虚幻引擎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才可以编辑CharacterName
	UPROPERTY(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表数据看看是否改变
    在这里插入图片描述
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

西门子电梯控制保姆级教程

一、电梯运行控制 1.电梯控制系统结构 可以理解是通过ip进行访问的 2.基于PLCSIM Adv与电梯仿真软件的控制环境搭建 虽然都是用一台电脑来控制&#xff0c;但是还是用以太网来连接 在FC块里面也要用两个DB块来放输入和输出 二、电梯对象的分析 在eet里面&#xff0c;用手动控制…

关于高版本 Plant Simulation 每次保存是 提示提交comm对话框的处理方法

关于高版本 Plant Simulation 每次保存是 提示提交comm对话框的处理方法 如下图 将model saving history 修改为None即可 关于AutoCAD 2022 丢失模板库的问题 从新从以下地址打开即可&#xff1a; D:\Program Files\Autodesk\AutoCAD 2022\UserDataCache\zh-cn\Template

LabVIEW步进电机的串口控制方法与实现

本文介绍了在LabVIEW环境中通过串口控制步进电机的方法&#xff0c;涵盖了基本的串口通信原理、硬件连接步骤、LabVIEW编程实现以及注意事项。通过这些方法&#xff0c;用户可以实现对步进电机的精确控制&#xff0c;适用于各种自动化和运动控制应用场景。 步进电机与串口通信…

python--面向对象-文件读写-异常

一、继承 定义一个类时&#xff0c;需要使用另外一个类的方法或属性&#xff0c;就可以通过继承实现 object是Python的顶级类&#xff0c;创建类是会自动继承&#xff0c;就拥有object中的方法 定义格式 # 类的定义 # 旧式类定义 一般在定义单个类时使用 class 类名:name N…

Nginx01-HTTP简介与Nginx简介(安装、命令介绍、目录介绍、配置文件介绍)

目录 HTTP简介HTTP原理查看访问网站的详细流程curl -vwget --debug 查看网站访问量HTTP协议版本HTTP协议交互HTTP 请求请求报文起始行请求头 HTTP响应响应报文起始行响应头 Nginx常见的Web服务常见网站服务 安装NginxNginx目录结构Nginx启动管理Nginx常用命令 Nginx配置文件主配…

牛客周赛 Round 45VP

这场应该是十分仁慈的一场了 1.签到&#xff1a;https://ac.nowcoder.com/acm/contest/84244/A AC代码&#xff1a; #include<bits/stdc.h> using namespace std; int a,b,c,d,e; int main() {cin>>a>>b>>c>>d>>e;int sabcde;if(s>1…

关于nodejs单线程

Node是使用C++语言写的一款JavaScrip解析器。 高并发 一般来说,高并发的解决方案就是多线程模型,服务器为每隔客户端请求分配一个线程,使用同步I/o,比如Apache就是这种策略,由于I/O一般都是耗时操作,因为这种策略很难实现高性能,但非常简单,可以实现复杂的交互逻辑。…

深度学习复盘与论文复现B

文章目录 1、Knowledge Review1.1 NLLLoss vs CrossEntropyLoss1.2 MNIST dataset1.2.1 Repare Dataset1.2.2 Design Model1.2.3 Construct Loss and Optimizer1.2.4 Train and Test1.2.5 Training results Pytorch-Lightning MNIST :rocket::fire:1.3 Basic Convolutional Neu…

都说美国去工业化了,那美国人都做什么工作啊?

美国&#xff0c;这个全球经济的重要参与者&#xff0c;经历了一场深刻的变革——去工业化。这一过程意味着&#xff0c;曾经以制造业为荣的美国&#xff0c;逐渐将重心转移到了其他领域。那么&#xff0c;美国人都做什么工作呢&#xff1f;让我们走近这位“经济体巨人”&#…

Nginx企业级负载均衡:技术详解系列(17)—— 长连接优化策略与下载服务器高效搭建

你好&#xff0c;我是赵兴晨&#xff0c;97年文科程序员。 今天咱们来聊聊Nginx的两个知识点&#xff1a;Nginx的长连接优化、如何将Nginx配置成下载服务器。 长连接配置详解 在Nginx的配置中&#xff0c;长连接是一个重要的性能优化手段。它允许一个TCP连接上发送多个请求和…

centos7下安装MySQL,Oracle数据库

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 操作系统版本为CentOS 7 使⽤ MySQ…

“人工智能AI+” 应用场景盘点

在这个科技与梦想交相辉映的时代&#xff0c;人工智能已不再停留于遥不可及的概念构想&#xff0c;而是化身为一股汹涌的创新洪流&#xff0c;深刻塑造着社会的每一个角落。从文化艺术的智慧火花到生命科学的精密探索&#xff0c;从工业制造的革新升级到日常生活的细致入微&…

解决sd-webui中rembg插件使用报错问题

stable-diffusion-webui-rembg是我非常喜欢的sd-webui插件&#xff0c;PS抠图手残党的福音&#xff0c;一键抠图太爽啦。对于主体明确线条简单的图片&#xff08;如汽车、服饰简洁的人像等&#xff09;效果相当好&#xff1b;即便对于毛茸茸的对象&#xff08;如动物&#xff0…

轻松上手MYSQL:优化MySQL慢查询,让数据库起飞

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索MYSQL慢查询之旅✨ &#x1f44b; 大家好&#xff01;我是你们的…

牛客网刷题 | BC112 空心三角形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

961题库 北航计算机 操作系统 附答案 选择题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 计算机系统的组成包括&#xff08; &#xff09; A、程序和数据 B、处理器和内存 C、计算机硬件和计算机软件 D、处理器、存储器和外围设备 财务软件是一种&#xff…

从 @MapperScan 看 Spring 和 MyBatis 的整合

在 Spring Boot 中使用 Mybatis 时&#xff0c;会经常使用到一个注解 MapperScan&#xff0c;它可以指定对哪些包进行扫描&#xff08;basePackages&#xff09;&#xff0c;将哪些类作为 Mapper 类&#xff08;basePackageClasses&#xff09;&#xff0c;多数据源下的一些设置…

【JavaScript详解】Day01

JavaScript 基础 - 第1天 了解变量、数据类型、运算符等基础概念&#xff0c;能够实现数据类型的转换&#xff0c;结合四则运算体会如何编程。 体会现实世界中的事物与计算机的关系理解什么是数据并知道数据的分类理解变量存储数据的“容器”掌握常见运算符的使用&#xff0c;了…

win+mac通用的SpringBoot+H2数据库集成过程。

有小部分大学的小部分老师多毛病&#xff0c;喜欢用些晦涩难搞的数据库来折腾学生&#xff0c;我不理解&#xff0c;但大受震撼。按我的理解&#xff0c;这种数据库看着好像本地快速测试代码很舒服&#xff0c;但依赖和数据库限制的很死板&#xff0c;对不上就是用不了&#xf…

对HTTP和HTTPS的介绍

HTTP HTTP 是什么&#xff1f; HTTP (全称为 “超⽂本传输协议”) 是⼀种应⽤⾮常⼴泛的 应用层协议. 所谓 “超⽂本” 的含义, 就是传输的内容不仅仅是⽂本(⽐如 html, css 这个就是⽂本), 还可以是⼀些其他的资源, ⽐如图⽚, 视频, ⾳频等⼆进制的数据 HTTP 往往是基于传输层…