虚幻引擎5 Gameplay框架(四)

news2025/1/17 23:26:24

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表数据看看是否改变
    在这里插入图片描述
    在这里插入图片描述

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

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

相关文章

6.5 作业

设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数。 #include <iostream>using namespace std; class Stu { privat…

低温测控芯片迎来突破性进展!

为支持大规模超导量子计算机的开发&#xff0c;日本最大的公共研究机构之一国家先进工业科学与技术研究所 (AIST) 的研究人员与横滨国立大学、东北大学&#xff08;日本国立大学之一&#xff09;和NEC公司合作&#xff0c;提出并成功演示了一种可在低温下控制许多量子比特的超导…

CSAPP Lab08——Proxy Lab完成思路

蓝色的思念 突然演变成了阳光的夏天 空气中的温暖不会很遥远 ——被风吹过的夏天 完整代码见&#xff1a;CSAPP/proxylab-handout at main SnowLegend-star/CSAPP (github.com) Q&#xff1a;计算机网络中port的作用是什么&#xff1f; A&#xff1a;在计算机网络中&#xff…

下载安装Grafana 监控mysql和Linux主机

下载地址:https://grafana.com/grafana/download [rootlocalhost ~]# wget https://dl.grafana.com/oss/release/grafana-7.2.0- 1.x86_64.rpm 安装 [rootlocalhost ~]# yum install grafana-7.2.0-1.x86_64.rpm -y启动服务 [rootlocalhost ~]# systemctl enable --now grafa…

AST 在前端开发中的应用与实践:从代码分析到自动化转换

抽象语法树&#xff08;AST&#xff0c;Abstract Syntax Tree&#xff09;在前端开发中有着广泛的应用。它是编译器和工具链的核心组件&#xff0c;使得代码分析、转换、优化等操作成为可能。在前端开发中&#xff0c;AST 主要用于代码编译和转译、代码优化、代码分析、代码格式…

C语言数字全排列生成器

前言 从0开始记录我的学习历程&#xff0c;我会尽我所能&#xff0c;写出最最大白话的文章&#xff0c;希望能够帮到你&#xff0c;谢谢。 提示&#xff1a;文章作者为初学者&#xff0c;有问题请评论指正&#xff0c;感谢。 这个代码的功能是生成并打印出从1到N的所有整数的…

Allegro-开店指南

开店指南 Allegro企业账户注册流程 Allegro注册流程分成两个主要阶段: 第一创建您的账户&#xff0c;第二激活您账户的销售功能。完成两个阶段&#xff0c;才能在Allegro进行销售。 中国企业应该入驻Business account&#xff08;企业账户&#xff09;。 第二阶段&#xff…

nginx中配置ssl证书(宝塔面板)

首先申请一个SSL证书&#xff0c;这里我申请的joyssl的免费证书。提交订单申请后&#xff0c;按照页面提示在域名解析中将CNAME和记录值配置好。 比如我用的阿里云&#xff0c; 这是好后&#xff0c;需要等几分钟&#xff0c;然后域名检验成功。 然后点击joyssl的左侧菜单的“证…

【新书上市】图像画质算法与底层视觉技术

图书主页&#xff1a;https://book.douban.com/subject/36895899/ 购买链接&#xff1a;https://item.jd.com/10105601481762.html 内容介绍 本书主要介绍了图像画质相关的各类底层视觉任务及其相关算法&#xff0c;重点讲解了去噪、超分辨率、去雾、高动态范围、图像合成与图…

conflicting types for 错误问题

操作系统真象还原中&#xff0c;第十一章出现的问题&#xff1a; 怎样编译都会出现一个conflicting types for ’xxx‘的错误 出现这个错误的原因&#xff1a; 头文件声明和定义参数稍有不同 头文件中声明 void Hanlder(const char * buf); 在定义时写作 void Hanlder(char…

双指针法 ( 三数之和 )

题目 &#xff1a;给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复…

嵌入式 Linux LED 驱动开发实验学习

I.MX6U-ALPHA 开发板上的 LED 连接到 I.MX6ULL 的 GPIO1_IO03 这个引脚上&#xff0c;进行这个驱动开发实验之前&#xff0c;需要了解下地址映射。 地址映射 MMU 全称叫做 MemoryManage Unit&#xff0c;也就是内存管理单元。在老版本的 Linux 中要求处理器必须有 MMU&#x…

Amazon云计算AWS(一)

目录 一、基础存储架构Dynamo&#xff08;一&#xff09;Dynamo概况&#xff08;二&#xff09;Dynamo架构的主要技术 二、弹性计算云EC2&#xff08;一&#xff09;EC2的基本架构&#xff08;二&#xff09;EC2的关键技术&#xff08;三&#xff09;EC2的安全及容错机制 提供的…

NXP i.MX8系列平台开发讲解 - 3.14 Linux 之Power Supply子系统(二)

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 目录 1. 前言 2. 芯片简介 2. 系统原理设计 2. 设备树相关 本文实操是基于Android11 系统下i.MX8MQ环境下&#x…

DKTCDR:Domain-Oriented Knowledge Transfer for Cross-Domain Recommendation

Domain-Oriented Knowledge Transfer for Cross-Domain Recommendation IEEE(CCF B.SCI 1)-Guoshuai Zhao, Xiaolong Zhang, Hao Tang, Jialie Shen, and Xueming Qian-2024 思路 在CDR中,构建连接两个域的桥梁是实现跨域推荐的基础。然而现在的CDR方法往往在连接两个域时忽…

STM32-- GPIO->EXTI->NVIC中断

一、NVIC简介 什么是 NVIC &#xff1f; NVIC 即嵌套向量中断控制器&#xff0c;全称 Nested vectored interrupt controller 。它 是内核的器件&#xff0c;所以它的更多描述可以看内核有关的资料。M3/M4/M7 内核都是支持 256 个中断&#xff0c;其中包含了 16 个系统中…

调用smc为什么就能直接切换到ATF?

快速链接: . &#x1f449;&#x1f449;&#x1f449;Trustzone/TEE/安全 面试100问-目录 &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】:联系方式-加入交流群 ----联系方式-加入交流群 个人博客笔记导读目录(全部) 背景 插播一个小插曲&#…

图片的dpi数值怎么修改?快速在线改图片dpi的操作技巧

在网上报名或者上传个人证件照片时&#xff0c;经常会对图片dpi数值有所要求&#xff0c;需要按照要求将图修改图片dpi到规定数值才可以正常上传&#xff0c;有很多人都对这个问题的感到非常的困扰&#xff0c;那么有什么方法能够快速在线改图片分辨率的dpi数值呢&#xff1f; …

WPF 依赖属性原理、 附加属性

依赖属性如何节约内存 MSDN中给出了下面几种应用依赖属性的场景&#xff1a; 希望可在样式中设置属性。 希望属性支持数据绑定。 希望可使用动态资源引用设置属性。 希望从元素树中的父元素自动继承属性值。 希望属性可进行动画处理。 希望属性系统在属性系统、环境或用户…

Wpf 使用 Prism 开发MyToDo应用程序

MyToDo 是使用 WPF &#xff0c;并且塔配Prism 框架进行开发的项目。项目中进行了前后端分离设计&#xff0c;客户端所有的数据均通过API接口获取。适合新手入门学习WPF以及Prism 框架使用。 首页统计以及点击导航到相关模块功能待办事项增删改查功能备忘录增删改查功能登录注册…