赞
踩
目录
UGameInstanceSubsystem中UCLASS()里的说明符和元数据修饰符:
UWorldSubsystem和ULocalPlayerSubsystem向上查看,为USubsystem:
UEngineSubsystem和UEditorSubsystem向上查看,为UDynamicSubsystem:
UDynamicSubsystem向上查看,为USubsystem:
通过FSubsystemCollectionBase看FGCObject:
通过FSubsystemCollectionBase找到FSubsystemCollection模板类后,发现在GameInstance中拥有这个FSubsystemCollection创建的类:
然后是上面中AddAndInitializeSubsystem的解析:
说人话总结就是:通过反射机制找到UGameInstanceSubsystem的子类,然后创建对应子类的实例并初始化。
看了关于UGameInstanceSubstance这块的代码后,我们看看UWorldSubsystem的代码就会发现基本上是一致的:
上次我讲过关于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<UGameInstanceSubsystem> SubsystemCollection;
然后在GameInstance里面的初始化中,我们可以看到对这个的初始化:
上面两个部分就不讲了,都是初始化的东西。
这个初始化的方法在最开始讲的FSubsystemCollectionBase里面:
我们着重看的是最下面的这个初始化:
SubsystemCollection.Initialize(this);
看看实现
可以看到传入的是一个NewOuter,而在里面和其相关的是Outer,所以我们来到FSubsystemCollectionBase里面可以看到:
Outer存的是GameInstance的指针!
然后我们解析一下:
- 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<bool> PopulatingGuard(bPopulating, true);
-
- //这里判断是不是属于这个UDynamicSubsystem系统的
- if (BaseType->IsChildOf(UDynamicSubsystem::StaticClass()))
- {
- for (const TPair<FName, TArray<TSubclassOf<UDynamicSubsystem>>>& SubsystemClasses : GlobalDynamicSystemModuleMap)
- {
- for (const TSubclassOf<UDynamicSubsystem>& SubsystemClass : SubsystemClasses.Value)
- {
- if (SubsystemClass->IsChildOf(BaseType))
- {
- AddAndInitializeSubsystem(SubsystemClass);
- }
- }
- }
- }
- else
- {
- TArray<UClass*> 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<bool> 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<USubsystem>();
- if (CDO->ShouldCreateSubsystem(Outer))//是否允许创建
- {
- //创建一个value值
- USubsystem* Subsystem = NewObject<USubsystem>(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:
因为里面包含了不同的模块,和不同的运行方式。
这方面将在之后讲解
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。