107. UE5 GAS RPG 实现存档
游戏的存档功能是为了记录当前的游戏进度,然后在下次游玩时,玩家可以通过存档在当前进度继续游玩。
要实现存档功能,我们要先明白哪些数据需要存储。
在这个教程里,我们需要存储角色的主要属性,次级属性是根据主要属性计算出来的,不需要存储。我们还要存储当前角色未分配的技能点和属性点,还有经验值和玩家等级。
还有重要的一项就是玩家设置的技能相关,我们可以通过技能标签记录玩家修改的对应的技能的相关数据,比如设置的技能等级,是否装配到技能栏等。
最后,我们还需要保存玩家角色所在的地图以及位置等一些额外信息。
如果是多人游戏,我们还需要记录玩家的角色ID,可以通过ID获取对应角色的相关信息。
存储存储的位置主要有两种,就是存储到本地磁盘这是单机游戏常用的方式,另外一种是存储到远程服务器的数据库,这种是网络游戏常用的方式。
首先,我们将实现,通过点击创建新存档页面,实现创建一个新的存档,将跳转到输入名称用户控件,输入角色名称,然后点击新存档按钮,将创建一个新的存档,并跳转到进入游戏用户控件,并显示当前的角色名称,点击进入游戏便可进入游戏。
添加保存游戏类
这里,我们首先创建一个新的类,用于实现存档功能,在5.3以后,增加了一个LocalPlayerSaveGame基类,它可以实现与特定的本地玩家关联,也就是可以实现本地多人游戏,教程里有可能不涉及,但是希望大家能明白有这个东西。
命名,作为玩家存档类
在类里,我们增加一些所需的内容,比如存档的名称,当前存档的索引,以及玩家名称。在顶部增加了一个枚举,用于在玩家进入加载界面时,如果获取到存档,将按照存档的设置的枚举进行显示。(存档有三个用户界面切换,通过一个切换器进行切换,枚举代表对应的索引)
//当前存档可以显示的用户控件的枚举
UENUM(BlueprintType)
enum ESaveSlotStatus
{
Vacant,
EnterName,
Taken
};
/**
*
*/
UCLASS()
class RPG_API ULoadScreenSaveGame : public ULocalPlayerSaveGame
{
GENERATED_BODY()
public:
//存档名称
UPROPERTY()
FString SlotName = FString();
//存档索引
UPROPERTY()
int32 SlotIndex = 0;
//玩家姓名
UPROPERTY()
FString PlayerName = FString("Default Name");
//当前存档进入存档界面时,默认显示的用户界面
UPROPERTY()
TEnumAsByte<ESaveSlotStatus> SaveSlotStatus = Vacant;
};
要实现存档,肯定需要保存存档和加载存档功能,由于这种功能使用的地方比较多,我们将其功能设置到GameMode里,这样在任何地方都可以进行调用。
我们在GameMode里添加一个设置使用的存档类,类将会在UE里通过上面创建的c++创建一个蓝图类去设置。
然后增加两个函数,分别用于重新创建并保存存档和获取。
/**
* 创建新存档
* @param LoadSlot 需要保存的视图模型示例
* @param SlotIndex 存档索引
*/
void SaveSlotData(const UMVVM_LoadSlot* LoadSlot, int32 SlotIndex) const;
/**
* 获取保存的存档
* @param SlotName 存档名称(每个存档名称固定)
* @param SlotIndex 存档索引
* @return
*/
ULoadScreenSaveGame* GetSaveSlotData(const FString& SlotName, int32 SlotIndex) const;
//存档使用的数据结构
UPROPERTY(EditDefaultsOnly)
TSubclassOf<USaveGame> LoadScreenSaveGameClass;
在cpp文件里,我们实现加载和保存。
保存功能首先将之前保存的存档删除,然后再创建一个新的存档,设置数据保存。
获取函数是判断存档是否存在,存在则获取对应存档,不存在就创建一个默认的存档。默认存档则是默认显示第一个界面,和没有存档显示一致。
void ARPGGameMode::SaveSlotData(const UMVVM_LoadSlot* LoadSlot, const int32 SlotIndex) const
{
//检查是否有对应名称的存档
if(UGameplayStatics::DoesSaveGameExist(LoadSlot->GetSlotName(), SlotIndex))
{
//删除已保存的存档
UGameplayStatics::DeleteGameInSlot(LoadSlot->GetSlotName(), SlotIndex);
}
//创建一个新的存档
USaveGame* SaveGameObject = UGameplayStatics::CreateSaveGameObject(LoadScreenSaveGameClass);
ULoadScreenSaveGame* LoadScreenSaveGame = Cast<ULoadScreenSaveGame>(SaveGameObject);
//设置需要保存的数据
LoadScreenSaveGame->PlayerName = LoadSlot->GetPlayerName();
LoadScreenSaveGame->SlotName = LoadSlot->GetSlotName();
LoadScreenSaveGame->SlotIndex = SlotIndex;
LoadScreenSaveGame->SaveSlotStatus = Taken;
//保存存档
UGameplayStatics::SaveGameToSlot(LoadScreenSaveGame, LoadSlot->GetSlotName(), SlotIndex);
}
ULoadScreenSaveGame* ARPGGameMode::GetSaveSlotData(const FString& SlotName, int32 SlotIndex) const
{
USaveGame* SaveGameObject;
//检查是否有对应名称的存档
if(UGameplayStatics::DoesSaveGameExist(SlotName, SlotIndex))
{
//获取存档
SaveGameObject = UGameplayStatics::LoadGameFromSlot(SlotName, SlotIndex);
}
else
{
//创建新存档
SaveGameObject = UGameplayStatics::CreateSaveGameObject(LoadScreenSaveGameClass);
}
//转换类型
ULoadScreenSaveGame* LoadScreenSaveGame = Cast<ULoadScreenSaveGame>(SaveGameObject);
return LoadScreenSaveGame;
}
接着,我们修改每个存档的用户控件使用的视图模型。在此类里,我们额外增加了存档的索引,进入加载存档界面,存档需要显示的用户控件枚举,和角色名称。
UCLASS()
class RPG_API UMVVM_LoadSlot : public UMVVMViewModelBase
{
GENERATED_BODY()
public:
//切换存档显示的用户控件的委托
UPROPERTY(BlueprintAssignable)
FSetWidgetSwitcherIndex SetWidgetSwitcherIndex;
void InitializeSlot() const;
//当前视图模型的索引,对应存档的索引
UPROPERTY()
int32 SlotIndex;
//当前进入加载存档界面时,此存档应该显示的用户控件界面。
UPROPERTY()
TEnumAsByte<ESaveSlotStatus> LoadSlotStatus;
void SetSlotName(const FString& InSlotName);
FString GetSlotName() const { return SlotName; };
void SetPlayerName(const FString& InPlayerName);
FString GetPlayerName() const { return PlayerName; };
private:
//用户控件的名称
UPROPERTY(BlueprintReadOnly, FieldNotify, Setter, Getter, meta=(AllowPrivateAccess))
FString SlotName;
//用户设置的角色名称
UPROPERTY(BlueprintReadOnly, FieldNotify, Setter, Getter, meta=(AllowPrivateAccess))
FString PlayerName;
};
在cpp里,我们修改初始化函数,从枚举里获取到索引,然后通过委托广播。
void UMVVM_LoadSlot::InitializeSlot() const
{
//从枚举获取到对应的索引
const int32 WidgetSwitcherIndex = LoadSlotStatus.GetValue();
//广播切换
SetWidgetSwitcherIndex.Broadcast(WidgetSwitcherIndex);
}
然后设置角色名称通过宏去调用,这样在修改参数的同时,会自动进行广播对UI进行更新。
void UMVVM_LoadSlot::SetPlayerName(const FString& InPlayerName)
{
UE_MVVM_SET_PROPERTY_VALUE(PlayerName, InPlayerName);
}
我们接着在加载界面使用的视图模型里增加一个加载存档的函数
void LoadData();
接着实现此函数,我们获取到GameMode,然后通过枚举进行对所有存档进行遍历,通过GameMode去获取存档,然后修改每个存档使用的视图模型的参数,最后调用初始化,初始化函数里会通过保存的枚举修改存档显示的用户控件。
void UMVVM_LoadScreen::LoadData()
{
//获取到加载存档界面的GameMode
ARPGGameMode* RPGGameMode = Cast<ARPGGameMode>(UGameplayStatics::GetGameMode(this));
//遍历映射,获取对应存档
for(const TTuple<int32, UMVVM_LoadSlot*> Slot : LoadSlots)
{
ULoadScreenSaveGame* SaveGame = RPGGameMode->GetSaveSlotData(Slot.Value->GetSlotName(), Slot.Key);
//获取存档数据
const FString PlayerName = SaveGame->PlayerName;
const TEnumAsByte<ESaveSlotStatus> SaveSlotStatus = SaveGame->SaveSlotStatus;
//设置存档视图模型数据
Slot.Value->SetPlayerName(PlayerName);
Slot.Value->LoadSlotStatus = SaveSlotStatus;
//调用视图模型初始化
Slot.Value->InitializeSlot();
}
}
然后我们修改创建新存档事件,在加载界面点击创建新存档后,我们将存档保存到硬盘,存档名称是在初始化时就创建的,而索引我们直接保存时作为参数传入,所以,存档的四个参数都得以保存。最后在调用初始化,更新存档显示的用户空间。
void UMVVM_LoadScreen::NewSlotButtonPressed(const int32 Slot, const FString& EnterName)
{
ARPGGameMode* RPGGameMode = Cast<ARPGGameMode>(UGameplayStatics::GetGameMode(this));
LoadSlots[Slot]->SetPlayerName(EnterName); //修改MVVM上存储的角色名称
LoadSlots[Slot]->LoadSlotStatus = Taken; //修改进入界面为加载界面
RPGGameMode->SaveSlotData(LoadSlots[Slot], Slot); //保存数据
LoadSlots[Slot]->InitializeSlot(); //调用初始化
}
接着,我们编译代码,打开UE,基于存档类创建一个蓝图类。
然后在GameMode蓝图类上设置对应的类名。
在修改名称用户控件里,我们将文本修改为可编辑文本。
在点击 新存档 按钮时,我们调用加载界面的保存新存档函数,将用户创建的角色名称保存下来。
接着,就可以测试效果了。
如果在编辑器里测试,存档将存储在 (项目目录-Saved-SaveGames)目录里,我们以视图模型作为名称,方便查找。
原文地址:https://blog.csdn.net/qq_30100043/article/details/143689610
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!