ue5 lyra探索分析1
问题1:
这个地图的worldsetting中并没有指定gamemode,那么游戏运行的时候角色的类型是怎么指定的呢?
有人说在projectsetting中有一个全局的gamemode
但是这个角色的类型也不是真的运行起来角色的类型
真的运行的时候角色的类型是 如上图所示
找一下吧,看看具体是在哪里设定的,过程是如何的。茫茫大海如何着手呢,我先在lyracharacter的构造函数打个断点看看
从堆栈中找到一个有用的信息
从图上看spawn的时候已经指定好了,而且这个函数是重载基类的方法,继续跟代码。
UClass* ALyraGameMode::GetDefaultPawnClassForController_Implementation(AController* InController)
{if (const ULyraPawnData* PawnData = GetPawnDataForController(InController)){if (PawnData->PawnClass){return PawnData->PawnClass;}}return Super::GetDefaultPawnClassForController_Implementation(InController);
}
重载了父类 的方法,这里出现了ULyraPawnData这个数据类型,继续跟代码
发现首先从gamestate上找到一个叫ULyraExperienceManagerComponent组件
从组件上获得一个ULyraExperienceDefinition数据类型的数据。
继续找
const ULyraExperienceDefinition* ULyraExperienceManagerComponent::GetCurrentExperienceChecked() const
{check(LoadState == ELyraExperienceLoadState::Loaded);check(CurrentExperience != nullptr);return CurrentExperience;
}
void ALyraGameMode::OnMatchAssignmentGiven(FPrimaryAssetId ExperienceId, const FString& ExperienceIdSource)
{
#if WITH_SERVER_CODEif (ExperienceId.IsValid()){UE_LOG(LogLyraExperience, Log, TEXT("Identified experience %s (Source: %s)"), *ExperienceId.ToString(), *ExperienceIdSource);ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();check(ExperienceComponent);ExperienceComponent->ServerSetCurrentExperience(ExperienceId);}else{UE_LOG(LogLyraExperience, Error, TEXT("Failed to identify experience, loading screen will stay up forever"));}
#endif
}
void ALyraGameMode::HandleMatchAssignmentIfNotExpectingOne()
{FPrimaryAssetId ExperienceId;FString ExperienceIdSource;// Precedence order (highest wins)// - Matchmaking assignment (if present)// - URL Options override// - Developer Settings (PIE only)// - Command Line override// - World Settings// - Default experienceUWorld* World = GetWorld();if (!ExperienceId.IsValid() && UGameplayStatics::HasOption(OptionsString, TEXT("Experience"))){const FString ExperienceFromOptions = UGameplayStatics::ParseOption(OptionsString, TEXT("Experience"));ExperienceId = FPrimaryAssetId(FPrimaryAssetType(ULyraExperienceDefinition::StaticClass()->GetFName()), FName(*ExperienceFromOptions));ExperienceIdSource = TEXT("OptionsString");}if (!ExperienceId.IsValid() && World->IsPlayInEditor()){ExperienceId = GetDefault<ULyraDeveloperSettings>()->ExperienceOverride;ExperienceIdSource = TEXT("DeveloperSettings");}// see if the command line wants to set the experienceif (!ExperienceId.IsValid()){FString ExperienceFromCommandLine;if (FParse::Value(FCommandLine::Get(), TEXT("Experience="), ExperienceFromCommandLine)){ExperienceId = FPrimaryAssetId::ParseTypeAndName(ExperienceFromCommandLine);ExperienceIdSource = TEXT("CommandLine");}}// see if the world settings has a default experienceif (!ExperienceId.IsValid()){if (ALyraWorldSettings* TypedWorldSettings = Cast<ALyraWorldSettings>(GetWorldSettings())){ExperienceId = TypedWorldSettings->GetDefaultGameplayExperience();ExperienceIdSource = TEXT("WorldSettings");}}ULyraAssetManager& AssetManager = ULyraAssetManager::Get();FAssetData Dummy;if (ExperienceId.IsValid() && !AssetManager.GetPrimaryAssetData(ExperienceId, /*out*/ Dummy)){UE_LOG(LogLyraExperience, Error, TEXT("EXPERIENCE: Wanted to use %s but couldn't find it, falling back to the default)"), *ExperienceId.ToString());ExperienceId = FPrimaryAssetId();}// Final fallback to the default experienceif (!ExperienceId.IsValid()){//@TODO: Pull this from a config setting or somethingExperienceId = FPrimaryAssetId(FPrimaryAssetType("LyraExperienceDefinition"), FName("B_LyraDefaultExperience"));ExperienceIdSource = TEXT("Default");}OnMatchAssignmentGiven(ExperienceId, ExperienceIdSource);
}
void ALyraGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{Super::InitGame(MapName, Options, ErrorMessage);//@TODO: Eventually only do this for PIE/autoGetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::HandleMatchAssignmentIfNotExpectingOne);
}
从代码中我们发现 ,当gamemode初始化game的时候,下一帧就调用
HandleMatchAssignmentIfNotExpectingOne()这个方法
这个方法中有多个获得
ExperienceId的方法,不同的情况分别命中一个
而默认地图用的是最后一个
对就是硬编码,写死的读取一个叫
B_LyraDefaultExperience的资源我们看看这个资源是什么
真是绕啊,不过lyra也遵循了数据驱动的原则,这些UPrimaryDataAsset就是数据配置。
--------------------------------------------------------------------------------------------------------
问题2:我们进这个地图会是什么流程
首先看看这个control 特效这一疙瘩是怎么生成的。
刚开始默认的地图中,有一个这个蓝图actor,里面有逻辑。
在beginplay里面获得了所有类型是LyraUserFacingExperienceDefinition的资源primary asset id
然后有几个LyraUserFacingExperienceDefinition这个就生成几个B_TeleportToUserFacingExperience
我们先看看LyraUserFacingExperienceDefinition这个都定义了什么
通过上图可以看到最重要的是定义了那张地图和一个数据这个数据定义了角色是哪个还有各种gamefeature的 action
生成B_TeleportToUserFacingExperience的时候把对应的LyraUserFacingExperienceDefinition带进去
在B_TeleportToUserFacingExperience里面有一个碰撞体,看看碰撞方法里面的逻辑
当一个角色碰撞到这个粒子特效的时候
1是生成了request
然后
我们一直用的是standalong模式所以目前的流程就是server的流程
从外面向里面分析先到这里,我们从里面向外面分析,最后肯定会汇合到这里
我们断点到这里
同一个函数,control这个地图进的是这个,并且把experienceid得到了
我们看看 这个optionsString是怎么赋值的
看看对上了,就是servertravel的时候把nextmap记录下来
然后在
UEngine::TickWorldTravel
中判断是不是空,非空就loadmap。
---------------------------------------------------------------------------------------------------
问题3:lyra的gamefeature and ULyraExperienceDefinition中的actions
在看 lyra的过程中你会发现:
既有正常的gamefeature的action
又有在ULyraExperienceDefinition中定义的action
感觉好乱
先看正常的gamefeature
lyra工程中也有继承
UDefaultGameFeaturesProjectPolicies
而来的
ULyraGameFeaturePolicy
但是看了源码,并没有对plugin里面的gamefeature做策略性的加载,所以plugin里面的gamefeature会随着gamefeaturesubsystem的创建和init方法,而导入到内存中,并且自动active,所以gamefeaturedata里面设置的action都会自动运行。
而ULyraExperienceDefinition中定义的action是手动调用的
---------------------------------------------------------------------------------------------------------------------------------
在play L_DefaultEditorOverview场景中如果按下esc键 就会出现退出ui 那么这个ui是在哪设置的呢,具体是什么逻辑呢
我先查了 project setting中的input 设置 没有关于esc的设置 在character和controller中也没有相关处理esc的处理逻辑
有个很有用的工具 能查看当ui的相关逻辑,当你无处下手的时候 可以用用这个工具
通过这个工具 我们很快查到了 退出 界面的ui蓝图叫
然后通过引用查看工具 我们看到 最终引用它的是
这个ui蓝图 是在 L_DefaultEditorOverview 地图的 exprerience数据中设置的
一进场景 就把 这个w_DefaultHUDLayout加载进来了
在w_DefaultHUDLayout中有一个设定
这就跟退出ui联系起来了
看看代码中怎么写的
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_UI_LAYER_MENU, "UI.Layer.Menu");
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_UI_ACTION_ESCAPE, "UI.Action.Escape");ULyraHUDLayout::ULyraHUDLayout(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
{
}void ULyraHUDLayout::NativeOnInitialized()
{Super::NativeOnInitialized();RegisterUIActionBinding(FBindUIActionArgs(FUIActionTag::ConvertChecked(TAG_UI_ACTION_ESCAPE), false, FSimpleDelegate::CreateUObject(this, &ThisClass::HandleEscapeAction)));
}void ULyraHUDLayout::HandleEscapeAction()
{if (ensure(!EscapeMenuClass.IsNull())){UCommonUIExtensions::PushStreamedContentToLayer_ForPlayer(GetOwningLocalPlayer(), TAG_UI_LAYER_MENU, EscapeMenuClass);}
}
在初始化的时候就注册了 退出监听
当退出的时候就调用handleEscapeAction 就把退出界面给调用出来了
疑问解决了
------------------------------------------------------------------------------------------------------------------
问题
在默认地图
按下play键 会发现有一个蓝色的界面出现一下就消失了,
那么这个界面出现是什么逻辑呢?
我们探索一下
想用widget reflector 发现 这个界面出现时间太短 根本截取不到
那就现在content 中找到 这个界面吧
用Type=WidgetBlueprint 搜索所有的widget
发现有好几个看起来都一样
而且从名字上来看 这三个都挺有可能的 只能一个一个先查看引用
看来看去 觉得这个很像啊
全局搜索一下 看看W_LoadingScreen_host在哪创建的
全局蓝图搜索
没找到结果,路线断了
难道是在c++中创建的?搜一下c++工程
果然找到了 写的这么隐蔽 竟然在config文件中配置 点进去看看
意思是说 在CommonLoadingScreen插件下的CommonLoadingScreenSettings文件中的LoadingScreenWidget这个变量赋值了,去看看
这么一看 这个类是个配置类 应该 没啥逻辑 继承于UDeveloperSettingsBackedByCVars
那应该在project setting中找到 相关的配置ui界面 打开 project setting 看看
果然是在这个界面配置 最后保存在defaultgame.ini中,同时还有common input settings可以随手看一下。
然后仅仅是配置了值 还是没有找到 在哪生成,找c++那个变量就能找到
接着往上层找
首先 这个 manager类是个gameinstancesubsystem ,所以 游戏一运行这个manager就创建了
并且生命周期跟随gameinstance的生命周期
在tick中每帧都刷 就看shouldshowloadingscreen条件满足了就创建这个蓝色的loading画面
那就看 这个条件是如何满足的
bool ULoadingScreenManager::ShouldShowLoadingScreen()
{const UCommonLoadingScreenSettings* Settings = GetDefault<UCommonLoadingScreenSettings>();// Check debugging commands that force the state one way or another
#if !UE_BUILD_SHIPPINGstatic bool bCmdLineNoLoadingScreen = FParse::Param(FCommandLine::Get(), TEXT("NoLoadingScreen"));if (bCmdLineNoLoadingScreen){DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("CommandLine has 'NoLoadingScreen'"));return false;}
#endif// Check for a need to show the loading screenconst bool bNeedToShowLoadingScreen = CheckForAnyNeedToShowLoadingScreen();// Keep the loading screen up a bit longer if desiredbool bWantToForceShowLoadingScreen = false;if (bNeedToShowLoadingScreen){// Still need to show itTimeLoadingScreenLastDismissed = -1.0;}else{// Don't *need* to show the screen anymore, but might still want to for a bitconst double CurrentTime = FPlatformTime::Seconds();const bool bCanHoldLoadingScreen = (!GIsEditor || Settings->HoldLoadingScreenAdditionalSecsEvenInEditor);const double HoldLoadingScreenAdditionalSecs = bCanHoldLoadingScreen ? LoadingScreenCVars::HoldLoadingScreenAdditionalSecs : 0.0;if (TimeLoadingScreenLastDismissed < 0.0){TimeLoadingScreenLastDismissed = CurrentTime;}const double TimeSinceScreenDismissed = CurrentTime - TimeLoadingScreenLastDismissed;// hold for an extra X seconds, to cover up streamingif ((HoldLoadingScreenAdditionalSecs > 0.0) && (TimeSinceScreenDismissed < HoldLoadingScreenAdditionalSecs)){// Make sure we're rendering the world at this point, so that textures will actually stream in//@TODO: If bNeedToShowLoadingScreen bounces back true during this window, we won't turn this off again...UGameViewportClient* GameViewportClient = GetGameInstance()->GetGameViewportClient();GameViewportClient->bDisableWorldRendering = false;DebugReasonForShowingOrHidingLoadingScreen = FString::Printf(TEXT("Keeping loading screen up for an additional %.2f seconds to allow texture streaming"), HoldLoadingScreenAdditionalSecs);bWantToForceShowLoadingScreen = true;}}return bNeedToShowLoadingScreen || bWantToForceShowLoadingScreen;
}bool ULoadingScreenManager::CheckForAnyNeedToShowLoadingScreen()
{// Start out with 'unknown' reason in case someone forgets to put a reason when changing this in the future.DebugReasonForShowingOrHidingLoadingScreen = TEXT("Reason for Showing/Hiding LoadingScreen is unknown!");const UGameInstance* LocalGameInstance = GetGameInstance();if (LoadingScreenCVars::ForceLoadingScreenVisible){DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("CommonLoadingScreen.AlwaysShow is true"));return true;}const FWorldContext* Context = LocalGameInstance->GetWorldContext();if (Context == nullptr){// We don't have a world context right now... better show a loading screenDebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("The game instance has a null WorldContext"));return true;}UWorld* World = Context->World();if (World == nullptr){DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We have no world (FWorldContext's World() is null)"));return true;}AGameStateBase* GameState = World->GetGameState<AGameStateBase>();if (GameState == nullptr){// The game state has not yet replicated.DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("GameState hasn't yet replicated (it's null)"));return true;}if (bCurrentlyInLoadMap){// Show a loading screen if we are in LoadMapDebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("bCurrentlyInLoadMap is true"));return true;}if (!Context->TravelURL.IsEmpty()){// Show a loading screen when pending travelDebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We have pending travel (the TravelURL is not empty)"));return true;}if (Context->PendingNetGame != nullptr){// Connecting to another serverDebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We are connecting to another server (PendingNetGame != nullptr)"));return true;}if (!World->HasBegunPlay()){DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("World hasn't begun play"));return true;}if (World->IsInSeamlessTravel()){// Show a loading screen during seamless travelDebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We are in seamless travel"));return true;}// Ask the game state if it needs a loading screen if (ILoadingProcessInterface::ShouldShowLoadingScreen(GameState, /*out*/ DebugReasonForShowingOrHidingLoadingScreen)){return true;}// Ask any game state components if they need a loading screenfor (UActorComponent* TestComponent : GameState->GetComponents()){if (ILoadingProcessInterface::ShouldShowLoadingScreen(TestComponent, /*out*/ DebugReasonForShowingOrHidingLoadingScreen)){return true;}}// Ask any of the external loading processors that may have been registered. These might be actors or components// that were registered by game code to tell us to keep the loading screen up while perhaps something finishes// streaming in.for (const TWeakInterfacePtr<ILoadingProcessInterface>& Processor : ExternalLoadingProcessors){if (ILoadingProcessInterface::ShouldShowLoadingScreen(Processor.GetObject(), /*out*/ DebugReasonForShowingOrHidingLoadingScreen)){return true;}}// Check each local playerbool bFoundAnyLocalPC = false;bool bMissingAnyLocalPC = false;for (ULocalPlayer* LP : LocalGameInstance->GetLocalPlayers()){if (LP != nullptr){if (APlayerController* PC = LP->PlayerController){bFoundAnyLocalPC = true;// Ask the PC itself if it needs a loading screenif (ILoadingProcessInterface::ShouldShowLoadingScreen(PC, /*out*/ DebugReasonForShowingOrHidingLoadingScreen)){return true;}// Ask any PC components if they need a loading screenfor (UActorComponent* TestComponent : PC->GetComponents()){if (ILoadingProcessInterface::ShouldShowLoadingScreen(TestComponent, /*out*/ DebugReasonForShowingOrHidingLoadingScreen)){return true;}}}else{bMissingAnyLocalPC = true;}}}UGameViewportClient* GameViewportClient = LocalGameInstance->GetGameViewportClient();const bool bIsInSplitscreen = GameViewportClient->GetCurrentSplitscreenConfiguration() != ESplitScreenType::None;// In splitscreen we need all player controllers to be presentif (bIsInSplitscreen && bMissingAnyLocalPC){DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("At least one missing local player controller in splitscreen"));return true;}// And in non-splitscreen we need at least one player controller to be presentif (!bIsInSplitscreen && !bFoundAnyLocalPC){DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("Need at least one local player controller"));return true;}// Victory! The loading screen can go away nowDebugReasonForShowingOrHidingLoadingScreen = TEXT("(nothing wants to show it anymore)");return false;
}
我 ca 太多条件了 原来 她把所有应该出现的loading的条件都写这里了,我们游戏刚开始出现只是她众多条件中的某一个条件
疑问解决
总结一下 在project setting中配置loading界面 在 一个loading subsystem中 每帧都tick 当条件满足 就出现loading界面 不满足就隐藏loading界面。
-------------------------------------------------------------------------------------
关于这个地图里面的东西 我感觉都已经探索完了
篇幅过长 再开一个2
这个角色也没有什么ability所以 这篇就结束了 太长了
开个新的
ue5 lyra探索分析1相关推荐
- ue5 lyra探索分析2 持续更新中
探索 看看角色走到 front end 发生了什么 当角色触碰到 光效的时候会走到这里 先看看谁碰的我 是pawn的话继续往下走 如果pawn的controller合法 如果这个pawn是local ...
- 推荐 2个十分好用的 pandas 数据探索分析神器!
作者 | 俊欣 来源 | 关于数据分析与可视化 今天小编给大家推荐两款超好用的工具来对数据进行探索分析.更好地帮助数据分析师从数据集当中来挖掘出有用的信息 PandasGUI 一听到这个名字,大家想必 ...
- 推荐2个十分好用的pandas数据探索分析神器!
今天小编给大家推荐两款超好用的工具来对数据进行探索分析.更好地帮助数据分析师从数据集当中来挖掘出有用的信息 PandasGUI 一听到这个名字,大家想必就会知道这个工具是在Pandas的基础之上加了G ...
- python共享单车案例分析_python分析数据分析项目:共享单车租用情况影响因素探索分析...
python分析数据分析项目:共享单车租用情况影响因素探索分析
- 数据挖掘_task2数据探索分析
数据挖掘_task2数据探索分析EDA 1.前言 2.内容介绍 2.1 读取数据并查看大体信息 2.1.1读取数据 2.1.2 查看数据信息 2.1.2.1 数据集的概况 2.1.2.2 判断数据缺失 ...
- 【金猿产品展】亚信科技“数据探索分析平台”——深挖数据价值,助客户高效管理和经营生产...
亚信科技产品 本产品由亚信科技投递并参与"数据猿年度金猿策划活动--2020大数据产业创新服务产品榜单及奖项"评选. 大数据产业创新服务媒体 --聚焦数据 · 改变商业 亚信科技数 ...
- 拍拍贷业务数据探索分析-基于R语言
======================================================= 1.前言 根据百度百科和官网:拍拍贷成立于2007年6月,公司全称为"上海拍拍 ...
- 空间分析:3-2.geoda地图与探索分析
先来个简单的,看下geoda的地图设置(地图)与图表设置(探索分析). 数据就是之前爬下来的北京二手房房价数据shp文件. 一.打开shp数据 在geoda工具条中选择打开,打开连接数据源对话框,选择 ...
- 【Python】数据探索分析——东北大学软件学院大数据班数据挖掘实训一(1)
数据探索分析 (1)获取数据并进行数据预处理,将含有缺失值的样本去掉,取出死亡率在 0 (1)获取数据并进行数据预处理,将含有缺失值的样本去掉,取出死亡率在 0<q<=1范围内的数据. i ...
最新文章
- stonesoft 虚拟安全解决方案
- 《敏捷个人-认识自我、管理自我.pdf》更新至 v0.7
- Eclipse常用的快捷方式
- SEO之Google--PageRank优化剖析(二)
- 7-2 定义日期类 (28 分)
- 编译安装PHP出现Cannot load /usr/local/apache/modules/libphp5.so
- 2021-06-16 forkjion stream流式计算方法
- java DefaultMutableTreeNode 树形结构 目录 1. Tree的概念	1 1.1. treeNode接口,mutabletreenode接口	1 1.2. 10-4:以T
- 实践的意义——写给图像处理算法爱好者的建议
- R语言安装教程 | 图文介绍超详细
- 《Python语言程序设计基础》嵩天著-第5章程序全练习题答案
- python IndentationError: unindent does not match any outer indentation level
- 记录遇到的小问题:Google浏览器在搜索时自动出现搜索记录的问题
- 歌唱比赛报名php源码,2021东方音乐挑战赛正式官宣发布 歌唱比赛音乐选秀节目全球报名招募...
- 12306是抢票原理分析-多线程之间实现同步
- Win10 WLAN消失网络连接不上解决方法
- 个人中心页面的UI设计知识点
- 浮动广告代码在网页两侧
- spring中IOP的配置
- 12306火车订票系统谈网站架构优化