目录
结构查看:
UGameInstanceSubsystem向上查看:
向上一次查看,其继承于USubsystem:
UGameInstanceSubsystem中UCLASS()里的说明符和元数据修饰符:
向上第二次查看,USubsystem继承于UObject:
UWorldSubsystem和ULocalPlayerSubsystem向上查看,为USubsystem:
UEngineSubsystem和UEditorSubsystem向上查看,为UDynamicSubsystem:
UDynamicSubsystem向上查看,为USubsystem:
总结画图:
通过FSubsystemCollectionBase看FGCObject:
重写的AddReferenceObjects函数:
那么这个SubsystemMap的对象是什么呢?
通过FSubsystemCollectionBase找到FSubsystemCollection模板类后,发现在GameInstance中拥有这个FSubsystemCollection创建的类:
然后是上面中AddAndInitializeSubsystem的解析:
说人话总结就是:通过反射机制找到UGameInstanceSubsystem的子类,然后创建对应子类的实例并初始化。
接下来看是如何卸载这个:
上面讲的都是c++在哪里,那么蓝图在哪里呢?
看了关于UGameInstanceSubstance这块的代码后,我们看看UWorldSubsystem的代码就会发现基本上是一致的:
UWorld:
卸载是一样的:
ULocalPlayer:
为什么包含了UDynamicSubsystem这一层:
上次我讲过关于subsystem的5个子系统,那么subsystem的底层是怎么样的呢?
UE4/5C++之SubSystem的了解与创建_多方通行8的博客-CSDN博客
首先来看看上次写的:
一个继承UGameInstanceSubsystem,一个继承UEditorSubsystem。
首先我们从UGameInstanceSubsystem这里向上查看:
通过F12点击UGameInstanceSubsystem后,我们可以看到:
UGameInstanceSubsystem继承于这个USubsystem,看看上面的UCLASS()里面的说明符和元数据修饰符:
(不了解这块的可以看看这个:UE4/5C++之关于UCLASS()的说明符和元数据修饰符_多方通行8的博客-CSDN博客)
他的这个写的是
UCLASS(Abstract, Within = GameInstance)
什么意思呢?
Abstract在这里,表示这个UGameInstanceSubsystem是个抽象的,不能被实例化的,防止有人实例化UGameInstanceSubsystem,不安全。
Within = GameInstance,则表示是这个UGameInstanceSubsystem类不能存在于USubsystem对象的实例之外。
在这下面我们可以看到这些函数:
上面的是正常的,和我们重载的是一样的。
下面的这块友元函数是比较重要的,在这个目录里面查找-->>“然后是上面中AddAndInitializeSubsystem的解析”,在这个代码里面我写了注释。
然后我们可以看看,其他的两个UWorldSubsystem和ULocalPlayerSubsystem两个的上面是什么:
我们发现这两个和UGameInstanceSubsystem的上面一样,是USubsystem。
看完上面缩所写的继承之后,我们可以得出:
先记住这个FSubsystemCollectionBase,这个之后讲。
首先解释一些FGCObject的意思:F类继承的垃圾回收(GC)【简单理解一下,知道概念】
F类开头的是原生的c++类,一般自定义的类都是由F开头。
所以说如果原生的c++想要GC的话,要继承这个类,所以也就能看到:
着重看一下下面这个重写的AddReferenceObjects的函数。
查看一下他的实现:
可以看到,他是将SubsystemMap这个对象的引用添加到根。
看到这个后就知道,这个是存储USubsystem的TMap类型。
然后:人话理解就是:把这个对象加进去,然后看时间自己给这个对象回收,正常释放掉。
FSubsystemCollectionBase
可以看到 FSubsystemCollection是一个模板类,然后我在GameInstance类的最下面发现:
FSubsystemCollection SubsystemCollection;
然后在GameInstance里面的初始化中,我们可以看到对这个的初始化:
上面两个部分就不讲了,都是初始化的东西。
这个初始化的方法在最开始讲的FSubsystemCollectionBase里面:
我们着重看的是最下面的这个初始化:
SubsystemCollection.Initialize(this);
可以看到传入的是一个NewOuter,而在里面和其相关的是Outer,所以我们来到FSubsystemCollectionBase里面可以看到:
然后我们解析一下:
void FSubsystemCollectionBase::Initialize(UObject* NewOuter)
{
if (Outer != nullptr)//判断是不是空的
{
// already initialized
return;
}
Outer = NewOuter;//不是空的,则把NewOuter赋予Outer
check(Outer);//在这里check,如果Outer是空的,他就会直接断在这里,不是则继续
if (ensure(BaseType) && ensureMsgf(SubsystemMap.Num() == 0, TEXT("Currently don't support repopulation of Subsystem Collections.")))
{
check(!bPopulating); //Populating collections on multiple threads?
//non-thread-safe use of Global lists, must be from GameThread:
check(IsInGameThread());
if (GlobalSubsystemCollections.Num() == 0)
{
FSubsystemModuleWatcher::InitializeModuleWatcher();
}
TGuardValue PopulatingGuard(bPopulating, true);
//这里判断是不是属于这个UDynamicSubsystem系统的
if (BaseType->IsChildOf(UDynamicSubsystem::StaticClass()))
{
for (const TPair>>& SubsystemClasses : GlobalDynamicSystemModuleMap)
{
for (const TSubclassOf& SubsystemClass : SubsystemClasses.Value)
{
if (SubsystemClass->IsChildOf(BaseType))
{
AddAndInitializeSubsystem(SubsystemClass);
}
}
}
}
else
{
TArray SubsystemClasses;//通过反射,把BaseType获得的子类存储到SubsystemClasses里面去
GetDerivedClasses(BaseType, SubsystemClasses, true);//通过BaseType获取所有继承UGameInstanceSubsystem的类
for (UClass* SubsystemClass : SubsystemClasses)
{
AddAndInitializeSubsystem(SubsystemClass);//在这里添加并初始化子系统
}
}
// Update Internal Arrays without emptying it so that existing refs remain valid
for (auto& Pair : SubsystemArrayMap)
{
Pair.Value.Empty();
UpdateSubsystemArrayInternal(Pair.Key, Pair.Value);
}
// Statically track collections
GlobalSubsystemCollections.Add(this);
}
}
BaseType的底层,存的是引用:
看构造函数:
TBaseType是模板参数,在之前的图片中可以看到。
USubsystem* FSubsystemCollectionBase::AddAndInitializeSubsystem(UClass* SubsystemClass)
{
TGuardValue PopulatingGuard(bPopulating, true);
if (!SubsystemMap.Contains(SubsystemClass))//如果他不存在的话
{
// Only add instances for non abstract Subsystems
//这里他判断是否存在,以及是否是抽象类:这里不能是抽象类
if (SubsystemClass && !SubsystemClass->HasAllClassFlags(CLASS_Abstract))
{
// Catch any attempt to add a subsystem of the wrong type
//检查是不是其孩子,是则通过
checkf(SubsystemClass->IsChildOf(BaseType), TEXT("ClassType (%s) must be a subclass of BaseType(%s)."), *SubsystemClass->GetName(), *BaseType->GetName());
// Do not create instances of classes aren't authoritative
if (SubsystemClass->GetAuthoritativeClass() != SubsystemClass)
{
return nullptr;
}
//获取类的CDO对象到CDO中去,这个是为了在项目中供反射使用的
const USubsystem* CDO = SubsystemClass->GetDefaultObject();
if (CDO->ShouldCreateSubsystem(Outer))//是否允许创建
{
//创建一个value值
USubsystem* Subsystem = NewObject(Outer, SubsystemClass);
//这里赋予key值和value值
SubsystemMap.Add(SubsystemClass,Subsystem);
Subsystem->InternalOwningSubsystem = this;//这里的InternalOwningSubsystem就是之前讲的友元类
Subsystem->Initialize(*this);//最后初始化自己的类
return Subsystem;
}
UE_LOG(LogSubsystemCollection, VeryVerbose, TEXT("Subsystem does not exist, but CDO choose to not create (%s)"), *SubsystemClass->GetName());
}
return nullptr;
}
UE_LOG(LogSubsystemCollection, VeryVerbose, TEXT("Subsystem already exists (%s)"), *SubsystemClass->GetName());
return SubsystemMap.FindRef(SubsystemClass);
}
首先我们看GameInstance的Shutdown函数:
我们退出按esc的时候,就会进入到这个函数里面:
void UGameInstance::Shutdown()
{
ReceiveShutdown();
if (OnlineSession)
{
OnlineSession->ClearOnlineDelegates();
OnlineSession = nullptr;
}
for (int32 PlayerIdx = LocalPlayers.Num() - 1; PlayerIdx >= 0; --PlayerIdx)
{
ULocalPlayer* Player = LocalPlayers[PlayerIdx];
if (Player)
{
RemoveLocalPlayer(Player);
}
}
SubsystemCollection.Deinitialize();//调用的这个方法是重点。
FNetDelegates::OnReceivedNetworkEncryptionToken.Unbind();
FNetDelegates::OnReceivedNetworkEncryptionAck.Unbind();
// Clear the world context pointer to prevent further access.
WorldContext = nullptr;
}
我们可以看到其调用了SubsystemCollection.Deinitialize();我们看看里面写了什么:
void FSubsystemCollectionBase::Deinitialize()
{
//non-thread-safe use of Global lists, must be from GameThread:
check(IsInGameThread());
// already Deinitialize'd :
if ( Outer == nullptr )
return;
// Remove static tracking
GlobalSubsystemCollections.Remove(this);
if (GlobalSubsystemCollections.Num() == 0)
{
FSubsystemModuleWatcher::DeinitializeModuleWatcher();
}
// Deinit and clean up existing systems
SubsystemArrayMap.Empty();
//会在这里判断子系统里面,有多少的东西
for (auto Iter = SubsystemMap.CreateIterator(); Iter; ++Iter)
{
UClass* KeyClass = Iter.Key();//在这里获取Key值
USubsystem* Subsystem = Iter.Value();//在这里获取Value,即对象,取出来。
if ( Subsystem != nullptr && Subsystem->GetClass() == KeyClass)//检测取出来的东西是不是一样的
{
Subsystem->Deinitialize();//这里是指在将子系统干掉之前,我们要先将自己关掉
Subsystem->InternalOwningSubsystem = nullptr;//即友元的那个函数,变成空
}
}
//最后在清空一遍,同时这里GC也做了回收
SubsystemMap.Empty();
Outer = nullptr;
}
在这个USubsystemBlueprintLibrary里面:
在UWorld里面看看初始化子系统:
void UWorld::InitializeSubsystems()
{
//在一个特定的代码路径(从ContentBrowser打开关卡)InitializeSubsystems被调用两次。
//第一次是通过UEditorEngine::InitializeNewlyCreatedInactiveWorld与EWorldTvpe::Inactive,我们想防止初始化
//因为SubsystemCollection::Initialize只能被调用一次,我们想用适当的 WorldType来调用它
//第二次是通过UEditorEngine::Map_Load,此时WorldType是有效的。
if (WorldType != EWorldType::Inactive)
{
SubsystemCollection.Initialize(this);
if (bIsWorldInitialized)
{
PostInitializeSubsystems();
}
}
}
一样功能的函数,底层的实现逻辑是一样的,名字不同。
用的是PlayerAdded:
简直可以说是一模一样的。
销毁用的是PlayerRemoved:
因为里面包含了不同的模块,和不同的运行方式。
这方面将在之后讲解