UE4 ShooterGame 项目源码分析 - Session的创建和加入

UE4 ShooterGame 项目源码分析 - Session的创建和加入

前言

ShooterGame是虚幻引擎开源的多人在线第一人称射击游戏项目,它的源码可以从Epic公司的商店里免费下载。里面包含了目前多人在线第一人称射击游戏常见功能,原本官方也有一个页面专门做了讲解,但是实在太过简单,对学习没有太多的帮助,所以我想花点时间在源代码层面自己分析一下,并把分析的过程记录在这里。今天分析一下代码中Session的创建和加入逻辑。

什么是Session

Session一般翻译成会话,这个概念对于做过服务器开发的人来说不会陌生。你可以简单认为Session是一个存在于服务器端的对象,它用来记录玩家的身份、连接状态等信息,比如你平时常遇到网站登录,当你登录成功后网页服务器就会创建一个Session来记录你的登录状态,身份等信息。ShooterGame作为一个多人在线的游戏,所以理所当然也会使用到Session这个概念。

创建Session

ShooterGame进程启动后,当前的游戏实例可以有两个选择:一是当服务器主机,等待其他客户端加入;二是作为客户端直接加入一个已经运行的主机。所谓当服务器主机,从引擎的角度来说就是创建一个Session等待其他的客户端加入。我们先看主菜单的Host菜单项的响应方法:

void FShooterMainMenu::HostGame(const FString& GameType)
{	
	if (ensure(GameInstance.IsValid()) && GetPlayerOwner() != NULL)
	{
		FString const StartURL = FString::Printf(TEXT("/Game/Maps/%s?game=%s%s%s?%s=%d%s"), *GetMapName(), *GameType, GameInstance->GetOnlineMode() != EOnlineMode::Offline ? TEXT("?listen") : TEXT(""), GameInstance->GetOnlineMode() == EOnlineMode::LAN ? TEXT("?bIsLanMatch") : TEXT(""), *AShooterGameMode::GetBotsCountOptionName(), BotsCountOpt, bIsRecordingDemo ? TEXT("?DemoRec") : TEXT("") );

		// Game instance will handle success, failure and dialogs
		GameInstance->HostGame(GetPlayerOwner(), GameType, StartURL);
	}
}

这个方法实现很简单,即根据玩家在界面上选择的关卡、游戏类型、Online Mode以及机器人数量等配置生产一个URL,然后调用UShooterGameInstance的HostGame方法。

// starts playing a game as the host
bool UShooterGameInstance::HostGame(ULocalPlayer* LocalPlayer, const FString& GameType, const FString& InTravelURL)
{
    // 非Online game 略
	AShooterGameSession* const GameSession = GetGameSession();
	if (GameSession)
	{
		// add callback delegate for completion
		OnCreatePresenceSessionCompleteDelegateHandle = GameSession->OnCreatePresenceSessionComplete().AddUObject(this, &UShooterGameInstance::OnCreatePresenceSessionComplete);

		TravelURL = InTravelURL;
		bool const bIsLanMatch = InTravelURL.Contains(TEXT("?bIsLanMatch"));

		//determine the map name from the travelURL
		const FString& MapNameSubStr = "/Game/Maps/";
		const FString& ChoppedMapName = TravelURL.RightChop(MapNameSubStr.Len());
		const FString& MapName = ChoppedMapName.LeftChop(ChoppedMapName.Len() - ChoppedMapName.Find("?game"));

		if (GameSession->HostSession(LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, GameType, MapName, bIsLanMatch, true, AShooterGameSession::DEFAULT_NUM_PLAYERS))
		{
			if ( (PendingState == CurrentState) || (PendingState == ShooterGameInstanceState::None) )
			{
				ShowLoadingScreen();
				GotoState(ShooterGameInstanceState::Playing);
				return true;
			}
		}
	}

	return false;
}

在UShooterGameInstance的HostGame方法中,首先通过从当前GameMode实例里获得AGameSession实例。然后往AGameSession实例上注册创建Session完成事件的监听代理。最后就是根据玩家选择的游戏类型、关卡等设置调用AGameSession的HostSession来创建Session。Session创建完之后,接下来就是等待客户端加入Session。

加入Session

要作为一个客户端加入一个已经运行的游戏主机上,玩家只需要在主菜单上点击JOINT按钮,然后在弹出的服务器列表界面选择一个服务器即可。在服务器界面选择服务器会调用到以下的方法:

void SShooterServerList::ConnectToServer()
{
	if (SelectedItem.IsValid())
	{
		int ServerToJoin = SelectedItem->SearchResultsIndex;

		if (GEngine && GEngine->GameViewport)
		{
			GEngine->GameViewport->RemoveAllViewportWidgets();
		}
		
		UShooterGameInstance* const GI = Cast<UShooterGameInstance>(PlayerOwner->GetGameInstance());
		if (GI)
		{
			GI->JoinSession(PlayerOwner.Get(), ServerToJoin);
		}
	}
}

SShooterServerList::ConnectToServer方法会尝试去获得UShooterGameInstance实例,然后以玩家选择的服务器索引为参数调用UShooterGameInstance的JoinSession方法:

bool UShooterGameInstance::JoinSession(ULocalPlayer* LocalPlayer, int32 SessionIndexInSearchResults)
{
	AShooterGameSession* const GameSession = GetGameSession();
	if (GameSession)
	{
		AddNetworkFailureHandlers();

		OnJoinSessionCompleteDelegateHandle = GameSession->OnJoinSessionComplete().AddUObject(this, &UShooterGameInstance::OnJoinSessionComplete);
		if (GameSession->JoinSession(LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, SessionIndexInSearchResults))
		{
			if ( (PendingState == CurrentState) || (PendingState == ShooterGameInstanceState::None) )
			{
				ShowLoadingScreen();
				GotoState(ShooterGameInstanceState::Playing);
				return true;
			}
		}
	}

	return false;
}

JoinSession顾名思义就是加入会话,它的实现主要依赖AGameSession类的JoinSession方法,在调用完该方法之后剩下的就是等待AGameSession的回调来获得加入会话的执行结果。

总结

从上面的源代码我们看到,session的创建和加入逻辑实现其实都是被引擎封装好了,作为使用者并不需要接触底层的网络代码,不需要自己去维护像网络连接创建和维护等这些很底层的代码,实际上引擎做的不仅仅是这样,还包括登录验证等安全相关的逻辑,有时间的话可以继续深入研读这部分代码。

你可能感兴趣的:(游戏开发,虚幻引擎入门)