当前位置: 首页 > news >正文

防止网站被采集河南郑州网站制作公司

防止网站被采集,河南郑州网站制作公司,郑州专业做微信网站,python发wordpress实现角色的技能存档保存和加载 首先#xff0c;我们在LoadScreenSaveGame.h文件里#xff0c;增加一个结构体#xff0c;用于存储技能相关的所有信息 //存储技能的相关信息结构体 USTRUCT(BlueprintType) struct FSavedAbility {GENERATED_BODY()//需要存储的技能UPROPERT…实现角色的技能存档保存和加载 首先我们在LoadScreenSaveGame.h文件里增加一个结构体用于存储技能相关的所有信息 //存储技能的相关信息结构体 USTRUCT(BlueprintType) struct FSavedAbility {GENERATED_BODY()//需要存储的技能UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, CategoryClassDefaults)TSubclassOfUGameplayAbility GameplayAbility;//当前技能的等级UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)int32 AbilityLevel 0;//当前技能的标签UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)FGameplayTag AbilityTag FGameplayTag();//当前技能的状态标签UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)FGameplayTag AbilityStatus FGameplayTag();//当前技能装配到的插槽如果技能未装配则为空UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)FGameplayTag AbilityInputTag FGameplayTag();//当前技能的类型主动技能还是被动技能UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)FGameplayTag AbilityType FGameplayTag(); };然后重新设置运算符的方法如果我们在判断FSavedAbility是否相等时会通过此函数运算返回布尔结果。 //自定义运算符如果左右都是FSavedAbility类型的值将通过函数内的值判断是否相等。 inline bool operator(const FSavedAbility Left, const FSavedAbility Right) {return Left.AbilityTag.MatchesTagExact(Right.AbilityTag); }然后我们在SaveGame类里添加一个属性用于保存所有技能属性。 /************************** 技能 **************************/UPROPERTY()TArrayFSavedAbility SavedAbilities;有了相关数据可以存储到存档后我们可以在角色保存时实现保存技能相关数据。 在保存数据的时候我们先将存档里的技能数组清空然后使用RPGASC-ForEachAbility技能委托遍历所有应用到角色身上的技能通过结构体设置技能数据并通过AddUnique添加到数组前面我们添加了operator覆写了获取相等的设置 void ARPGHero::SaveProgress_Implementation(const FName CheckpointTag) {if(const ARPGGameMode* GameMode CastARPGGameMode(UGameplayStatics::GetGameMode(this))){...SaveGameData-bFirstTimeLoadIn false; //保存完成将第一次加载属性设置为falseif(!HasAuthority()) return;URPGAbilitySystemComponent* RPGASC CastURPGAbilitySystemComponent(AbilitySystemComponent);SaveGameData-SavedAbilities.Empty(); //清空数组//使用ASC里创建的ForEach函数循环获取角色的技能并生成技能结构体保存FForEachAbility SaveAbilityDelegate;SaveAbilityDelegate.BindLambda([this, RPGASC, SaveGameData](const FGameplayAbilitySpec AbilitySpec){//获取技能标签和const FGameplayTag AbilityTag URPGAbilitySystemComponent::GetAbilityTagFromSpec(AbilitySpec);UAbilityInfo* AbilityInfo URPGAbilitySystemLibrary::GetAbilityInfo(this);FRPGAbilityInfo Info AbilityInfo-FindAbilityInfoForTag(AbilityTag);//创建技能结构体FSavedAbility SavedAbility;SavedAbility.GameplayAbility Info.Ability;SavedAbility.AbilityLevel AbilitySpec.Level;SavedAbility.AbilityTag AbilityTag;SavedAbility.AbilityStatus RPGASC-GetStatusTagFromAbilityTag(AbilityTag);SavedAbility.AbilityInputTag RPGASC-GetInputTagFromAbilityTag(AbilityTag);SavedAbility.AbilityType Info.AbilityType;SaveGameData-SavedAbilities.AddUnique(SavedAbility);});//调用ForEach技能来执行存储到存档RPGASC-ForEachAbility(SaveAbilityDelegate);//保存存档GameMode-SaveInGameProgressData(SaveGameData);} }ForEach技能就是传入一个委托然后遍历所有的应用技能。 接着在ASC里增加一个通过存档数据设置角色技能的函数。 我们遍历存储的技能根据技能是主动技能还是被动技能应用给角色。 void URPGAbilitySystemComponent::AddCharacterAbilitiesFormSaveData(ULoadScreenSaveGame* SaveGameData) {for(const FSavedAbility Data : SaveGameData-SavedAbilities){const TSubclassOfUGameplayAbility LoadedAbilityClass Data.GameplayAbility;FGameplayAbilitySpec LoadedAbilitySpec FGameplayAbilitySpec(LoadedAbilityClass, Data.AbilityLevel);LoadedAbilitySpec.DynamicAbilityTags.AddTag(Data.AbilityInputTag); //设置技能激活输入标签LoadedAbilitySpec.DynamicAbilityTags.AddTag(Data.AbilityStatus); //设置技能状态标签//主动技能的处理if(Data.AbilityType FRPGGameplayTags::Get().Abilities_Type_Offensive){GiveAbility(LoadedAbilitySpec); //只应用不激活}//被动技能的处理else if(Data.AbilityType FRPGGameplayTags::Get().Abilities_Type_Passive){//确保技能已经装配if(Data.AbilityStatus.MatchesTagExact(FRPGGameplayTags::Get().Abilities_Status_Equipped)){GiveAbilityAndActivateOnce(LoadedAbilitySpec); //应用技能并激活}else{GiveAbility(LoadedAbilitySpec); //只应用不激活}}}bStartupAbilitiesGiven true;AbilityGivenDelegate.Broadcast(); }接下来我们编译打开UE查看现在角色身上装配的技能这些技能我们保存需要技能对应的资产数据 接着我们给一些不需要操作的被动技能添加标签这个被动技能主要是监听属性加点我们通过加点是会发送事件此技能会接受事件通过GE给角色增加属性值。 我们在技能的数据资产里增加对应的配置由于不需要显示我们不需要设置Icon 还有之前制作的眩晕技能也需要配置数据 删除多余的测试技能就可以去测试。 处理加载时的一些bug 首先第一个bug是我这边通过存档进入时激活的技能不显示这个问题的原因是初始化时对应的面板控制器没有初始化不使用存档时没这个问题是因为我们打开对应面板时进行的初始化。 解决方案是在HUD初始化面板时我们将对应的控制器都初始化一下即可 第二个问题被动技能特效无法显示出现这个问题的原因是因为在ASC通过存档初始化技能时对应的被动技能效果组件还没有执行绑定监听解决这个问题的方式是我们在绑定监听时调用一次即可。 实现保存场景状态 我们实现了对技能的保存接下来我们实现可以在存档里将角色进入过的场景的状态也保存。 我们要在存档里保存场景的数据首先在场景里创建对应的数据类型对于场景的Actor我们考虑使用结构体保存并通过UE内置的序列器进行对Actor的数据进行序列化存储到存档并在使用的时候通过反序列化应用会角色身上。 这里我们增加两个结构体一个用于保存关卡场景里的Actor另一个是保存所在的关卡这样 我们可以通过进入的关卡获取到对应关卡的Actor数据并应用回去。 //保存场景中的Actor结构体 USTRUCT() struct FSavedActor {GENERATED_BODY()UPROPERTY()FName ActorName FName();UPROPERTY()FTransform Transform FTransform();//Actor身上序列号的数据必须通过UPROPERTY定义过只在保存存档时使用。UPROPERTY()TArrayuint8 Bytes; };//自定义运算符如果结构体内的ActorName相同这代表这两个结构体为相同结构体 inline bool operator(const FSavedActor Left, const FSavedActor Right) {return Left.ActorName Right.ActorName; }//地图相关数据保存 USTRUCT() struct FSavedMap {GENERATED_BODY()UPROPERTY()FString MapAssetName FString();UPROPERTY()TArrayFSavedActor SavedActors; };接着我们在存档类里添加一个属性可以通过此属性可以获取到所有保存的关卡数据并增加两个函数 用于获取对应的关卡数据和判断存档里是否有对应关卡的数据。 UPROPERTY()TArrayFSavedMap SavedMaps;//通过地图名称获取地图数据FSavedMap GetSavedMapWithMapName(const FString InMapName);//判断存档是否含有对于地图数据bool HasMap(const FString InMapName);实现对应的函数通过遍历判断名称来实现。 FSavedMap ULoadScreenSaveGame::GetSavedMapWithMapName(const FString InMapName) {for(const FSavedMap Map : SavedMaps){if(Map.MapAssetName InMapName){return Map;}}return FSavedMap(); }bool ULoadScreenSaveGame::HasMap(const FString InMapName) {for(const FSavedMap Map : SavedMaps){if(Map.MapAssetName InMapName){return true;}}return false; }接着我们增加一个新的接口新的接口可以用于判断当前场景中的哪些Actor需要保存到存档里 我们命名新接口 需要保存的场景的Actor都需要继承此接口。 我们在接口里增加两个函数一个是用于判断是否需要修改位置变换这个功能可以在解密游戏里解开机关后开启了某扇密门后将其保存起来防止下次还需要继续重新开启。另一个是在从存档里读取数据后更新Actor。 // 版权归暮志未晚所有。#pragma once#include CoreMinimal.h #include UObject/Interface.h #include SaveInterface.generated.h// This class does not need to be modified. UINTERFACE(MinimalAPI) class USaveInterface : public UInterface {GENERATED_BODY() };/*** */ class RPG_API ISaveInterface {GENERATED_BODY()// Add interface functions to this class. This is the class that will be inherited to implement this interface. public://设置Actor是否需要修改位置变换UFUNCTION(BlueprintCallable, BlueprintNativeEvent)bool ShouldLoadTransform();//在存档读取完数据后调用更新ActorUFUNCTION(BlueprintCallable, BlueprintNativeEvent)void LoadActor(); }; 接着我们修改检查点的类我们需要将检查点的数据存储到存档里这里增加了一个变量用于判断当前检查点是否被激活并且覆写继承接口的函数。我们不需要修改存档点的位置变换这里直接返回false如果像机关密门那种则需要保存我们可以设置为true。 将bReached设置SaveGame时如果我们将实例序列化存档数据时它的值将会被存入到存档中在反序列化时将存档数据重新设置回来。 /* Save Interface */virtual bool ShouldLoadTransform_Implementation() override { return false; } //是否需要修改变换检查点不需要virtual void LoadActor_Implementation() override; //通过存档二进制修改Actor数据后更新Actor/* End Save Interface *///当前检查点是否已经被激活设置SaveGame表示该值将会被存储到存档文件中UPROPERTY(BlueprintReadOnly, SaveGame)bool bReached false;我们实现一下LoadActor函数在从存档读取数据设置完Actor调用它实现一些处理。 void ACheckPoint::LoadActor_Implementation() {if(bReached){HandleGlowEffects();} }实现场景状态的存储和读取 要实现场景状态的存储和读取我们在GameMode里增加两个函数分别处理存储和读取。 //保存关卡中的状态到当前存档中void SaveWorldState(UWorld* World) const;//从存档中加载当前关卡的状态void LoadWorldState(UWorld* World) const;首先我们实现对关卡的状态存储这里说一下逻辑首先获取到关卡名称和游戏实例游戏实例有存档的相关信息获取到存档对象判断对应关卡在存档内是否有对应的数据存储如果没有新创建一个。 然后获取到对应的关卡数据将数据内存储的Actor数据清除遍历关卡内的所有Actor找到继承保存接口的Actor创建一个对应Actor的存储数据结构体通过FMemoryWriter将Actor身上需要保存到存档的数据设置到存档结构体内。 最后将地图的数据存储到存档对象里。 void ARPGGameMode::SaveWorldState(UWorld* World) const {//获取关卡名称FString WorldName World-GetMapName();WorldName.RemoveFromStart(World-StreamingLevelsPrefix); //从关卡名称这里移除指定前缀当前为移除通常用于标识流式加载的关卡文件前缀//获取到游戏实例URPGGameInstance* RPGGI CastURPGGameInstance(GetGameInstance());check(RPGGI);//获取存档if(ULoadScreenSaveGame* SaveGame GetSaveSlotData(RPGGI-LoadSlotName, RPGGI-LoadSlotIndex)){if(!SaveGame-HasMap(WorldName)){//如果存档不包含对应关卡内容将创建一个对应的数据结构体存储FSavedMap NewSavedMap;NewSavedMap.MapAssetName WorldName;SaveGame-SavedMaps.Add(NewSavedMap);}//获取对应的存档关卡数据结构体FSavedMap SavedMap SaveGame-GetSavedMapWithMapName(WorldName);SavedMap.SavedActors.Empty(); //存储的内容//使用迭代起便利场景里的每一个Actor将需要保存Actor数据保存到结构体内for(FActorIterator It(World); It; It){AActor* Actor *It;//判断Actor是否存在并判断Actor是否需要存储if(!IsValid(Actor) || !Actor-ImplementsUSaveInterface()) continue;//创建存储结构体FSavedActor SavedActor;SavedActor.ActorName Actor-GetFName();SavedActor.Transform Actor-GetTransform();//创建一个 FMemoryWriter用于将数据写入SavedActor.BytesFMemoryWriter MemoryWriter(SavedActor.Bytes);//创建一个序列化器将对象的成员以名称和值的形式保存到 MemoryWriter。FObjectAndNameAsStringProxyArchive Archive(MemoryWriter, true);Archive.ArIsSaveGame true; //设置序列化方式为保存到存档的模式//将Actor所需要保存的数据写入到ArchiveArchive将把数据存储到SavedActor.BytesActor-Serialize(Archive);SavedMap.SavedActors.AddUnique(SavedActor);}//找到对应的名称的结构体将数据存储到存档对象内for(FSavedMap MapToReplace : SaveGame-SavedMaps){if(MapToReplace.MapAssetName WorldName){MapToReplace SavedMap;}}//保存存档UGameplayStatics::SaveGameToSlot(SaveGame, RPGGI-LoadSlotName, RPGGI-LoadSlotIndex);} }接着是加载关卡状态我们通过关卡名称和游戏实例获取到存档实例判断存档实例是否存在此关卡的存档数据。 如果存在此关卡数据我们将遍历此关卡的所有Actor如果Actor包含存储接口我们将判断存档里是否存储了此Actor对应的数据然后通过MemoryReader读取存档数据进行反序列化将数据应用回Actor。 最后通过接口函数调用Actor更新自身状态。 void ARPGGameMode::LoadWorldState(UWorld* World) const {//获取关卡名称FString WorldName World-GetMapName();WorldName.RemoveFromStart(World-StreamingLevelsPrefix); //从关卡名称这里移除指定前缀当前为移除通常用于标识流式加载的关卡文件前缀//获取到游戏实例URPGGameInstance* RPGGI CastURPGGameInstance(GetGameInstance());check(RPGGI);//判断获取的存档是否存在if(UGameplayStatics::DoesSaveGameExist(RPGGI-LoadSlotName, RPGGI-LoadSlotIndex)){//获取存档ULoadScreenSaveGame* SaveGame CastULoadScreenSaveGame(UGameplayStatics::LoadGameFromSlot(RPGGI-LoadSlotName, RPGGI-LoadSlotIndex));if(SaveGame nullptr){UE_LOG(LogRPG, Error, TEXT(加载对应存档失败));}//判断存档是否含有对应关卡的数据if(SaveGame-HasMap(WorldName)){//遍历场景内的所有Actor寻找存档内对应的数据并应用到场景for(FActorIterator It(World); It; It){AActor* Actor *It;if(!Actor-ImplementsUSaveInterface()) continue;//遍历存档里对应关卡的所有actor数据for(FSavedActor SavedActor : SaveGame-GetSavedMapWithMapName(WorldName).SavedActors){//查找到对应的actor的存档数据if(SavedActor.ActorName Actor-GetFName()){//判断当前Actor是否需要设置位置变换if(ISaveInterface::Execute_ShouldLoadTransform(Actor)){Actor-SetActorTransform(SavedActor.Transform);}//反序列化创建一个FMemoryReader实例用于从二进制数据中读取内容FMemoryReader MemoryReader(SavedActor.Bytes);//FObjectAndNameAsStringProxyArchive 代理类用于序列化和反序列化对象的属性 true表示允许使用字符串形式的对象和属性名称便于调试和可读性。FObjectAndNameAsStringProxyArchive Archive(MemoryReader, true);Archive.ArIsSaveGame true; //指定反序列化是用于加载存档数据。Actor-Serialize(Archive); //执行反序列化将二进制数据设置到actor属性上//修改Actor上的属性后调用函数更新Actor的显示ISaveInterface::Execute_LoadActor(Actor);}}} }} }实现场景存储和读取 对应的函数实现了最后一步我们需要知道在什么时候存储和什么时候去读取。 存储我们选择在玩家角色激活检查点时我们将bReached设置为true并调用保存状态函数将数据保存到关卡内。 void ACheckPoint::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult SweepResult) {//if(OtherActor-ActorHasTag(Player)) //如果只需要判断是不是玩家角色通过标签判断即可if(OtherActor-ImplementsUPlayerInterface()){//设置当前检查点已被玩家激活bReached true;if(ARPGGameMode* RPGGameMode CastARPGGameMode(UGameplayStatics::GetGameMode(this))){//保存场景状态RPGGameMode-SaveWorldState(GetWorld());}//修改存档当的检测点IPlayerInterface::Execute_SaveProgress(OtherActor, PlayerStartTag);//如果与碰撞体重叠的是HandleGlowEffects();} }在读取时我们选择在玩家PossessedBy里它将调用加载存档函数我们将读取写在读取完技能之后实现场景的加载。 最后测试即可。
http://www.hkea.cn/news/14562269/

相关文章:

  • 柳州网站建设外贸cms什么意思
  • 个人网站名称举例工作总结及工作计划
  • 手机网站创建站点成功黑猫会活动策划网站
  • 怎么制作网站视频教程seo排名优化教程
  • 怎么做自己的网站赚钱做优惠券网站要多少钱
  • 青岛网站建设找润商文化传媒有限公司
  • 深圳 网站开发公司热点事件舆情分析
  • 北京网站设计联系电话淘宝网网页版登录入口在哪里
  • 多语言外贸企业网站源码免费好用的crm系统
  • 网站建设经验典型做期货要关注哪些网站
  • 八卦岭网站建设百度电脑版官网下载
  • 深圳国内网站设计公司仿银行网站 asp
  • 淘宝网站开发实训报告2023网站推荐
  • 做网站为什么赚钱小学校园网站建设方案工作职责
  • ppt模板免费下载网站哪个好个人成立咨询公司的条件
  • vs网站制作教程开发一个打车软件需要多少钱
  • 大同网站建设优化推广四川今天刚刚发生的新闻
  • 张家界企业网站制作代理网络设置
  • 网站建设公司ipo网站优缺点分析
  • 关于建设招商网站的通知文化建设设计公司网站
  • seo网站建设刘贺稳营销专家a品牌商标设计logo
  • 黑龙江省建设网站首页wordpress打开网站加速
  • 青岛公司网站建设价格低四川省建设工程造价信息网站
  • 美食网站开发的难点免费ppt模板年终总结
  • php网站建设招聘一个主体可以备案几个网站
  • 网站内容好做住宿的有几个网站
  • 网站如何进行优化设计高职院校优质校建设专栏网站
  • 上海网站推广优化wordpress加载ajax
  • 安徽建筑工程网站麻六记网络营销方式
  • 城乡建设部官方网站网站名称大全