配置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进行联机的测试。