news 2026/4/18 7:34:42

LyraStarterGame_5.6 Experience系统加载流程详细实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LyraStarterGame_5.6 Experience系统加载流程详细实现

1. 加载流程概述

Lyra的Experience系统采用异步加载模式,确保游戏在加载过程中保持响应性。完整的加载流程包含以下状态转换:

Unloaded → Loading → LoadingGameFeatures → ExecutingActions → Loaded

2. 详细流程实现

2.1 设置当前经验

voidULyraExperienceManagerComponent::SetCurrentExperience(FPrimaryAssetId ExperienceId){ULyraAssetManager&AssetManager=ULyraAssetManager::Get();FSoftObjectPath AssetPath=AssetManager.GetPrimaryAssetPath(ExperienceId);TSubclassOf<ULyraExperienceDefinition>AssetClass=Cast<UClass>(AssetPath.TryLoad());check(AssetClass);constULyraExperienceDefinition*Experience=GetDefault<ULyraExperienceDefinition>(AssetClass);check(Experience!=nullptr);check(CurrentExperience==nullptr);CurrentExperience=Experience;StartExperienceLoad();}

关键实现点:

  • 通过ULyraAssetManager获取经验资源路径
  • 尝试加载资源类并转换为ULyraExperienceDefinition类型
  • 验证经验有效性并设置为当前经验
  • 调用StartExperienceLoad()开始加载流程

2.2 开始经验加载

voidULyraExperienceManagerComponent::StartExperienceLoad(){check(CurrentExperience!=nullptr);check(LoadState==ELyraExperienceLoadState::Unloaded);UE_LOG(LogLyraExperience,Log,TEXT("EXPERIENCE: StartExperienceLoad(CurrentExperience = %s, %s)"),*CurrentExperience->GetPrimaryAssetId().ToString(),*GetClientServerContextString(this));LoadState=ELyraExperienceLoadState::Loading;ULyraAssetManager&AssetManager=ULyraAssetManager::Get();TSet<FPrimaryAssetId>BundleAssetList;TSet<FSoftObjectPath>RawAssetList;BundleAssetList.Add(CurrentExperience->GetPrimaryAssetId());for(constTObjectPtr<ULyraExperienceActionSet>&ActionSet:CurrentExperience->ActionSets){if(ActionSet!=nullptr){BundleAssetList.Add(ActionSet->GetPrimaryAssetId());}}TArray<FName>BundlesToLoad;BundlesToLoad.Add(FLyraBundles::Equipped);constENetMode OwnerNetMode=GetOwner()->GetNetMode();constboolbLoadClient=GIsEditor||(OwnerNetMode!=NM_DedicatedServer);constboolbLoadServer=GIsEditor||(OwnerNetMode!=NM_Client);if(bLoadClient)BundlesToLoad.Add(UGameFeaturesSubsystemSettings::LoadStateClient);if(bLoadServer)BundlesToLoad.Add(UGameFeaturesSubsystemSettings::LoadStateServer);TSharedPtr<FStreamableHandle>BundleLoadHandle=nullptr;if(BundleAssetList.Num()>0){BundleLoadHandle=AssetManager.ChangeBundleStateForPrimaryAssets(BundleAssetList.Array(),BundlesToLoad,{},false,FStreamableDelegate(),FStreamableManager::AsyncLoadHighPriority);}TSharedPtr<FStreamableHandle>RawLoadHandle=nullptr;if(RawAssetList.Num()>0){RawLoadHandle=AssetManager.LoadAssetList(RawAssetList.Array(),FStreamableDelegate(),FStreamableManager::AsyncLoadHighPriority,TEXT("StartExperienceLoad()"));}TSharedPtr<FStreamableHandle>Handle=nullptr;if(BundleLoadHandle.IsValid()&&RawLoadHandle.IsValid()){Handle=AssetManager.GetStreamableManager().CreateCombinedHandle({BundleLoadHandle,RawLoadHandle});}else{Handle=BundleLoadHandle.IsValid()?BundleLoadHandle:RawLoadHandle;}FStreamableDelegate OnAssetsLoadedDelegate=FStreamableDelegate::CreateUObject(this,&ThisClass::OnExperienceLoadComplete);if(!Handle.IsValid()||Handle->HasLoadCompleted()){FStreamableHandle::ExecuteDelegate(OnAssetsLoadedDelegate);}else{Handle->BindCompleteDelegate(OnAssetsLoadedDelegate);Handle->BindCancelDelegate(FStreamableDelegate::CreateLambda([OnAssetsLoadedDelegate](){OnAssetsLoadedDelegate.ExecuteIfBound();}));}TSet<FPrimaryAssetId>PreloadAssetList;if(PreloadAssetList.Num()>0){AssetManager.ChangeBundleStateForPrimaryAssets(PreloadAssetList.Array(),BundlesToLoad,{});}}

关键实现点:

  • 验证当前经验有效性和加载状态
  • 记录加载日志,包含经验ID和客户端/服务器上下文
  • 转换状态为ELyraExperienceLoadState::Loading
  • 构建要加载的资源列表,包括经验定义和相关动作集
  • 根据网络模式(客户端/服务器/编辑器)确定要加载的资源包
  • 使用FStreamableHandle进行异步资源加载
  • 支持合并多个加载请求,提高效率
  • 绑定资源加载完成回调
  • 支持预加载额外资源(当前留空)

2.3 经验资源加载完成

voidULyraExperienceManagerComponent::OnExperienceLoadComplete(){check(LoadState==ELyraExperienceLoadState::Loading);check(CurrentExperience!=nullptr);UE_LOG(LogLyraExperience,Log,TEXT("EXPERIENCE: OnExperienceLoadComplete(CurrentExperience = %s, %s)"),*CurrentExperience->GetPrimaryAssetId().ToString(),*GetClientServerContextString(this));GameFeaturePluginURLs.Reset();autoCollectGameFeaturePluginURLs=[This=this](constUPrimaryDataAsset*Context,constTArray<FString>&FeaturePluginList){for(constFString&PluginName:FeaturePluginList){FString PluginURL;if(UGameFeaturesSubsystem::Get().GetPluginURLByName(PluginName,/*out*/PluginURL)){This->GameFeaturePluginURLs.AddUnique(PluginURL);}else{ensureMsgf(false,TEXT("OnExperienceLoadComplete failed to find plugin URL from PluginName %s for experience %s - fix data, ignoring for this run"),*PluginName,*Context->GetPrimaryAssetId().ToString());}}};CollectGameFeaturePluginURLs(CurrentExperience,CurrentExperience->GameFeaturesToEnable);for(constTObjectPtr<ULyraExperienceActionSet>&ActionSet:CurrentExperience->ActionSets){if(ActionSet!=nullptr){CollectGameFeaturePluginURLs(ActionSet,ActionSet->GameFeaturesToEnable);}}NumGameFeaturePluginsLoading=GameFeaturePluginURLs.Num();if(NumGameFeaturePluginsLoading>0){LoadState=ELyraExperienceLoadState::LoadingGameFeatures;for(constFString&PluginURL:GameFeaturePluginURLs){ULyraExperienceManager::NotifyOfPluginActivation(PluginURL);UGameFeaturesSubsystem::Get().LoadAndActivateGameFeaturePlugin(PluginURL,FGameFeaturePluginLoadComplete::CreateUObject(this,&ThisClass::OnGameFeaturePluginLoadComplete));}}else{OnExperienceFullLoadCompleted();}}

关键实现点:

  • 验证加载状态和当前经验有效性
  • 记录资源加载完成日志
  • 收集游戏特性插件URL的Lambda函数
  • 从经验定义和动作集中收集需要加载的游戏特性插件
  • 转换状态为ELyraExperienceLoadState::LoadingGameFeatures
  • 异步加载并激活游戏特性插件
  • 支持无插件情况下直接完成加载

2.4 游戏特性插件加载完成

voidULyraExperienceManagerComponent::OnGameFeaturePluginLoadComplete(constUE::GameFeatures::FResult&Result){NumGameFeaturePluginsLoading--;if(NumGameFeaturePluginsLoading==0){OnExperienceFullLoadCompleted();}}

关键实现点:

  • 递减正在加载的插件计数
  • 当所有插件加载完成后,调用最终完成函数

2.5 经验完全加载完成

voidULyraExperienceManagerComponent::OnExperienceFullLoadCompleted(){check(LoadState!=ELyraExperienceLoadState::Loaded);if(LoadState!=ELyraExperienceLoadState::LoadingChaosTestingDelay){constfloatDelaySecs=LyraConsoleVariables::GetExperienceLoadDelayDuration();if(DelaySecs>0.0f){FTimerHandle DummyHandle;LoadState=ELyraExperienceLoadState::LoadingChaosTestingDelay;GetWorld()->GetTimerManager().SetTimer(DummyHandle,this,&ThisClass::OnExperienceFullLoadCompleted,DelaySecs,/*bLooping=*/false);return;}}LoadState=ELyraExperienceLoadState::ExecutingActions;FGameFeatureActivatingContext Context;constFWorldContext*ExistingWorldContext=GEngine->GetWorldContextFromWorld(GetWorld());if(ExistingWorldContext){Context.SetRequiredWorldContextHandle(ExistingWorldContext->ContextHandle);}autoActivateListOfActions=[&Context](constTArray<UGameFeatureAction*>&ActionList){for(UGameFeatureAction*Action:ActionList){if(Action!=nullptr){Action->OnGameFeatureRegistering();Action->OnGameFeatureLoading();Action->OnGameFeatureActivating(Context);}}};ActivateListOfActions(CurrentExperience->Actions);for(constTObjectPtr<ULyraExperienceActionSet>&ActionSet:CurrentExperience->ActionSets){if(ActionSet!=nullptr){ActivateListOfActions(ActionSet->Actions);}}LoadState=ELyraExperienceLoadState::Loaded;OnExperienceLoaded_HighPriority.Broadcast(CurrentExperience);OnExperienceLoaded_HighPriority.Clear();OnExperienceLoaded.Broadcast(CurrentExperience);OnExperienceLoaded.Clear();OnExperienceLoaded_LowPriority.Broadcast(CurrentExperience);OnExperienceLoaded_LowPriority.Clear();#if!UE_SERVERULyraSettingsLocal::Get()->OnExperienceLoaded();#endif}

关键实现点:

  • 验证非已加载状态
  • 支持混沌测试延迟(用于模拟加载延迟)
  • 转换状态为ELyraExperienceLoadState::ExecutingActions
  • 创建游戏特性激活上下文,包含世界上下文信息
  • 激活动作列表的Lambda函数
  • 执行经验定义和动作集中的所有动作(注册→加载→激活)
  • 转换状态为ELyraExperienceLoadState::Loaded
  • 按优先级广播加载完成事件:
    • 高优先级(核心功能)
    • 普通优先级
    • 低优先级
  • 在客户端调用本地设置的经验加载完成回调

3. 网络同步机制

Lyra的Experience系统支持网络同步,确保客户端和服务器使用相同的经验配置:

voidULyraExperienceManagerComponent::OnRep_CurrentExperience(){StartExperienceLoad();}voidULyraExperienceManagerComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>&OutLifetimeProps)const{Super::GetLifetimeReplicatedProps(OutLifetimeProps);DOREPLIFETIME(ThisClass,CurrentExperience);}

关键实现点:

  • 当前经验通过DOREPLIFETIME宏自动复制到客户端
  • 客户端在收到复制的经验后,自动调用StartExperienceLoad()开始加载
  • 加载流程在客户端和服务器上独立执行,但最终结果一致

4. 加载状态管理

Experience系统使用ELyraExperienceLoadState枚举管理加载状态:

enumclassELyraExperienceLoadState{Unloaded,// 未加载状态Loading,// 加载经验资源LoadingGameFeatures,// 加载游戏特性插件LoadingChaosTestingDelay,// 混沌测试延迟ExecutingActions,// 执行经验动作Loaded,// 完全加载完成Deactivating// 停用经验};

5. 加载流程的取消和清理

系统支持加载过程中的取消处理:

Handle->BindCancelDelegate(FStreamableDelegate::CreateLambda([OnAssetsLoadedDelegate](){OnAssetsLoadedDelegate.ExecuteIfBound();}));

在组件结束时,会尝试停用已激活的游戏特性插件:

voidULyraExperienceManagerComponent::EndPlay(constEEndPlayReason::Type EndPlayReason){Super::EndPlay(EndPlayReason);for(constFString&PluginURL:GameFeaturePluginURLs){if(ULyraExperienceManager::RequestToDeactivatePlugin(PluginURL)){UGameFeaturesSubsystem::Get().DeactivateGameFeaturePlugin(PluginURL);}}// 停用和清理动作// ...}

6. 加载状态查询

boolULyraExperienceManagerComponent::IsExperienceLoaded()const{return(LoadState==ELyraExperienceLoadState::Loaded)&&(CurrentExperience!=nullptr);}constULyraExperienceDefinition*ULyraExperienceManagerComponent::GetCurrentExperienceChecked()const{check(LoadState==ELyraExperienceLoadState::Loaded);check(CurrentExperience!=nullptr);returnCurrentExperience;}

7. 总结

Lyra的Experience系统加载流程具有以下特点:

  1. 异步加载模式:确保游戏在加载过程中保持响应性
  2. 状态驱动:通过明确的状态转换管理加载过程
  3. 模块化设计:将资源加载、插件激活和动作执行分离
  4. 网络同步:确保客户端和服务器使用相同的经验配置
  5. 优先级事件系统:支持不同优先级的加载完成回调
  6. 可测试性:包含混沌测试延迟功能
  7. 可扩展性:支持通过动作集扩展经验功能

这种设计确保了Experience系统的灵活性和可维护性,同时提供了良好的用户体验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/22 15:16:03

3个痛点一个方案:B站Linux客户端的终极观影指南

还在为Linux系统上看不了B站而烦恼吗&#xff1f;作为Linux用户&#xff0c;你是不是经常遇到这些问题&#xff1a;区域限制打不开番剧、官方客户端不支持Linux、网页版体验不够沉浸&#xff1f;今天我要给你介绍一个免费开源的终极解决方案——B站Linux客户端&#xff0c;让你…

作者头像 李华
网站建设 2026/4/18 9:18:51

联想拯救者工具箱:轻量化硬件控制新体验

联想拯救者工具箱&#xff1a;轻量化硬件控制新体验 【免费下载链接】LenovoLegionToolkit Lightweight Lenovo Vantage and Hotkeys replacement for Lenovo Legion laptops. 项目地址: https://gitcode.com/gh_mirrors/le/LenovoLegionToolkit 还在为官方软件占用系统…

作者头像 李华
网站建设 2026/4/17 19:31:29

decimal.js 高精度数值计算库完整实战指南

decimal.js 高精度数值计算库完整实战指南 【免费下载链接】decimal.js An arbitrary-precision Decimal type for JavaScript 项目地址: https://gitcode.com/gh_mirrors/de/decimal.js 项目简介 decimal.js 是一款专为 JavaScript 设计的任意精度十进制数计算库&…

作者头像 李华
网站建设 2026/4/17 10:09:39

LosslessCut终极指南:零基础掌握无损视频剪辑技术

还在为视频剪辑导致的画质下降而烦恼吗&#xff1f;LosslessCut这款革命性的无损视频处理工具&#xff0c;让每个人都能在不重新编码的情况下完成专业级的视频编辑任务。无论是从长视频中提取精彩片段&#xff0c;还是为不同设备优化视频格式&#xff0c;这款被誉为"音视频…

作者头像 李华
网站建设 2026/4/18 3:27:51

OpenBoardView:完全免费的.brd电路板文件终极查看方案

OpenBoardView&#xff1a;完全免费的.brd电路板文件终极查看方案 【免费下载链接】OpenBoardView View .brd files 项目地址: https://gitcode.com/gh_mirrors/op/OpenBoardView 在电子设计领域&#xff0c;工程师们常常面临一个现实问题&#xff1a;如何在没有昂贵专业…

作者头像 李华