目录
前提简介
UDynamicSubsystem相关解析:
回到之前我们就看的 FSubsystemCollectionBase::Initialize的源码:
FSubsystemModuleWatcher::InitializeModuleWatcher():
FSubsystemModuleWatcher::OnModulesChanged:
FSubsystemModuleWatcher::AddClassesForModule:
FSubsystemCollectionBase::AddAllInstances对象实例的创建:
FSubsystemModuleWatcher::RemoveClassesForModule
FSubsystemCollectionBase::RemoveAllInstances对象实例的销毁:
小总结:
后期动态加载
引用:
详情可以看大钊的文章:
《InsideUE4》GamePlay架构(十一)Subsystems - 知乎 (zhihu.com)
(大钊的图),其很好的讲解了Subsystem的不同生命周期:
之前作者介绍了UE4/5C++Subsystem的底层源码分析解析学习【一,UE5.0.3,UGameInstanceSubsystem,UWorldSubsystem,ULocalPlayerSubsystem】_多方通行8的博客-CSDN博客
3个subsystem的源码解析,现在作者将介绍UEngineubsystem和UEditorSubsystem,以及他们所继承的UDynamicSubsystem.
在之前的讲解中,我们知道UEngineSubsystem和UEditorSubsystem是继承于UDynamicSubsystem的,那么为什么要这么设计呢?
Dynamic,翻译为动态,就这两个字就足以形容为什么了。
既然是动态的对象,说明我们也会对其动态的加载和释放。
首先我们需要了解一下项目的模块和项目的插件:
大象无形UE4笔记五:模块总结 - 知乎 (zhihu.com)
UE4/5C++模块与插件_多方通行8的博客-CSDN博客
简单来说,一个UProject项目或UPlugin插件可以包含多个Module模块,其中的每个Module都有一个Build.cs,每个模块可以被编译成一个Dll。
这样模块之间可以互相引用。
因此一个模块可能有多个依赖的其他模块,我们假如叫它:myModules。
引擎的机制是加载一个模块的时候会自动的加载myModules,
这里和之前讲的并无差别,只是有用过if来判断是不是UDynamicSubsystem的,也一样是添加和初始化Subsystem。
除此之外,还有这个函数:
代码注释:
void FSubsystemModuleWatcher::InitializeModuleWatcher()
{
//non-thread-safe use of Global lists, must be from GameThread:
check(IsInGameThread());
check(!ModulesChangedHandle.IsValid());
// Add Loaded Modules
TArray SubsystemClasses;//获得所有UDynamicSubsystem的子类
GetDerivedClasses(UDynamicSubsystem::StaticClass(), SubsystemClasses, true);
//遍历
for (UClass* SubsystemClass : SubsystemClasses)
{
//不为抽象类
if (!SubsystemClass->HasAllClassFlags(CLASS_Abstract))
{
///获得所属于的包
UPackage* const ClassPackage = SubsystemClass->GetOuterUPackage();/
if (ClassPackage)
{
const FName ModuleName = FPackageName::GetShortFName(ClassPackage->GetFName());
if (FModuleManager::Get().IsModuleLoaded(ModuleName))
{
TArray>& ModuleSubsystemClasses = GlobalDynamicSystemModuleMap.FindOrAdd(ModuleName);
添加到DynamicSystemModuleMap
ModuleSubsystemClasses.Add(SubsystemClass);
}
}
}
}
//注册模块加载和释放事件
ModulesChangedHandle = FModuleManager::Get().OnModulesChanged().AddStatic(&FSubsystemModuleWatcher::OnModulesChanged);
}
其中最重要的就是遍历了当前进程里的UDynamicSubsystem子类们,并按照模块划分存储进DynamicSystemModuleMap【就是遍历中的最后一行那里的Add】。
这样之后就知道当加载或释放某个模块的时候,应该去创建和销毁哪些Subsystem类型对象。
然后仍然是这个cpp文件里面,我们可以找到这个函数:
从这个代码我们可以看见,里面写的是一个枚举,分别是创建动态模块的类们和销毁动态模块的类们。
让我们详细查看这两个函数的实现:
我将注释写在代码中:
void FSubsystemModuleWatcher::AddClassesForModule(const FName& InModuleName)
{
//non-thread-safe use of Global lists, must be from GameThread:
check(IsInGameThread());
check(! GlobalDynamicSystemModuleMap.Contains(InModuleName));
// Find the class package for this module
//前面的都是检测,无需在意
//这里是找到该模块所定义的代码包
const UPackage* const ClassPackage = FindPackage(nullptr, *(FString("/Script/") + InModuleName.ToString()));
if (!ClassPackage)
{
return;
}
TArray> SubsystemClasses;
TArray PackageObjects;
//下面是得到该模块里定义的所有对象
GetObjectsWithPackage(ClassPackage, PackageObjects, false);
for (UObject* Object : PackageObjects)
{
//这里转换为UClass
UClass* const CurrentClass = Cast(Object);
//在满足这些条件的情况下,为这个类创建对象实例【AddAllInstances的那个】
if (CurrentClass && !CurrentClass->HasAllClassFlags(CLASS_Abstract) && CurrentClass->IsChildOf(UDynamicSubsystem::StaticClass()))
{
SubsystemClasses.Add(CurrentClass);
FSubsystemCollectionBase::AddAllInstances(CurrentClass);
}
}
//判断其内部有没有定义Subsystem类,有的可能有【有的话就需要登记一下】,有的可能没有
if (SubsystemClasses.Num() > 0)
{
GlobalDynamicSystemModuleMap.Add(InModuleName, MoveTemp(SubsystemClasses));
}
}
这里可以看见,里面的实现都是大同小异,添加并初始化对象。
我将注释写在代码中:
void FSubsystemModuleWatcher::RemoveClassesForModule(const FName& InModuleName)
{
//non-thread-safe use of Global lists, must be from GameThread:
check(IsInGameThread());
TArray>* SubsystemClasses = GlobalDynamicSystemModuleMap.Find(InModuleName);
if (SubsystemClasses)//这里是判断这里之前有没有被登记过,有则true,没有则false
{
for (TSubclassOf& SubsystemClass : *SubsystemClasses)
{
//在这里销毁所有的对象
FSubsystemCollectionBase::RemoveAllInstances(SubsystemClass);
}
//移除登记
GlobalDynamicSystemModuleMap.Remove(InModuleName);
}
}
这里的关键其实是SubsystemCollections是个静态变量【从上面我们可以看见,所有类实现的地方是哪个cpp里面】,其引用了整个进程的所有定义的FSubsystemCollection的数量(2个在GEngine和GEditor里,其他多个可能动态新增在GameInstance,World,LocalPlayer里)
总而言之:MyPlugin模块在被加载的时候,会自动的触发OnModulesChanged事件,从而被自动的创建出内部的UMyPluginEngineSubsystem和UMyPluginEditorSubsystem。
讲到这里,他并不能真正的展示所谓的动态,所以如果你想要在游戏或编辑器运行一段时间后然后动态的加载某个模块或插件,这个动态就可以体现出来了。
可以看看大钊大佬写的:
《InsideUE4》GamePlay架构(十一)Subsystems - 知乎 (zhihu.com)
Programming Subsystems in Unreal Engine | Unreal Engine 5.1 Documentation