您现在的位置是:首页 >学无止境 >UE4/5多人游戏详解(八、游戏模式和游戏状态里的函数重写,插件内地图的地址做变量,做变量让按钮出现不同状态,插件内的所有代码)网站首页学无止境

UE4/5多人游戏详解(八、游戏模式和游戏状态里的函数重写,插件内地图的地址做变量,做变量让按钮出现不同状态,插件内的所有代码)

多方通行8 2023-06-08 08:00:02
简介UE4/5多人游戏详解(八、游戏模式和游戏状态里的函数重写,插件内地图的地址做变量,做变量让按钮出现不同状态,插件内的所有代码)

目录

这里不写在插件里面,而是在游戏模式:

头文件:

Cpp文件:

更改ini文件

进入地图设置模式:

写插件里面,做一个变量:

写变量

然后更改函数MenuSet:

在子系统中做变量:

变量:

销毁的回调函数:

给按钮设置不输入

效果:

后续添加:

所有代码:

MultiPlayerSessionPlugin:

MultiPlayerSessionPlugin.h:

MultiPlayerSessionPlugin.cpp:

MultiPlayerSessionGISubsystem:

MultiPlayerSessionGISubsystem.h:

MultiPlayerSessionGISubsystem.cpp:

InPluginsMenu:

InPluginsMenu.h:

InPluginsMenu.cpp:

MultiPlayerSessionPlugin.Build.cs:

MultiPlayerSessionPlugin.uplugin:


游戏模式和游戏状态

GameMode和GameState在联网游戏中扮演着重要的角色,它们的函数可以帮助开发者管理游戏状态和玩家连接,实现联网游戏的功能。

这里不写在插件里面,而是在游戏模式:

创建这个类的原因是我们希望这个游戏模式是在Lobby这个地图使用的,而在这个Level(地图)中,每一次玩家加入和离开,我们都需要专用的函数去使用

 

这个创建到content里面,而不是写在插件里面的:

然后

头文件:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "LobbyGameMode.generated.h"

/**
 * 
 */
UCLASS()
class MOREPERSONTEST_API ALobbyGameMode : public AGameModeBase
{
	GENERATED_BODY()
public:

	//重写: 登录成功后调用。这是第一个可以安全地在PlayerController上调用复制函数的地方。
	virtual void PostLogin(APlayerController* NewPlayer)override;
	//重写: 当具有PlaverState的控制器离开游戏或被销毁时调用
	virtual void Logout(AController* Exiting)override;
};

Cpp文件:

// Fill out your copyright notice in the Description page of Project Settings.


#include "LobbyGameMode.h"
#include "GameFramework/GameStateBase.h"
#include "GameFramework/PlayerState.h"
#include "MorePersonTestCharacter.h"

void ALobbyGameMode::PostLogin(APlayerController* NewPlayer)
{
	//调用父类
	Super::PostLogin(NewPlayer);
	//GameState用于将游戏状态相关属性复制到所有客户端。
	if (GameState)
	{
		//获取玩家数量
		int32 NumberOfPlayer = GameState.Get()->PlayerArray.Num();
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(1, 15, FColor::Blue, FString::Printf(TEXT("Now PlayerNum is %d"), NumberOfPlayer));
			//获取状态->详情可以向父类看过去
			APlayerState* playerState =NewPlayer->GetPlayerState<APlayerState>();
			if (playerState)
			{
				//获取名字
				FString playerName =playerState->GetPlayerName();
				GEngine->AddOnScreenDebugMessage(-1, 15, FColor::Cyan, FString::Printf(TEXT("%s join game"), *playerName));
			}
		}
	}
}

void ALobbyGameMode::Logout(AController* Exiting)
{
	//调用父类
	Super::Logout(Exiting);

	APlayerState* playerState = Exiting->GetPlayerState<APlayerState>();
	if (playerState)
	{
		//现在的人数
		int32 NumberOfPlayer = GameState.Get()->PlayerArray.Num();
		GEngine->AddOnScreenDebugMessage(1, 15, FColor::Blue, FString::Printf(TEXT("Now PlayerNum is %d"), NumberOfPlayer-1));
		//获取名字
		FString playerName = playerState->GetPlayerName();
		GEngine->AddOnScreenDebugMessage(-1, 15, FColor::Cyan, FString::Printf(TEXT("%s out the game"), *playerName));
	}
}

更改ini文件

 

进入地图设置模式:

将游戏模式创建蓝图类,然后换蓝图的pawn上去,之后放到lobby的地图里面:

 

 

 

写插件里面,做一个变量:

写变量

我们之前进入这个Lobby的地图的时候,使用的是Lobby地图的地址

但这个是一个插件,那么,我们就需要考虑到不同项目有不同的地图,所以这里我们需要做一个全新的变量:

在菜单类中:

 

然后更改函数MenuSet

我们需要在MenuSet的后面添加这个地址:

 不用忘记更改cpp中的文件,然后添加这个:

 在完成这些之后,我们在下面的创建会话的回调函数就可以把绝对的地址引用改为这个变量:

 

在子系统中做变量:

变量:

 然后cpp里面

 实现销毁函数:

 代码:

void UMultiPlayerSessionGISubsystem::DeleteSession()
{
	if (!mySessionInterface.IsValid())
	{
		MultiPlayerOnDestroySessionComplete.Broadcast(false);
		return;
	}
	//句柄和添加委托
	DestroySessionCompleteDelegateHandle =mySessionInterface->AddOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegate);

	if (!mySessionInterface->DestroySession(NAME_GameSession))
	{
		//销毁会话失败
		mySessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle);
		MultiPlayerOnDestroySessionComplete.Broadcast(false);
	}
}

销毁的回调函数:

 代码:

void UMultiPlayerSessionGISubsystem::onDestorySessionComplete(FName SessionName, bool bWasSuccessful)
{
	if (mySessionInterface)
	{
		//清除
		mySessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle);
	}
	//因为已经有一个会话了,所以要销毁会话然后在创建会话
	if (bWasSuccessful && bCreateSessionOnDestory)
	{
		//重置初始值
		bCreateSessionOnDestory = false;
		//创建会话
		CreateSession(LastNumPublicConnects,LastMatchType);
	}
	MultiPlayerOnDestroySessionComplete.Broadcast(bWasSuccessful);

}

给按钮设置不输入

 给几个回调函数做一个判断

 

 

 

效果:

这样就会变得透明

 

后续添加:

到这里为止,想必懂的都懂了,所以我就直接进行一下后续的添加(因为这个是插件,所以):

 

这里的

UPROPERTY(BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))

的意思是允许在蓝图中私有访问

所有代码:

分别是3个头文件,3个cpp文件,1个uplugin文件和一个cs文件:

MultiPlayerSessionPlugin:

MultiPlayerSessionPlugin.h:

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class FMultiPlayerSessionPluginModule : public IModuleInterface
{
public:

	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
};
//需要更改的ini设置:
//更改:DefaultEngine.ini中的(添加到末尾):
//[/ Script / Engine.GameEngine]
//+ NetDriverDefinitions = (DefName = "GameNetDriver", DriverClassName = "OnlineSubsystemSteam.SteamNetDriver", DriverClassNameFallback = "OnlineSubsystemUtils.IpNetDriver")
//
//[OnlineSubsystem]
//DefaultPlatformService = Steam
//
//[OnlineSubsystemSteam]
//bEnabled = true
//SteamDevAppId = 480
//bInitServerOnClient = true
//
//[/ Script / OnlineSubsystemSteam.SteamNetDriver]
//NetConnectionClassName = "OnlineSubsystemSteam.SteamNetConnection"

//然后是DefaultGame.ini中的(添加到末尾):
//[/ Script / Engine.GameSession]
//MaxPlayers = 100

MultiPlayerSessionPlugin.cpp:

// Copyright Epic Games, Inc. All Rights Reserved.

#include "MultiPlayerSessionPlugin.h"

#define LOCTEXT_NAMESPACE "FMultiPlayerSessionPluginModule"

void FMultiPlayerSessionPluginModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}

void FMultiPlayerSessionPluginModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FMultiPlayerSessionPluginModule, MultiPlayerSessionPlugin)

MultiPlayerSessionGISubsystem:

MultiPlayerSessionGISubsystem.h:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"

#include "Interfaces/OnlineSessionInterface.h"

#include "MultiPlayerSessionGISubsystem.generated.h"

//自定义委托,用于回调到菜单类
//动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiOnCreateSessionComplete, bool, bWasSuccessful);
//这里使用多播,而不是动态多播
DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiOnFindSessionComplete, const TArray<FOnlineSessionSearchResult>& SessionResult, bool bWasSuccessful);
DECLARE_MULTICAST_DELEGATE_OneParam(FMultiOnJoinSessionComplete, EOnJoinSessionCompleteResult::Type Result);
//动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiOnDestroySessionComplete, bool, bWasSuccessful);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiOnStartSessionComplete, bool, bWasSuccessful);

/**
 * 
 */
UCLASS()
class MULTIPLAYERSESSIONPLUGIN_API UMultiPlayerSessionGISubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()
public:
	UMultiPlayerSessionGISubsystem();

	//
	//会话 --公开的链接函数
	//这里将输入玩家数量和会话的类型(用于匹配不同的会话)
	void CreateSession(int32 playerConnectNum, FString MatchType);
	//输入的是寻找会话的最大数量
	void FindSession(int32 findSessionMaxNum);
	//加入会话
	void JoinSession(const FOnlineSessionSearchResult& SessionResult);
	//删除
	void DeleteSession();
	//开始
	void StartSession();

	//将回调函数绑定到的菜单类的自定义委托
	//委托变量
	//UPROPERTY(BlueprintAssignable)
	FMultiOnCreateSessionComplete MultiPlayerOnCreateSessionComplete;
	FMultiOnFindSessionComplete  MultiPlayerOnFindSessionComplete;
	FMultiOnJoinSessionComplete  MultiPlayerOnJoinSessionComplete;
	FMultiOnDestroySessionComplete  MultiPlayerOnDestroySessionComplete;
	FMultiOnStartSessionComplete  MultiPlayerOnStartSessionComplete;

protected:

	//回调函数,将会绑定到委托(根据委托输入对应输入对象)
	void onCreateSessionComplete(FName SessionName, bool bWasSuccessful);
	void onFindSessionComplete(bool bWasSuccessful);
	void onJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
	void onDestorySessionComplete(FName SessionName, bool bWasSuccessful);
	void onStartSessionComplete(FName SessionName, bool bWasSuccessful);

	

private:
	//会话接口
	IOnlineSessionPtr mySessionInterface;
	//会话设置的智能指针 查看上次的会话设置
	TSharedPtr<FOnlineSessionSettings> LastSessionSettings;
	//寻找会话的智能指针
	TSharedPtr<FOnlineSessionSearch> LastSessionSearch;
	
	//需要添加的委托,到时候要一一对应制作回调函数
	FOnCreateSessionCompleteDelegate CreateSessionCompleteDelegate;
	FDelegateHandle CreateSessionCompleteDelegateHandle;//委托句柄
	FOnFindSessionsCompleteDelegate FindSessionsCompleteDelegate;
	FDelegateHandle FindSessionsCompleteDelegateHandle;//委托句柄
	FOnJoinSessionCompleteDelegate JoinSessionCompleteDelegate;
	FDelegateHandle JoinSessionCompleteDelegateHandle;//委托句柄
	FOnDestroySessionCompleteDelegate DestroySessionCompleteDelegate;
	FDelegateHandle DestroySessionCompleteDelegateHandle;//委托句柄
	FOnStartSessionCompleteDelegate StartSessionCompleteDelegate;
	FDelegateHandle StartSessionCompleteDelegateHandle;//委托句柄

	//用于判断是否是在创建会话的时候正在销毁会话
	bool bCreateSessionOnDestory{ false };
	int32 LastNumPublicConnects;
	FString LastMatchType;
};

MultiPlayerSessionGISubsystem.cpp:

// Fill out your copyright notice in the Description page of Project Settings.


#include "MultiPlayerSessionGISubsystem.h"
#include "OnlineSubsystem.h"
#include "OnlineSessionSettings.h"

UMultiPlayerSessionGISubsystem::UMultiPlayerSessionGISubsystem():
	CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this,&UMultiPlayerSessionGISubsystem::onCreateSessionComplete)),
	FindSessionsCompleteDelegate(FOnFindSessionsCompleteDelegate::CreateUObject(this,&UMultiPlayerSessionGISubsystem::onFindSessionComplete)),
	JoinSessionCompleteDelegate(FOnJoinSessionCompleteDelegate::CreateUObject(this,&UMultiPlayerSessionGISubsystem::onJoinSessionComplete)),
	DestroySessionCompleteDelegate(FOnDestroySessionCompleteDelegate::CreateUObject(this,&UMultiPlayerSessionGISubsystem::onDestorySessionComplete)),
	StartSessionCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this,&UMultiPlayerSessionGISubsystem::onStartSessionComplete))
{
	//获取子系统
	IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
	if (Subsystem)
	{
		//从子系统中获取了会话系统,并放到我们的会话接口指针里面
		mySessionInterface = Subsystem->GetSessionInterface();
	}
}

void UMultiPlayerSessionGISubsystem::CreateSession(int32 playerConnectNum, FString MatchType)
{
	//判断会话系统是否有效
	if (!mySessionInterface.IsValid())
	{
		return;
	}
	//有会话则获取现有的会话
	auto ExistingSession = mySessionInterface->GetNamedSession(NAME_GameSession);
	//如果现有的会话并不是空的
	if (ExistingSession != nullptr)
	{
		bCreateSessionOnDestory = true;
		LastNumPublicConnects = playerConnectNum;
		LastMatchType = MatchType;
		//删除游戏会话
		DeleteSession();
	}
	//在会话创建请求完成时触发委托CreateSessionCompleteDelegate,返回句柄到CreateSessionCompleteDelegateHandle,方便之后删除
	CreateSessionCompleteDelegateHandle = mySessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegate);
	//
	LastSessionSettings = MakeShareable(new FOnlineSessionSettings());
	//判断现在的接口是不是空的,是则返回true,不是则返回false【此游戏将仅限局域网,外部玩家无法看到,是true还是false】
	//直接在这里判断你是局域网还不是
	LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName()=="NULL"?true:false;
	//可用的连接数量是多少
	LastSessionSettings->NumPublicConnections = playerConnectNum;
	//是否允许加入线程
	LastSessionSettings->bAllowJoinInProgress = true;
	//是否允许通过玩家存在加入
	LastSessionSettings->bAllowJoinViaPresence = true;
	//该比赛是否在在线服务上面公开广告
	LastSessionSettings->bShouldAdvertise = true;
	//是否显示用户状态信息
	LastSessionSettings->bUsesPresence = true;
	//不同key和value的匹配,从现有会话设置中定义会话设置
	LastSessionSettings->Set(FName("MatchType"), MatchType, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
	//用于防止不同的构建在搜索期间看到彼此
	LastSessionSettings->BuildUniqueId = 1;
	//支持api则使用
	LastSessionSettings->bUseLobbiesIfAvailable = true;

	//获取本地的第一个玩家控制器
	const ULocalPlayer* localPlayer = GetWorld()->GetFirstLocalPlayerFromController();
	//*localPlayer->GetPreferredUniqueNetId() 首选唯一网格ID
	//判断创建会话是否成功
	if (!mySessionInterface->CreateSession(*localPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings))
	{
		//创建失败
		//委托列表中删除委托,传入的是一个句柄(这个CreateSessionCompleteDelegateHandle在上面获取过)
		mySessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle);
		//广播自定义委托:传给所有注册的回调函数false
		MultiPlayerOnCreateSessionComplete.Broadcast(false);
	}
}

void UMultiPlayerSessionGISubsystem::FindSession(int32 findSessionMaxNum)
{
	if (!mySessionInterface.IsValid())
	{
		return;
	}
	//会话接口委托列表添加委托,然后句柄获取
	FindSessionsCompleteDelegateHandle = mySessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);

	LastSessionSearch = MakeShareable(new FOnlineSessionSearch);
	//配对服务返回的查询的最大数目
	LastSessionSearch->MaxSearchResults = findSessionMaxNum;
	//查询是否用于局域网匹配
	LastSessionSearch->bIsLanQuery = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
	//SEARCH_PRESENCE :仅搜索存在会话(值为true/false)
	//QuerySettings :用于查找匹配服务器的查询
	LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
	//获取本地的玩家控制器
	const ULocalPlayer* playerControler = GetWorld()->GetFirstLocalPlayerFromController();
	//搜索与指定匹配的对话
	//GetPreferredUniqueNetId :检索首选的唯一网id。这是为了向后兼容不使用缓存唯一网络id逻辑的游戏
	if (!mySessionInterface->FindSessions(*playerControler->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef()))
	{
		//寻找失败
		//清除
		mySessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
		//因为失败了,所以传入的是一个空的数组和false
		MultiPlayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
	}
}

void UMultiPlayerSessionGISubsystem::JoinSession(const FOnlineSessionSearchResult& SessionResult)
{
	if (!mySessionInterface.IsValid())
	{
		//无效情况下
		//广播到所有绑定对象:UnknownError
		MultiPlayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
		return;
	}
	//获取句柄和委托列表添加
	JoinSessionCompleteDelegateHandle =mySessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate);
	//加入会话
	const ULocalPlayer* localPlayer = GetWorld()->GetFirstLocalPlayerFromController();
	if (!mySessionInterface->JoinSession(*localPlayer->GetPreferredUniqueNetId(), NAME_GameSession, SessionResult))
	{
		//加入失败
		//清除委托
		mySessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
		//广播回调函数为未知错误
		MultiPlayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
	}
}

void UMultiPlayerSessionGISubsystem::DeleteSession()
{
	if (!mySessionInterface.IsValid())
	{
		MultiPlayerOnDestroySessionComplete.Broadcast(false);
		return;
	}
	//句柄和添加委托
	DestroySessionCompleteDelegateHandle =mySessionInterface->AddOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegate);

	if (!mySessionInterface->DestroySession(NAME_GameSession))
	{
		//销毁会话失败
		mySessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle);
		MultiPlayerOnDestroySessionComplete.Broadcast(false);
	}
}

void UMultiPlayerSessionGISubsystem::StartSession()
{
}

void UMultiPlayerSessionGISubsystem::onCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
	if (mySessionInterface)
	{
		//这里是清除之前注册的回调函数,以便在下一次创建会话时不会重复调用(即委托句柄)
		mySessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle);
	}
	//传给所有注册的回调函数 bWasSuccessful
	MultiPlayerOnCreateSessionComplete.Broadcast(bWasSuccessful);
}

void UMultiPlayerSessionGISubsystem::onFindSessionComplete(bool bWasSuccessful)
{
	if (mySessionInterface)
	{
		//清除
		mySessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
	}
	//查找的会话数量
	if (LastSessionSearch->SearchResults.Num()<=0)
	{
		//因为失败了,所以传入的是一个空的数组和false
		MultiPlayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
		return;
	}
	//传入菜单
	MultiPlayerOnFindSessionComplete.Broadcast(LastSessionSearch->SearchResults, bWasSuccessful);
}

void UMultiPlayerSessionGISubsystem::onJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
	if (mySessionInterface)
	{
		//执行完成,所以清除一下
		mySessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
	}
	//广播到菜单的onjoinsession
	MultiPlayerOnJoinSessionComplete.Broadcast(Result);
}

void UMultiPlayerSessionGISubsystem::onDestorySessionComplete(FName SessionName, bool bWasSuccessful)
{
	if (mySessionInterface)
	{
		//清除
		mySessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle);
	}
	//因为已经有一个会话了,所以要销毁会话然后在创建会话
	if (bWasSuccessful && bCreateSessionOnDestory)
	{
		//重置初始值
		bCreateSessionOnDestory = false;
		//创建会话
		CreateSession(LastNumPublicConnects,LastMatchType);
	}
	MultiPlayerOnDestroySessionComplete.Broadcast(bWasSuccessful);

}

void UMultiPlayerSessionGISubsystem::onStartSessionComplete(FName SessionName, bool bWasSuccessful)
{
}

InPluginsMenu:

InPluginsMenu.h:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Interfaces/OnlineSessionInterface.h"

#include "InPluginsMenu.generated.h"

/**
 * 
 */
UCLASS()
class MULTIPLAYERSESSIONPLUGIN_API UInPluginsMenu : public UUserWidget
{
	GENERATED_BODY()
public:
	//菜单设置 
	UFUNCTION(BlueprintCallable)
	void MenuSet(int32 NumberPublicConnect =4, FString TypeOfMatch= FString(TEXT("FreeForAll")),FString LobbyPath =FString(TEXT("/Game/Map/Lobby")));

protected:
	virtual bool Initialize() override;
	//这是5.0的重载
	//如果是5.1无法使用的,请使用virtual void NativeDestruct() override
	//【我专门在5.1测试了一遍,找不到OnLevelRemovedFromWorld】;
	virtual void OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld) override;

	//子系统上的自定义委托的回调 如果绑定失败则加上UFUNCTION
	UFUNCTION()
	void onCreateSession(bool bWasSuccessful);
	void onFindSession(const TArray<FOnlineSessionSearchResult>& SessionResult, bool bWasSuccessful);
	void onJoinSession(EOnJoinSessionCompleteResult::Type Result);
	UFUNCTION()
	void onDestroySession(bool bWasSuccessful);
	UFUNCTION()
	void onStartSession(bool bWasSuccessful);



private:
	//两个按钮,会和蓝图中的按钮链接,所以蓝图中的按钮要和c++中的一样名字
	UPROPERTY(meta=(BindWidget))
	class UButton* CreateSessionBotton;
	UPROPERTY(meta = (BindWidget))
	UButton* JoinSessionBotton;
	//点击创建会话按钮事件
	UFUNCTION()
	void CreateBottonClicked();
	//点击加入会话按钮事件
	UFUNCTION()
	void JoinBottonClicked();

	//移除控件
	void MenuTearDown();

	//这是最开始创建的类,在这里做一个变量
	//用于处理所有在线会话的子系统
	class UMultiPlayerSessionGISubsystem* MultiPlayerSessionSubsystem;

private:
	//初始化默认为4
	UPROPERTY(BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	int32 NumPublicConnect{4};
	//value值
	UPROPERTY(BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
	FString MatchType{ TEXT("FreeForAll") };
	//大厅地址 初始化的空值
	FString pathToLobby{ TEXT("") };
};

InPluginsMenu.cpp:

// Fill out your copyright notice in the Description page of Project Settings.


#include "InPluginsMenu.h"
#include "Components/Button.h"
#include "MultiPlayerSessionGISubsystem.h"
#include "OnlineSessionSettings.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "OnlineSubsystem.h"

void UInPluginsMenu::MenuSet(int32 NumberPublicConnect, FString TypeOfMatch, FString LobbyPath)
{
	//获取地址
	pathToLobby = FString::Printf(TEXT("%s?listen"), *LobbyPath);
	//私有变量初始化
	NumPublicConnect = NumberPublicConnect;
	MatchType = TypeOfMatch;
	//添加到视口
	AddToViewport();
	//设置可视
	SetVisibility(ESlateVisibility::Visible);
	//bIsFocusable设置为true是允许点击的时候受到焦点,是UserWidget里面定义的
	bIsFocusable = true;
	
	UWorld* world = GetWorld();
	if (world)
	{
		APlayerController* playerControler = world->GetFirstPlayerController();
		if (playerControler)
		{
			//FInputModeUIOnly 用于设置只允许ui响应用户输入的输入模式的数据结构
			FInputModeUIOnly inputModeData;
			//SetWidgetToFocus设置焦距
			//TakeWidget()获取底层的slate部件,不存在则构造它
			inputModeData.SetWidgetToFocus(TakeWidget());
			//设置鼠标在视口的行为,这里是不锁定
			inputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
			//设置完毕输入模式的东西之后,在玩家控制器里面将设置好的输入模式设置到玩家控制器的输入模式
			playerControler->SetInputMode(inputModeData);
			//显示鼠标光标
			playerControler->SetShowMouseCursor(true);
		}
	}
	//获取现在的游戏实例判断是否存在
	UGameInstance* gameInstance = GetGameInstance();
	if (gameInstance)
	{
		MultiPlayerSessionSubsystem =gameInstance->GetSubsystem<UMultiPlayerSessionGISubsystem>();
	}
	if (MultiPlayerSessionSubsystem)
	{
		//添加动态委托 回调函数onCreateSession
		MultiPlayerSessionSubsystem->MultiPlayerOnCreateSessionComplete.AddDynamic(this, &ThisClass::onCreateSession);
		MultiPlayerSessionSubsystem->MultiPlayerOnFindSessionComplete.AddUObject(this, &ThisClass::onFindSession);
		MultiPlayerSessionSubsystem->MultiPlayerOnJoinSessionComplete.AddUObject(this, &ThisClass::onJoinSession);
		MultiPlayerSessionSubsystem->MultiPlayerOnDestroySessionComplete.AddDynamic(this, &ThisClass::onDestroySession);
		MultiPlayerSessionSubsystem->MultiPlayerOnStartSessionComplete.AddDynamic(this, &ThisClass::onStartSession);
	}
}

bool UInPluginsMenu::Initialize()
{
	if (!Super::Initialize())
	{
		return false;
	}
	if (CreateSessionBotton)
	{
		//动态绑定
		CreateSessionBotton->OnClicked.AddDynamic(this,&UInPluginsMenu::CreateBottonClicked);
	}
	if (JoinSessionBotton)
	{
		//动态绑定
		JoinSessionBotton->OnClicked.AddDynamic(this, &UInPluginsMenu::JoinBottonClicked);
	}
	return true;
}

void UInPluginsMenu::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
{
	//这样就删除了控件和重置了输入模式和光标
	MenuTearDown();
	//执行父类 
	Super::OnLevelRemovedFromWorld(InLevel,InWorld);
}

void UInPluginsMenu::onCreateSession(bool bWasSuccessful)
{
	if (bWasSuccessful)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 15, FColor::Blue, FString(TEXT("create success")));
		}
		UWorld* world = GetWorld();
		if (world)
		{
			//将服务器跳转到新关卡
			world->ServerTravel(pathToLobby);
		}
	}
	else
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 15, FColor::Red, FString(TEXT("create Failed")));
		}
		//设置按钮的当前启用状态
		CreateSessionBotton->SetIsEnabled(true);
	}
}

void UInPluginsMenu::onFindSession(const TArray<FOnlineSessionSearchResult>& SessionResult, bool bWasSuccessful)
{
	if (MultiPlayerSessionSubsystem == nullptr)
	{
		return;
	}
	//遍历找到的会话数组
	for (auto Result : SessionResult)
	{
		FString SettingsValue;
		//获取定义会话设置的key对应的value赋予SettingsValue
		Result.Session.SessionSettings.Get(FName(TEXT("MatchType")), SettingsValue);
		if (SettingsValue ==MatchType)//判断SettingsValue和MatchType是否一致,即找到了会话
		{
			//加入会话(在这里)
			MultiPlayerSessionSubsystem->JoinSession(Result);
			return;
		}
	}
	//如果失败了,或者知道的会话数量为0
	if (!bWasSuccessful ||SessionResult.Num()==0)
	{
		JoinSessionBotton->SetIsEnabled(true);
	}
}

void UInPluginsMenu::onJoinSession(EOnJoinSessionCompleteResult::Type Result)
{
	IOnlineSubsystem* onlineSubsystem = IOnlineSubsystem::Get();
	if (onlineSubsystem)
	{
		//临时接口
		IOnlineSessionPtr TempMySessionInterface = onlineSubsystem->GetSessionInterface();
		if (TempMySessionInterface.IsValid())
		{
			//给予tempAddress地址
			FString tempAddress;
			TempMySessionInterface->GetResolvedConnectString(NAME_GameSession,tempAddress);
			//从游戏实例里面获取本地的第一个玩家控制器
			APlayerController* playerControler = GetGameInstance()->GetFirstLocalPlayerController();
			if (playerControler)
			{
				//世界跳跃
				//旅行到不同的地图或IP地址。
				//在执行任何操作之前调用PreClientTravel事件。
				playerControler->ClientTravel(tempAddress, ETravelType::TRAVEL_Absolute);
			}
		}
	}
	//如果不是成功
	if (Result !=EOnJoinSessionCompleteResult::Success)
	{
		JoinSessionBotton->SetIsEnabled(true);
	}
}

void UInPluginsMenu::onDestroySession(bool bWasSuccessful)
{
}

void UInPluginsMenu::onStartSession(bool bWasSuccessful)
{
}

void UInPluginsMenu::CreateBottonClicked()
{
	//设置按钮的当前启用状态
	CreateSessionBotton->SetIsEnabled(false);
	if (MultiPlayerSessionSubsystem)
	{
		//创建会话
		MultiPlayerSessionSubsystem->CreateSession(NumPublicConnect,MatchType);
	}
}

void UInPluginsMenu::JoinBottonClicked()
{
	//设置按钮的当前启用状态
	JoinSessionBotton->SetIsEnabled(false);
	if (MultiPlayerSessionSubsystem)
	{
		//寻找会话
		MultiPlayerSessionSubsystem->FindSession(10000);
	}

}

void UInPluginsMenu::MenuTearDown()
{
	//从父项中移除
	RemoveFromParent();
	UWorld* world = GetWorld();
	if (world)
	{
		//获取玩家控制器
		APlayerController* playerControler = world->GetFirstPlayerController();
		if (playerControler)
		{
			//创建默认的输入模式,然后设置为控制器
			FInputModeGameOnly inputModeData;
			playerControler->SetInputMode(inputModeData);
			//鼠标光标设置
			playerControler->SetShowMouseCursor(false);
		}
	}
}

MultiPlayerSessionPlugin.Build.cs:

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class MultiPlayerSessionPlugin : ModuleRules
{
	public MultiPlayerSessionPlugin(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
		
		PublicIncludePaths.AddRange(
			new string[] {
				// ... add public include paths required here ...
			}
			);
				
		
		PrivateIncludePaths.AddRange(
			new string[] {
				// ... add other private include paths required here ...
			}
			);
			
		
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				"OnlineSubsystem",
				"OnlineSubsystemSteam",
				"UMG",
				"Slate",
				"SlateCore",
				// ... add other public dependencies that you statically link with here ...
			}
			);
			
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				// ... add private dependencies that you statically link with here ...	
			}
			);
		
		
		DynamicallyLoadedModuleNames.AddRange(
			new string[]
			{
				// ... add any modules that your module loads dynamically here ...
			}
			);
	}
}

MultiPlayerSessionPlugin.uplugin:

{
	"FileVersion": 3,
	"Version": 1,
	"VersionName": "1.0",
	"FriendlyName": "MultiPlayerSessionPlugin",
	"Description": "a multiplayer game use,join and connect in steam session",
	"Category": "Other",
	"CreatedBy": "LinJohn",
	"CreatedByURL": "",
	"DocsURL": "",
	"MarketplaceURL": "",
	"SupportURL": "",
	"CanContainContent": true,
	"IsBetaVersion": false,
	"IsExperimentalVersion": false,
	"Installed": false,
	"Modules": [
		{
			"Name": "MultiPlayerSessionPlugin",
			"Type": "Runtime",
			"LoadingPhase": "Default"
		}
	],
	"Plugins": [
		{
			"Name": "OnlineSubsystem",
			"Enabled": true
		},
		{
			"Name": "OnlineSubsystemSteam",
			"Enabled": true
		}
	]
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。