Unreal Engine SDK のロビーサービスを実装する
Introduction
This topic is specific to the AccelByte Gaming Services (AGS) Shared Cloud tier.
The Lobby service is part of the AGS and provides continuous connection between your game and players by using WebSockets.
Prerequisites
The examples in this document use AGS Shared Cloud UE SDK version 15.0.0. Any other version may not be compatible or may need different steps to implement.
To complete the steps in this guide, you need to:
Implement the Authentication service and successfully test the flow.
Set up your own Lobby Configurations in the Admin Portal.
Quick Reference
References
#include "Core/AccelByteRegistry.h"
#include "Api/AccelByteLobbyApi.h"
Lobby connect notification events
FRegistry::Lobby.SetConnectionClosedDelegate(Api::Lobby::FConnectionClosed::CreateWeakLambda(this, [](int32 StatusCode, const FString& Reason, bool bWasClean)
{
// On Connection Closed
}));
FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsDisconnectNotif& Result)
{
// On Disconnect Notification
}));
Lobby connect
FRegistry::Lobby.SetConnectSuccessDelegate(FSimpleDelegate::CreateWeakLambda(this, []()
{
// On Connect to Lobby Success
}));
FRegistry::Lobby.SetConnectFailedDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
// On Connect Failed
}));
FRegistry::Lobby.Connect();
Lobby Disconnect
FRegistry::Lobby.Disconnect();
Check if Lobby is connected
FRegistry::Lobby.IsConnected()
Quickstart
In this section, you will learn how to use AGS Shared Cloud Lobby services. Follow the steps below to start to use the Lobby services:
Create a user widget C++ class called
AccelByteLobby
to specify the flow between functions when creating a Lobby menu.Add the AccelByte header to ensure the functions work correctly for the Lobby services:
. . .
#include "Api/AccelByteLobbyApi.h"
#include "Core/AccelByteRegistry.h"
. . .Create a new function called
SetLobbyNotificationDelegate()
to notify the player when the Lobby Connection is updated. Once completed, put all the lobby-related delegates inside it. These delegates include:Delegates Response SetConnectSuccessDelegate()
Lobby connection successful SetConnectFailedDelegate()
Lobby connection failed SetConnectionClosedDelegate()
Lobby connection process interrupted by a technical issue SetDisconnectNotifDelegate()
Player logged out of the game 注記Although you make reference to the Lobby service, the Lobby is not yet connected. For now, you will just add debug logs in these tutorials to notify you of any updates after calling the references.
After you have added the delegates, the function should look like this:
- .cpp
- h
void UAccelByteLobby::SetLobbyNotificationDelegate()
{
FRegistry::Lobby.SetConnectSuccessDelegate(FSimpleDelegate::CreateWeakLambda(this, []()
{
UE_LOG(LogTemp, Log, TEXT("Successfully Connected to Lobby"));
}));
FRegistry::Lobby.SetConnectFailedDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Failed Connect to Lobby. Code: %d, Reason: %s"), Code, *Message);
}));
FRegistry::Lobby.SetErrorNotifDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Error Connect to Lobby. Code: %d, Message: %s"), Code, *Message);
}));
FRegistry::Lobby.SetConnectionClosedDelegate(Api::Lobby::FConnectionClosed::CreateWeakLambda(this, [](int32 StatusCode, const FString& Reason, bool bWasClean)
{
UE_LOG(LogTemp, Error, TEXT("Error Connect to Lobby. Code: %d, Reason: %s, Clean: %s"), StatusCode, *Reason, bWasClean ? TEXT("true") : TEXT("false"));
}));
FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsDisconnectNotif& Result)
{
UE_LOG(LogTemp, Log, TEXT("Disconnected from Lobby"));
}));
}/**
* @brief Set Lobby services notification delegates.
*/
void SetLobbyNotificationDelegate();Create a new function called
ConnectToLobby()
. This will be triggered after Login is successful and will open a WebSocket that sets delegates to receive responses after requesting a connection to the Lobby service by callingSetLobbyNotificationDelegate()
.- .cpp
- h
void UAccelByteLobby::ConnectToLobby()
{
SetLobbyNotificationDelegate();
FRegistry::Lobby.Connect();
}/**
* @brief Connect to AGS Lobby.
*/
UFUNCTION()
void ConnectToLobby();Test your code by running the function.
After successfully connecting to the Lobby service, you can move to AccelByteAuth.cpp and add a call to the
FRegistry::Lobby.Connect()
function on the success delegate fromLoginWithUsername()
. You can findLoginWithUsername()
insideOnLoginButtonClicked()
.Test the function by triggering
OnLoginButtonClicked()
.void UAccelByteAuth::OnLoginButtonClicked()
{
...
FVoidHandler::CreateWeakLambda(this, []()
{
UE_LOG(LogTemp, Log, TEXT("Login success"));
FRegistry::Lobby.Connect();
if (FRegistry::Lobby.IsConnected())
{
UE_LOG(LogTemp, Log, TEXT("Lobby is connected!"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Lobby is not connected!"));
}
}),
...
}If you have configured your code correctly, you will see the message Lobby is connected! after logging in.
注記If the Lobby is not connected successfully, check your game project configuration and make sure the user is logged in. As long as the Lobby is connected, you can use all the Lobby services available.
(Optional) By default, if you quit your application, you are automatically disconnected from the Lobby. To manually disconnect without closing the application, use the
OnLogoutButtonClicked()
function in theAccelByteAuth.cpp
.void UAccelByteAuth::OnLogoutButtonClicked()
{
...
FRegistry::Lobby.Disconnect();
...
}
Congratulations! You have successfully connected to the Lobby.
Read on for a step-by-step guide to UI and code implementation. Otherwise, you are now ready to move on to the Friends service.
Step-by-Step guide
Implement the UI
The following steps are optional as the Lobby service can run without any widgets implemented.
In this step-by-step guide, you will implement the HUD to get the Lobby Class reference. This means you need to create a Lobby Menu widget but, for now, you can leave it empty.
Create an empty widget blueprint class called
WB_LobbyMenu
.Assign the
AccelByteLobby
as the parent class of theWB_LobbyMenu
.
Implement the code
In this section, you will show you what you need to do to change between the Login and Lobby widgets using a HUD.
TutorialMenuHUD
Create a HUD Blueprint class called
BP_TutorialHUD
.Create a HUD C++ class called
TutorialMenuHUD
and add the class as a parent class forBP_TutorialHUD
.Open the
TutorialMenuHUD
class and add the following headers at the top of the class:- .cpp
- h
...
#include "AccelByte/Authentication/AccelByteAuth.h"
#include "AccelByte/Lobby/AccelByteLobby.h"...
class UAccelByteAuth;
class UAccelByteLobby;
...Add the
BeginPlay()
function to check and set all of your class pointers.- .cpp
- h
void ATutorialMenuHUD::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = GetOwningPlayerController();
check(LoginMenuClass != nullptr);
check(LobbyMenuClass != nullptr);
LoginMenu = CreateWidget<UAccelByteAuth>(PlayerController, LoginMenuClass.Get());
LobbyMenu = CreateWidget<UAccelByteLobby>(PlayerController, LobbyMenuClass.Get());
}virtual void BeginPlay() override;
/**
* @brief Login menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteAuth> LoginMenuClass;
/**
* @brief Lobby menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteLobby> LobbyMenuClass;
/**
* @brief Login menu widget pointer
*/
UPROPERTY()
UAccelByteAuth* LoginMenu;
/**
* @brief Lobby Menu widget pointer
*/
UPROPERTY()
UAccelByteLobby* LobbyMenu;Add the Login and Lobby's C++ class in the
BP_TutorialHUD
under the pointer variables you declared in theBeginPlay()
function.
You will need to repeat Step 5 in order to add additional UI-related user widgets for in-game stores, inventories, leaderboards, etc.
Add
const
functions that will act as getters for the widgets.- h
/**
* @brief Getter for Login Menu widget
*/
UAccelByteAuth* GetLoginMenu() const {return LoginMenu; }
/**
* @brief Getter for Lobby Menu widget
*/
UAccelByteLobby* GetLobbyMenu() const {return LobbyMenu; }Add some functions that will be called to change to the desired menu page. Since you only have Login and Lobby widgets, create two new functions to open these widgets:
- .cpp
- h
void ATutorialMenuHUD::OpenLoginMenu()
{
LoginMenu->AddToViewport();
}
void ATutorialMenuHUD::OpenLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Visible);
}/**
* @brief Shows Login Menu on screen
*/
void OpenLoginMenu();
/**
* @brief Shows Lobby Menu which adds Party Menu in Sb_Party and destroys Main Menu
*/
UFUNCTION()
void OpenLobbyMenu();Add another function that will be used to destroy the widgets when you close the page.
- .cpp
- h
void ATutorialMenuHUD::CloseLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Collapsed);
}/**
* @brief Destroys Lobby Menu widget and shows Main Menu
*/
UFUNCTION()
void CloseLobbyMenu();Inside the
BeginPlay()
function, check the current session'suserId
. If it is empty, call theOpenLoginMenu()
function.- .cpp
void ATutorialMenuHUD::BeginPlay()
{
...
if (FRegistry::Credentials.GetUserId().IsEmpty())
{
OpenLoginMenu();
}
}
Lobby-related widget implementation update
Open the
AccelByteAuth
class and header file and add theTutorialMenuHUD
class.- .cpp
- h
...
#include "TutorialProject/TutorialMenuHUD.h"...
class ATutorialMenuHUD;
...In the
AccelByteAuth.cpp
, define theTutorialMenuHUD
's pointer in theNativeConstruct()
function.- .cpp
- h
void UAccelByteAuth::NativeConstruct()
{
...
TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetWorld()->GetFirstPlayerController()->GetHUD());
...
}/**
* @brief Instantiate all casting to the main menu HUD
*/
ATutorialMenuHUD* TutorialMenuHUD;Inside the
LoginSuccess()
function, call theConnectToLobby()
function with the Lobby getter.- .cpp
void UAccelByteAuth::LoginSuccess()
{
...
if(GetOwningPlayer() && GetOwningPlayer()->GetHUD())
{
TutorialMenuHUD->GetLobbyMenu()->ConnectToLobby();
this->RemoveFromParent();
}
}Add the Lobby Disconnect function inside the
OnLogoutButtonClicked()
function inAccelByteAuth.cpp
.- .cpp
void UAccelByteAuth::OnLogoutButtonClicked()
{
...
if (FRegistry::Lobby.IsConnected())
{
FRegistry::Lobby.Disconnect();
}
}
Congratulations! You have now fully implemented the Lobby, which is the gateway to all other AGS Shared Cloud services.
Go to the next section to learn how to implement AGS Shared Cloud Friends service.
Full Code for reference
AccelByteLobby.h
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
#pragma once
#include "CoreMinimal.h"
#include "AccelByteLobby.generated.h"
/**
* Component for Join to AccelByte Gaming Services (AGS) Lobby.
* This code covers AGS including :
*
* - Join Lobby
* - Leave Lobby
*/
UCLASS()
class TUTORIALPROJECT_API UAccelByteLobby : public UUserWidget
{
GENERATED_BODY()
#pragma region Utilities
public:
/**
* @brief Connect to AccelByte lobby.
*/
UFUNCTION()
void ConnectToLobby();
/**
* @brief Set Lobby services notification delegates.
*/
void SetLobbyNotificationDelegate();
#pragma endregion
};
AccelByteLobby.cpp
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
#include "AccelByteLobby.h"
#include "Api/AccelByteLobbyApi.h"
#include "Core/AccelByteRegistry.h"
#pragma region Utilities
void UAccelByteLobby::ConnectToLobby()
{
SetLobbyNotificationDelegate();
FRegistry::Lobby.Connect();
}
void UAccelByteLobby::SetLobbyNotificationDelegate()
{
FRegistry::Lobby.SetConnectSuccessDelegate(FSimpleDelegate::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Successfully Connected to Lobby"));
}));
FRegistry::Lobby.SetConnectFailedDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Failed Connect to Lobby : Code: %d; Message: %s"), Code, *Message);
}));
FRegistry::Lobby.SetErrorNotifDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Error Connect to Lobby : Code: %d; Message: %s"), Code, *Message);
}));
FRegistry::Lobby.SetConnectionClosedDelegate(Api::Lobby::FConnectionClosed::CreateWeakLambda(this, [this](int32 StatusCode, const FString& Reason, bool bWasClean)
{
UE_LOG(LogTemp, Error, TEXT("Connection Closed, Code: %d Reason: %s Clean: %s"), StatusCode, *Reason, bWasClean ? TEXT("true") : TEXT("false"));
}));
FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [this](const FAccelByteModelsDisconnectNotif& Result)
{
UE_LOG(LogTemp, Log, TEXT("Disconnected from Lobby"));
}));
}
#pragma endregion
`TutorialMenuHUD.h`
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "TutorialMenuHUD.generated.h"
class UAccelByteAuth;
class UAccelByteLobby;
/**
* Menu Widget Controller. All Widget functionality controlled here.
*/
UCLASS()
class TUTORIALPROJECT_API ATutorialMenuHUD : public AHUD
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
public:
/**
* @brief Shows Login Menu on screen
*/
void OpenLoginMenu();
/**
* @brief Shows Lobby Menu which adds Party Menu in Sb_Party and destroys Main Menu
*/
UFUNCTION()
void OpenLobbyMenu();
/**
* @brief Destroys Lobby Menu widget and shows Main Menu
*/
UFUNCTION()
void CloseLobbyMenu();
protected:
/**
* @brief Login Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteAuth> LoginMenuClass;
/**
* @brief Lobby Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteLobby> LobbyMenuClass;
public:
/**
* @brief Getter for Login Menu widget
*/
UAccelByteAuth* GetLoginMenu() const {return LoginMenu; }
/**
* @brief Getter for Lobby Menu widget
*/
UAccelByteLobby* GetLobbyMenu() const {return LobbyMenu; }
private:
/**
* @brief Login Menu widget pointer
*/
UPROPERTY()
UAccelByteAuth* LoginMenu;
/**
* @brief Lobby Menu widget pointer
*/
UPROPERTY()
UAccelByteLobby* LobbyMenu;
};
`TutorialMenuHUD.cpp`
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
#include "TutorialMenuHUD.h"
#include "AccelByte/Authentication/AccelByteAuth.h"
#include "AccelByte/Lobby/AccelByteLobby.h"
void ATutorialMenuHUD::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = GetOwningPlayerController();
check(LoginMenuClass != nullptr);
check(LobbyMenuClass != nullptr);
LoginMenu = CreateWidget<UAccelByteAuth>(PlayerController, LoginMenuClass.Get());
LobbyMenu = CreateWidget<UAccelByteLobby>(PlayerController, LobbyMenuClass.Get());
if (FRegistry::Credentials.GetUserId().IsEmpty())
{
OpenLoginMenu();
}
}
void ATutorialMenuHUD::OpenLoginMenu()
{
LoginMenu->AddToViewport();
}
void ATutorialMenuHUD::OpenLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Visible);
}
void ATutorialMenuHUD::CloseLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Collapsed);
}
`AccelByteAuth.h`
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "AccelByteAuth.generated.h"
class UVerticalBox;
class UEditableTextBox;
class UButton;
class ATutorialMenuHUD;
/**
* Component for logging in a user with the AGS back end as well as methods for grabbing information relating to that user.
* This code covers AGS including :
*
* - Login with username
*/
UCLASS()
class TUTORIALPROJECT_API UAccelByteAuth : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override;
/**
* @brief Editable Text Box for Username inside MainMenu Widget.
*/
UPROPERTY(meta = (BindWidget))
UEditableTextBox* Etb_Username;
/**
* @brief Editable Text Box for Password inside MainMenu Widget.
*/
UPROPERTY(meta = (BindWidget))
UEditableTextBox* Etb_Password;
/**
* @brief Take Button Login inside MainMenu Widget.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_Login;
/**
* @brief Text Block to display Login Status
*/
UPROPERTY(meta = (BindWidget))
UTextBlock* T_LoginStatus;
/**
* @brief Instantiate all casting to the main menu HUD
*/
ATutorialMenuHUD* TutorialMenuHUD;
public:
/**
* @brief Log an account in using the AGS Game SDK. This is executed automatically on component construction unless
* otherwise configured.
*/
UFUNCTION()
void OnLoginButtonClicked();
/**
* @brief Logout a session using the AGS Game SDK. This is executed automatically on component construction unless
* otherwise configured.
*/
UFUNCTION()
void OnLogoutButtonClicked();
private:
/**
* @brief Function behaviour when Login success. This function called inside AccelByte Login OnSuccess
* delegates inside lambda. Can be improved or changed how login behaviour works inside this function.
*/
void LoginSuccess();
/**
* @brief Function behaviour when Login failed. This function called inside AGS Login OnFailed
* delegates inside lambda. Can be improved or changed how login behaviour works inside this function.
*
* @param ErrorCode error code HTTP request. e.g 404.
* @param ErrorMessage error message HTTP request. e.g Unauthorized.
* @param ErrorJson error message for OAuth
*/
void LoginFailed(int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorJson);
/**
* @brief Function behaviour when Logout success. This function called inside AccelByte Logout OnSuccess
* delegates inside lambda. Can be improved or changed how Logout behaviour works inside this function.
*/
void LogoutSuccess();
/**
* @brief Function behaviour when Logout failed. This function called inside AGS Logout OnFailed
* delegates inside lambda. Can be improved or changed how Logout behaviour works inside this function.
*
* @param ErrorCode error code HTTP request. e.g 404.
* @param ErrorMessage error message HTTP request. e.g Unauthorized.
*/
void LogoutFailed(int32 ErrorCode, const FString& ErrorMessage);
};
`AccelByteAuth.cpp`
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
#include "AccelByteAuth.h"
#include "Api/AccelByteUserApi.h"
#include "Core/AccelByteRegistry.h"
#include "Components/Button.h"
#include "Components/EditableTextBox.h"
#include "Components/TextBlock.h"
#include "TutorialProject/TutorialMenuHUD.h"
void UAccelByteAuth::NativeConstruct()
{
Super::NativeConstruct();
TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetWorld()->GetFirstPlayerController()->GetHUD());
UE_LOG(LogTemp, Log, TEXT("Login with Username"));
T_LoginStatus->SetText(FText::FromString("Please Login"));
Btn_Login->OnClicked.AddUniqueDynamic(this, &UAccelByteAuth::OnLoginButtonClicked);
}
void UAccelByteAuth::OnLoginButtonClicked()
{
T_LoginStatus->SetText(FText::FromString("Logging in..."));
FRegistry::User.LoginWithUsername(
Etb_Username->GetText().ToString(),
Etb_Password->GetText().ToString(),
FVoidHandler::CreateUObject(this, &UAccelByteAuth::LoginSuccess),
FCustomErrorHandler::CreateUObject(this, &UAccelByteAuth::LoginFailed));
}
void UAccelByteAuth::OnLogoutButtonClicked()
{
FRegistry::User.Logout(
FVoidHandler::CreateUObject(this, &UAccelByteAuth::LogoutSuccess),
FErrorHandler::CreateUObject(this, &UAccelByteAuth::LogoutFailed));
if (FRegistry::Lobby.IsConnected())
{
FRegistry::Lobby.Disconnect();
}
}
void UAccelByteAuth::LoginSuccess()
{
UE_LOG(LogTemp, Log, TEXT("Login Success"));
T_LoginStatus->SetText(FText::FromString("Login successful"));
if(GetOwningPlayer() && GetOwningPlayer()->GetHUD())
{
TutorialMenuHUD->GetLobbyMenu()->ConnectToLobby();
this->RemoveFromParent();
}
}
void UAccelByteAuth::LoginFailed(int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorJson)
{
UE_LOG(LogTemp, Error, TEXT("Login Failed : %d , %s"), ErrorCode, *ErrorMessage);
T_LoginStatus->SetText(FText::FromString(FString::Printf(TEXT("Login Failed : %d , %s"), ErrorCode, *ErrorMessage)));
}
void UAccelByteAuth::LogoutSuccess()
{
UE_LOG(LogTemp, Log, TEXT("Logout Success"));
}
void UAccelByteAuth::LogoutFailed(int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Logout Failed : %d , %s"), ErrorCode, *ErrorMessage);
}