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

news2024/12/31 6:53:42

目录

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

头文件:

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
		}
	]
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/453666.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA 20个例程篇:20.USB2.0/RS232/LAN控制并行DAC输出任意频率正弦波、梯形波、三角波、方波(四)

接着同样地我们也需要完成对千兆网口ETH模块和USB2.0模块的编写&#xff0c;实际上和UART串口模块的设计思想大同小异&#xff0c;也同样地需要完成两项关键功能即识别并解析报文、接收并发送数据&#xff0c;千兆网口ETH和USB2.0的底层驱动在前面的例程中也详细说明了&#xf…

常用的设计模式(单例模式、工厂模式等)

1.单例模式 概述: 在有些系统中&#xff0c;为了节省内存资源、保证数据内容的一致性&#xff0c;对某些类要求只能创建一个实例&#xff0c;这就是所谓的单例模式. 例如&#xff0c;Windows 中只能打开一个任务管理器&#xff0c;这样可以避免因打开多个任务管理器窗口而造…

Centos切换jdk版本

先安装了jdk1.8的版本&#xff0c;需要使用jdk17的版本 1.先安装jdk17&#xff0c;再配置环境变量&#xff1a; vim ~/.bashrc 2.在最后一行添加 ##这个添加的就是路径&#xff0c;一定要和自己jdk安装的路径是一致的 export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 3.然…

Mybatis框架超详解及运用总结

Mybatis 一、什么是Mybatils&#xff1f;二、第一个Mybatils程序2.1、创建springboot工程2.2、准备数据2.3、配置MyBatis2.4、编写SQL语句2.5、单元测试 三、JDBC四、数据库连接池五、lombok六、Mybatis基础操作6.1、删除6.2、新增6.2.1、主键返回 6.3、修改6.4、查询6.4.1、数…

【AI绘画】AI绘画的创意应用

目录 1.引言2.将AI生成的图像转化为数字艺术品2.1AI生成的画作拍卖2.2将AI生成的图像转化为雕塑 3.将AI生成的图像用于虚拟场景的创建3.1使用GAN生成虚拟人物3.2在虚拟场景中使用AI生成的图像 1.引言 当今的AI绘画技术已经发展到了让人惊艳的程度&#xff0c;不仅可以生成高质量…

【每日一题Day183】LC1187使数组严格递增 | dp

使数组严格递增【LC1187】 给你两个整数数组 arr1 和 arr2&#xff0c;返回使 arr1 严格递增所需要的最小「操作」数&#xff08;可能为 0&#xff09;。 每一步「操作」中&#xff0c;你可以分别从 arr1 和 arr2 中各选出一个索引&#xff0c;分别为 i 和 j&#xff0c;0 <…

缓存优化----SpringCache

spring cache spring Cache介绍 spring cache是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring cache提供了一层抽象&#xff0c;底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不…

springboot JWT 搭建授权服务器

目录 0 基本介绍 0.1 课程视频 0.2 架构逻辑图 0.2.1 登录JWT与授权服务器交互 0.2.2 登录成功后JWT与gateway-server交互 路由限制 1 JWT私钥公钥 1.1 安装git ->win系统右键 -> git bash here 1.2 生成私钥jks文件 1.3 用私钥jks文件解析出公钥 1.4 保存 BEGI…

造型简约的机箱,安装简单兼容性好,安钛克P20C体验

我们准备组装一台新主机的时候&#xff0c;机箱确实很重要&#xff0c;它决定了主机的整体风格和兼容性。我比较喜欢用中塔机箱&#xff0c;像是上个月我新装的主机&#xff0c;用的就是安钛克P20C&#xff0c;这款机箱的设计很简约&#xff0c;而且还有多种版本可选&#xff0…

C++——模板进阶

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f337;类型模板参数&#x1f337;非类型模板参数&#x1f337;模板的特化&#x1f338;引例&#x1f338;函数模板的特化&#x1f338;类模板特化&#x1f33c;全特化 &#x1f338;偏特化&#x1f33c;部分特化&am…

【下载器篇】IDM下载器个性化设置

【下载器篇】IDM下载器个性化设置 IDM个性化设置—【蘇小沐】 文章目录 【下载器篇】IDM下载器个性化设置1.实验环境 &#xff08;一&#xff09;下载类型扩展UA默认值 &#xff08;二&#xff09;工具栏样式&#xff08;改风格&#xff09;3D Style样式 &#xff08;三&#…

2023.4.23 自注意力机制

一般都是单向量输入&#xff0c;但是如果多向量输入应该如何处理呢&#xff1f;引出自注意力机制 多向量输入可能会有多种输出&#xff0c;如果输入n个向量&#xff0c;输出n个向量表明这是sequence labeling&#xff0c;比如对于一个英文句子&#xff0c;每一个单词都判断是什…

c++11 标准模板(STL)(std::priority_queue)(二)

适配一个容器以提供优先级队列 std::priority_queue 定义于头文件 <queue> template< class T, class Container std::vector<T>, class Compare std::less<typename Container::value_type> > class priority_queue; priority_queu…

Windows下编译UHD

1.安装Visual Studio 2019,下载地址https://download.csdn.net/download/qq_36314864/87719209 2.安装cmake,下载地址https://download.csdn.net/download/qq_36314864/87719747 安装完成后记得C:\Program Files\cmake-3.22.1-windows-x86_64\bin添加到环境变量里面,或者安…

数据结构修炼:链表习题讲解!!!

题一&#xff1a;移除链表元素 我们可以看出这道题是让我们删除特定数据&#xff0c;我们可以用双指针来解这道题&#xff1a; 如果首元素为val&#xff0c;那么cur和head一起后移&#xff1a; 如果没有碰到val&#xff0c;那么就会cur后移&#xff0c;并且提前将cur传给perv&a…

如何避免美国ASP主机服务器崩溃和故障?

在当今数字化时代&#xff0c;网站是一个公司展示其业务的主要方式之一。因此&#xff0c;公司的在线业务应该始终保持高可用性和可靠性。ASP主机服务器是一种用于托管网站的服务器&#xff0c;其特点是可靠性高。但是&#xff0c;即使是最可靠的服务器也会遭受故障或崩溃。在本…

漏刻有时数据可视化大屏引导页设计(php原生开发、主背景图片更换、标题设置)

文章目录 1.引入外部js库2.HTML排版3.项目配置文件4.菜单图标自动匹配5.php与html混排6.CSS样式表7.添加/编辑信息8.生成配置文件 在制作数据可视化大屏时&#xff0c;尤其是在触摸屏演示时&#xff0c;需要开发和设计对应的数据大屏引导页。基于常见场景&#xff0c;单独开发数…

Unity API详解——Object类

Object类是Unity中所有对象的基类&#xff0c;例如GameObject、Component、Material、Shader、Texture、Mesh、Font等都是Object的子类。本博客介绍Object类的一些实例方法和静态方法。 一、Object类实例方法 在Object类中&#xff0c;涉及的实例方法主要有GetInstanceID方法…

Java基础学习(10)

Java基础学习 一、JDK8时间类1.1 Zoneld时区1.2 Instant时间戳1.3 ZonedDateTime1.4 DateTimeFormatter1.5 日历类时间表示1.6 工具类1.7 包装类JDK5提出的新特性Integer成员方法 二、集合进阶2.1 集合的体系结构2.1.1 Collection 2.2collection的遍历方式2.2.1 迭代器遍历2.2.…

RecycleView与TabLayout联动展示更多功能列表页面的实现

一.前言 对于更多功能页面&#xff0c;使用RecycleView与TabLayout联动方式实现是比较常见的&#xff0c;先上效果图&#xff08;请大佬们忽略gif的水印&#xff09; 单独使用TabLayout和RecycleView都是比较容易的&#xff0c;这里就不做举例了&#xff1b;gif中的列表实际上…