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

目录

结构查看:

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的指针!

然后我们解析一下:

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是模板参数,在之前的图片中可以看到。

然后是上面中AddAndInitializeSubsystem的解析:

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);
}

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

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

首先我们看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;
}

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

在这个USubsystemBlueprintLibrary里面:

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

UWorld:

在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();
		}
	}
}

 卸载是一样的:

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

ULocalPlayer:

用的是PlayerAdded:

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

销毁用的是PlayerRemoved:

 为什么包含了UDynamicSubsystem这一层:

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

这方面将在之后讲解

猜你喜欢

转载自blog.csdn.net/q244645787/article/details/130001259