当前位置:   article > 正文

UE4/5C++Subsystem的底层源码分析解析学习【一,UE5.0.3,UGameInstanceSubsystem,UWorldSubsystem,ULocalPlayerSubsystem】_ue5 c++ movie render queue

ue5 c++ movie render queue

目录

结构查看:

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向上查看:

首先来看看上次写的:

一个继承UGameInstanceSubsystem,一个继承UEditorSubsystem

首先我们从UGameInstanceSubsystem这里向上查看:

向上一次查看,其继承于USubsystem:

 通过F12点击UGameInstanceSubsystem后,我们可以看到:

UGameInstanceSubsystem中UCLASS()里的说明符和元数据修饰符:

UGameInstanceSubsystem继承于这个USubsystem,看看上面的UCLASS()里面的说明符和元数据修饰符:

 (不了解这块的可以看看这个:UE4/5C++之关于UCLASS()的说明符和元数据修饰符_多方通行8的博客-CSDN博客

他的这个写的是

UCLASS(Abstract, Within = GameInstance)

什么意思呢?

Abstract在这里,表示这个UGameInstanceSubsystem是个抽象的,不能被实例化的,防止有人实例化UGameInstanceSubsystem,不安全。

Within = GameInstance,则表示是这个UGameInstanceSubsystem类不能存在于USubsystem对象的实例之外。

向上第二次查看,USubsystem继承于UObject:

在这下面我们可以看到这些函数:

 上面的是正常的,和我们重载的是一样的。

下面的这块友元函数是比较重要的,在这个目录里面查找-->>然后是上面中AddAndInitializeSubsystem的解析”,在这个代码里面我写了注释。

UWorldSubsystemULocalPlayerSubsystem向上查看,为USubsystem

然后我们可以看看,其他的两个UWorldSubsystemULocalPlayerSubsystem两个的上面是什么:

我们发现这两个和UGameInstanceSubsystem的上面一样,是USubsystem

UEngineSubsystem和UEditorSubsystem向上查看,为UDynamicSubsystem

 UDynamicSubsystem向上查看,为USubsystem

 总结画图:

看完上面缩所写的继承之后,我们可以得出:

通过FSubsystemCollectionBase看FGCObject:

先记住这个FSubsystemCollectionBase,这个之后讲

 首先解释一些FGCObject的意思:F类继承的垃圾回收(GC)【简单理解一下,知道概念】

F类开头的是原生的c++类,一般自定义的类都是由F开头。

所以说如果原生的c++想要GC的话,要继承这个类,所以也就能看到:

重写的AddReferenceObjects函数

着重看一下下面这个重写的AddReferenceObjects的函数。

查看一下他的实现:

可以看到,他是将SubsystemMap这个对象的引用添加到根。

那么这个SubsystemMap的对象是什么呢?

 

 看到这个后就知道,这个是存储USubsystem的TMap类型。

然后:人话理解就是:把这个对象加进去,然后看时间自己给这个对象回收,正常释放掉。

通过FSubsystemCollectionBase找到FSubsystemCollection模板类后,发现在GameInstance中拥有这个FSubsystemCollection创建的类:

 FSubsystemCollectionBase

 可以看到 FSubsystemCollection是一个模板类,然后我在GameInstance类的最下面发现:

	FSubsystemCollection<UGameInstanceSubsystem> SubsystemCollection;

然后在GameInstance里面的初始化中,我们可以看到对这个的初始化:

上面两个部分就不讲了,都是初始化的东西。

 这个初始化的方法在最开始讲的FSubsystemCollectionBase里面:

我们着重看的是最下面的这个初始化:

SubsystemCollection.Initialize(this);

看看实现

可以看到传入的是一个NewOuter,而在里面和其相关的是Outer,所以我们来到FSubsystemCollectionBase里面可以看到:

Outer存的是GameInstance的指针!

然后我们解析一下:

  1. void FSubsystemCollectionBase::Initialize(UObject* NewOuter)
  2. {
  3. if (Outer != nullptr)//判断是不是空的
  4. {
  5. // already initialized
  6. return;
  7. }
  8. Outer = NewOuter;//不是空的,则把NewOuter赋予Outer
  9. check(Outer);//在这里check,如果Outer是空的,他就会直接断在这里,不是则继续
  10. if (ensure(BaseType) && ensureMsgf(SubsystemMap.Num() == 0, TEXT("Currently don't support repopulation of Subsystem Collections.")))
  11. {
  12. check(!bPopulating); //Populating collections on multiple threads?
  13. //non-thread-safe use of Global lists, must be from GameThread:
  14. check(IsInGameThread());
  15. if (GlobalSubsystemCollections.Num() == 0)
  16. {
  17. FSubsystemModuleWatcher::InitializeModuleWatcher();
  18. }
  19. TGuardValue<bool> PopulatingGuard(bPopulating, true);
  20. //这里判断是不是属于这个UDynamicSubsystem系统的
  21. if (BaseType->IsChildOf(UDynamicSubsystem::StaticClass()))
  22. {
  23. for (const TPair<FName, TArray<TSubclassOf<UDynamicSubsystem>>>& SubsystemClasses : GlobalDynamicSystemModuleMap)
  24. {
  25. for (const TSubclassOf<UDynamicSubsystem>& SubsystemClass : SubsystemClasses.Value)
  26. {
  27. if (SubsystemClass->IsChildOf(BaseType))
  28. {
  29. AddAndInitializeSubsystem(SubsystemClass);
  30. }
  31. }
  32. }
  33. }
  34. else
  35. {
  36. TArray<UClass*> SubsystemClasses;//通过反射,把BaseType获得的子类存储到SubsystemClasses里面去
  37. GetDerivedClasses(BaseType, SubsystemClasses, true);//通过BaseType获取所有继承UGameInstanceSubsystem的类
  38. for (UClass* SubsystemClass : SubsystemClasses)
  39. {
  40. AddAndInitializeSubsystem(SubsystemClass);//在这里添加并初始化子系统
  41. }
  42. }
  43. // Update Internal Arrays without emptying it so that existing refs remain valid
  44. for (auto& Pair : SubsystemArrayMap)
  45. {
  46. Pair.Value.Empty();
  47. UpdateSubsystemArrayInternal(Pair.Key, Pair.Value);
  48. }
  49. // Statically track collections
  50. GlobalSubsystemCollections.Add(this);
  51. }
  52. }

BaseType的底层,存的是引用:

 看构造函数:

 TBaseType是模板参数,在之前的图片中可以看到。

然后是上面中AddAndInitializeSubsystem的解析:

  1. USubsystem* FSubsystemCollectionBase::AddAndInitializeSubsystem(UClass* SubsystemClass)
  2. {
  3. TGuardValue<bool> PopulatingGuard(bPopulating, true);
  4. if (!SubsystemMap.Contains(SubsystemClass))//如果他不存在的话
  5. {
  6. // Only add instances for non abstract Subsystems
  7. //这里他判断是否存在,以及是否是抽象类:这里不能是抽象类
  8. if (SubsystemClass && !SubsystemClass->HasAllClassFlags(CLASS_Abstract))
  9. {
  10. // Catch any attempt to add a subsystem of the wrong type
  11. //检查是不是其孩子,是则通过
  12. checkf(SubsystemClass->IsChildOf(BaseType), TEXT("ClassType (%s) must be a subclass of BaseType(%s)."), *SubsystemClass->GetName(), *BaseType->GetName());
  13. // Do not create instances of classes aren't authoritative
  14. if (SubsystemClass->GetAuthoritativeClass() != SubsystemClass)
  15. {
  16. return nullptr;
  17. }
  18. //获取类的CDO对象到CDO中去,这个是为了在项目中供反射使用的
  19. const USubsystem* CDO = SubsystemClass->GetDefaultObject<USubsystem>();
  20. if (CDO->ShouldCreateSubsystem(Outer))//是否允许创建
  21. {
  22. //创建一个value值
  23. USubsystem* Subsystem = NewObject<USubsystem>(Outer, SubsystemClass);
  24. //这里赋予key值和value值
  25. SubsystemMap.Add(SubsystemClass,Subsystem);
  26. Subsystem->InternalOwningSubsystem = this;//这里的InternalOwningSubsystem就是之前讲的友元类
  27. Subsystem->Initialize(*this);//最后初始化自己的类
  28. return Subsystem;
  29. }
  30. UE_LOG(LogSubsystemCollection, VeryVerbose, TEXT("Subsystem does not exist, but CDO choose to not create (%s)"), *SubsystemClass->GetName());
  31. }
  32. return nullptr;
  33. }
  34. UE_LOG(LogSubsystemCollection, VeryVerbose, TEXT("Subsystem already exists (%s)"), *SubsystemClass->GetName());
  35. return SubsystemMap.FindRef(SubsystemClass);
  36. }

说人话总结就是:通过反射机制找到UGameInstanceSubsystem的子类,然后创建对应子类的实例并初始化。

接下来看是如何卸载这个:

首先我们看GameInstance的Shutdown函数:

我们退出按esc的时候,就会进入到这个函数里面:

  1. void UGameInstance::Shutdown()
  2. {
  3. ReceiveShutdown();
  4. if (OnlineSession)
  5. {
  6. OnlineSession->ClearOnlineDelegates();
  7. OnlineSession = nullptr;
  8. }
  9. for (int32 PlayerIdx = LocalPlayers.Num() - 1; PlayerIdx >= 0; --PlayerIdx)
  10. {
  11. ULocalPlayer* Player = LocalPlayers[PlayerIdx];
  12. if (Player)
  13. {
  14. RemoveLocalPlayer(Player);
  15. }
  16. }
  17. SubsystemCollection.Deinitialize();//调用的这个方法是重点。
  18. FNetDelegates::OnReceivedNetworkEncryptionToken.Unbind();
  19. FNetDelegates::OnReceivedNetworkEncryptionAck.Unbind();
  20. // Clear the world context pointer to prevent further access.
  21. WorldContext = nullptr;
  22. }

我们可以看到其调用了SubsystemCollection.Deinitialize();我们看看里面写了什么:

  1. void FSubsystemCollectionBase::Deinitialize()
  2. {
  3. //non-thread-safe use of Global lists, must be from GameThread:
  4. check(IsInGameThread());
  5. // already Deinitialize'd :
  6. if ( Outer == nullptr )
  7. return;
  8. // Remove static tracking
  9. GlobalSubsystemCollections.Remove(this);
  10. if (GlobalSubsystemCollections.Num() == 0)
  11. {
  12. FSubsystemModuleWatcher::DeinitializeModuleWatcher();
  13. }
  14. // Deinit and clean up existing systems
  15. SubsystemArrayMap.Empty();
  16. //会在这里判断子系统里面,有多少的东西
  17. for (auto Iter = SubsystemMap.CreateIterator(); Iter; ++Iter)
  18. {
  19. UClass* KeyClass = Iter.Key();//在这里获取Key值
  20. USubsystem* Subsystem = Iter.Value();//在这里获取Value,即对象,取出来。
  21. if ( Subsystem != nullptr && Subsystem->GetClass() == KeyClass)//检测取出来的东西是不是一样的
  22. {
  23. Subsystem->Deinitialize();//这里是指在将子系统干掉之前,我们要先将自己关掉
  24. Subsystem->InternalOwningSubsystem = nullptr;//即友元的那个函数,变成空
  25. }
  26. }
  27. //最后在清空一遍,同时这里GC也做了回收
  28. SubsystemMap.Empty();
  29. Outer = nullptr;
  30. }

 上面讲的都是c++在哪里,那么蓝图在哪里呢?

在这个USubsystemBlueprintLibrary里面:

看了关于UGameInstanceSubstance这块的代码后,我们看看UWorldSubsystem的代码就会发现基本上是一致的:

UWorld:

在UWorld里面看看初始化子系统:

  1. void UWorld::InitializeSubsystems()
  2. {
  3. //在一个特定的代码路径(从ContentBrowser打开关卡)InitializeSubsystems被调用两次。
  4. //第一次是通过UEditorEngine::InitializeNewlyCreatedInactiveWorld与EWorldTvpe::Inactive,我们想防止初始化
  5. //因为SubsystemCollection::Initialize只能被调用一次,我们想用适当的 WorldType来调用它
  6. //第二次是通过UEditorEngine::Map_Load,此时WorldType是有效的。
  7. if (WorldType != EWorldType::Inactive)
  8. {
  9. SubsystemCollection.Initialize(this);
  10. if (bIsWorldInitialized)
  11. {
  12. PostInitializeSubsystems();
  13. }
  14. }
  15. }

 卸载是一样的:

一样功能的函数,底层的实现逻辑是一样的,名字不同。

ULocalPlayer:

用的是PlayerAdded:

简直可以说是一模一样的。

销毁用的是PlayerRemoved:

 为什么包含了UDynamicSubsystem这一层:

因为里面包含了不同的模块,和不同的运行方式。

这方面将在之后讲解

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/107182
推荐阅读
相关标签
  

闽ICP备14008679号