UE4第三人称多人联机游戏开发02

配置Online Subsystem Steam

创建一个新的项目,并设置其与Steam连接。

首先我们建立一个新的第三人称模板,同时打开OnlineSubSystem的插件,并在cpp中的公共依赖项中加入OnlineSubSystem和OnlineSubSystemSteam。

 

配置好插件后,我们需要配置引擎的设置文件,之后我们需要配置引擎的设置文件,将Steam作为我们的在线子系统。

为了实现上述目标,我们需要打开Engineconfig.ini文件,增加以下的配置选项。

[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[OnlineSubsystem]
DefaultPlatformService=Steam

[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480

; If using Sessions
; bInitServerOnClient=true

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

NetDriver是网络驱动程序,目的是将计算机连接到给定的网络,我就没将其设置为Steam,那用我们就可以连接到Steam玩咯。

同时,我们将在线子系统的平台设置为Steam,这样我们就可以使用Steam的一些网络服务。

我们设置自己的SteamDevAppId为480,这是由于我们没有申请专门的开发ID,因此我们可以使用一些测试用ID。

在我们建立一个专门用于管理游戏会话的类之前,我们先在角色类中增加一些代码。

在Character.h中我们增加以下声明:

public:

	//Online Session接口的指针
	// IOnlineSessionPtr OnlineSessionInterface;

	TSharedPtr<class IOnlineSession, ESPMode::ThreadSafe> OnlineSessionInterface;

 在Character.cpp的构造函数中

IOnlineSubsystem* OnlineSubSystem = IOnlineSubsystem::Get();
	if (OnlineSubSystem)
	{
		OnlineSessionInterface = OnlineSubSystem->GetSessionInterface();

		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1,
				15.f,
				FColor::Blue,
				FString::Printf(TEXT("Found Subsystem %s"), 
                                *OnlineSubSystem->GetSubsystemName().ToString())); 
		}
	}

委托机制

UE4中的委托可以认为是一个持有对函数引用的对象,UE4中的函数可以绑定到他们身上,而当广播信号触发时,每一个绑定在委托上的函数都将被触发。

这些函数被称为回调函数,当委托触发或者广播时,回调函数将会被执行。

网络会话使用了委托机制,创建和连接游戏需要发送信息。

当我们使用 CreateSession()后,Session Interface将会发送信息到Steam,Steam将会创建游戏会话。之后Steam将会把信息发送回来,以便让我们知道创建会话的操作已经完成。

会话接口使用委托来做到这一点,会话接口定义了一组委托。

Steam联机设置

CreateSession:建立游戏会话

首先我们需要创建游戏会话,我们创建游戏会话后,将会发送至Steam,Steam创建好后会将该信息发送回UE4,因此我们需要使用委托机制来接收相关信息,并绑定相应函数打印出log。

#XXX_Character.h

public:
	//Online Session接口的指针
	IOnlineSessionPtr OnlineSessionInterface;
	
protected:
	//蓝图中使用的创建游戏会话函数
	UFUNCTION(BlueprintCallable)
	void CreateGameSession();

	//创建游戏会话的委托完成时绑定的函数
	void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
	
private:
	//创建会话委托
	FOnCreateSessionCompleteDelegate CreateSessionCompleteDelegate;
#XXX_Character.cpp
AMenuSystemCharacter::AMenuSystemCharacter():
	CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete)) //在构造函数中初始化Delegate,但这里具体的原理还没有搞懂
{
	
    //…………
        
        
    //获取OnlineSubSystem
	IOnlineSubsystem* OnlineSubSystem = IOnlineSubsystem::Get();
	if (OnlineSubSystem)
	{
		//获取OnlineSubSystem的接口
		OnlineSessionInterface = OnlineSubSystem->GetSessionInterface();

		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1,
				15.f,
				FColor::Blue,
				FString::Printf(TEXT("Found Subsystem %s"), 
                                *OnlineSubSystem->GetSubsystemName().ToString())); 
		}
	}
}


void AMenuSystemCharacter::CreateGameSession()
{
	//按下1键触发该函数
	//首先我们判断OnlineSessionInterface是否有效
	if (!OnlineSessionInterface.IsValid())
	{
		return;
	}

	//接着我们在判断前需要先查看是否有已经建立的游戏会话,如果有,则需要将其删除
	auto ExistingSession = OnlineSessionInterface->GetNamedSession(NAME_GameSession);

	if (ExistingSession != nullptr)
	{
		OnlineSessionInterface->DestroySession(NAME_GameSession);
	}

	//当创建Session的请求完成后将会触发该委托
	OnlineSessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegate);
	
    //使用MakeShareable,即使对象的构造函数为私有,其仍可运行。
    //利用此操作可拥有非自己创建的对象,并在删除对象时支持自定义行为。
	TSharedPtr<FOnlineSessionSettings> SessionSettings = MakeShareable(new FOnlineSessionSettings());
	
    //设定是否使用LAN
	SessionSettings->bIsLANMatch = false;
	SessionSettings->bUseLobbiesIfAvailable = true;
    //设定最大的联机人数
	SessionSettings->NumPublicConnections = 4;
    //是否可以在游戏途中加入
	SessionSettings->bAllowJoinInProgress = true;
    //是否允许玩家加入
	SessionSettings->bAllowJoinViaPresence = true;
    //Session是否会在在线服务器上公开发布
	SessionSettings->bShouldAdvertise = true;
    //是否显示用户状态信息
	SessionSettings->bUsesPresence = true;
	//设置寻找会话时的关键字和其对应的值
	SessionSettings->Set(FName("MatchType"), FString("FreeForAll"), EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
	
	const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
	OnlineSessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *SessionSettings);
}


void AMenuSystemCharacter::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
    //当委托触发后,该绑定函数执行
	if (bWasSuccessful)//当Session被创建后
	{
		if (GEngine)//显示在引擎界面
		{
			GEngine->AddOnScreenDebugMessage(
			-1,
			16.f,
			FColor::Blue,
			FString::Printf(TEXT("Created session: %s"), *SessionName.ToString())
			);
		}

		UWorld* World = GetWorld();
		if (World)//当创建游戏Session成功后
		{
            //使用ServerTravel进入到Lobby,并设置其为聆听服务器
			World->ServerTravel(FString("/Game/ThirdPersonCPP/Maps/Lobby?Listen"));
		}
	}
	else
	{
		if (GEngine)//否则则显示创建失败
		{
			GEngine->AddOnScreenDebugMessage(
			-1,
			16.f,
			FColor::Red,
			FString(TEXT("Failed to create session!"))
			);
		}
	}
}

 

JoinSession:加入游戏会话

当创建了GameSession后,我们可以首先寻找到创建的游戏会话的关键词,寻找到GameSession后,我们可以根据找到的GameSession得到创建游戏主机的IP地址,并使用IP地址进行加入。

# xxx_Character.h

protected:
	//寻找游戏会话委托完成时绑定的函数
	void OnFindSessionsComplete(bool bWasSuccessful);

	//加入游戏会话委托完成时绑定的函数
	void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);

private:

    //寻找会话委托
    FOnFindSessionsCompleteDelegate FindSessionsCompleteDelegate;
    //加入会话委托
    FOnJoinSessionCompleteDelegate JoinSessionCompleteDelegate;
	//游戏寻找结果
    TSharedPtr<FOnlineSessionSearch> SessionSearch;
#xxx_Character.cpp

AMenuSystemCharacter::AMenuSystemCharacter():
	CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete)),
	FindSessionsCompleteDelegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnFindSessionsComplete)),//初始化FindSession委托
	JoinSessionCompleteDelegate(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete))//初始化JoinSession委托
{

    //…………
        
    //获取OnlineSubSystem
	IOnlineSubsystem* OnlineSubSystem = IOnlineSubsystem::Get();
	if (OnlineSubSystem)
	{
		//获取OnlineSubSystem的接口
		OnlineSessionInterface = OnlineSubSystem->GetSessionInterface();

		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1,
				15.f,
				FColor::Blue,
				FString::Printf(TEXT("Found Subsystem %s"), *OnlineSubSystem->GetSubsystemName().ToString())); 
		}
	}
}


void AMenuSystemCharacter::JoinGameSession()
{
	// 确保连接到Steam
	if (!OnlineSessionInterface.IsValid())
	{
		return;
	}
	
    //调用FindSession的委托
	OnlineSessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);

	//设置OnlineSessionSetting
	SessionSearch = MakeShareable(new FOnlineSessionSearch());
	//设置最大搜寻数量
    SessionSearch->MaxSearchResults = 10000;
	//是否LAN局域网搜索结果
    SessionSearch->bIsLanQuery = false;
    //SEARCH_PRESENCE是只搜寻现有的游戏,并只搜索相同ID的游戏Session
	SessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);

	const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
	OnlineSessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), SessionSearch.ToSharedRef());
	
}

//寻找游戏会话委托的绑定函数
void AMenuSystemCharacter::OnFindSessionsComplete(bool bWasSuccessful)
{
	if (!OnlineSessionInterface.IsValid())
	{
		return;
	}

	
	for (auto Result: SessionSearch->SearchResults)
	{
        //获取搜索Session Id
		FString Id = Result.GetSessionIdStr();
		//获取用户
        FString User = Result.Session.OwningUserName;
		
        //获取到CreateSession中设置的关键词的value,key->value
		FString MatchType;
		Result.Session.SessionSettings.Get(FName("MatchType"), MatchType);
		
        //将信息打印到屏幕
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(
				-1,
				15.f,
				FColor::Cyan,
				FString::Printf(TEXT("Id: %s, User: %s"), *Id, *User)
			);
		}
		
        //如果关键词的值和需要寻找的值一样,则是我们需要加入的游戏Session
		if (MatchType == FString("FreeForAll"))
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(
					-1,
					15.f,
					FColor::Cyan,
					FString::Printf(TEXT("Joing Match Type: %s"), *MatchType)
				);
			}
            
            //调用JoinSession委托,即触发JoinSessionCompleted函数
            OnlineSessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate);
		const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
		OnlineSessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, Result);
		}	
	}
}


void AMenuSystemCharacter::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
	//确保连接在Steam
	if (!OnlineSessionInterface.IsValid())
	{
		return;
	}

	//获取IP地址,并将客户端跳转至该IP地址的聆听服务器上
	FString Address;
	if (OnlineSessionInterface->GetResolvedConnectString(NAME_GameSession, Address))
	{
		//将IP地址显示在屏幕上
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(
				-1,
				15.f,
				FColor::Yellow,
				FString::Printf(TEXT("Connect String: %s"), *Address)
			);
		}


		APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController();
		if (PlayerController)
		{
			//客户端根据IP跳转
			PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
		}
	}
}

实验结果如下,我们可以找到创建游戏会话的聆听服务器,得到其IP和UserName,同时获取到聆听服务器的IP,使用ClientTravel加入到服务器中的Lobby关卡中。至此,我们就完成了利用OnlineSubSyestemSteam进行联机的测试。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值